Merge branch 'master' into blender-tweaks
This commit is contained in:
@@ -9,8 +9,8 @@ return array(
|
||||
'names' => array(
|
||||
'conpherence.pkg.css' => 'e68cf1fa',
|
||||
'conpherence.pkg.js' => '15191c65',
|
||||
'core.pkg.css' => '7489ba0d',
|
||||
'core.pkg.js' => '4bde473b',
|
||||
'core.pkg.css' => '2b7e2a42',
|
||||
'core.pkg.js' => 'bd89cb1d',
|
||||
'differential.pkg.css' => '06dc617c',
|
||||
'differential.pkg.js' => 'ef0b989b',
|
||||
'diffusion.pkg.css' => 'a2d17c7d',
|
||||
@@ -127,7 +127,7 @@ return array(
|
||||
'rsrc/css/phui/calendar/phui-calendar-list.css' => '576be600',
|
||||
'rsrc/css/phui/calendar/phui-calendar-month.css' => '21154caf',
|
||||
'rsrc/css/phui/calendar/phui-calendar.css' => 'f1ddf11c',
|
||||
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '628f59de',
|
||||
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '7a7c22af',
|
||||
'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',
|
||||
@@ -216,7 +216,7 @@ return array(
|
||||
'rsrc/externals/javelin/core/__tests__/stratcom.js' => '88bf7313',
|
||||
'rsrc/externals/javelin/core/__tests__/util.js' => 'e251703d',
|
||||
'rsrc/externals/javelin/core/init.js' => '8d83d2a1',
|
||||
'rsrc/externals/javelin/core/init_node.js' => 'c234aded',
|
||||
'rsrc/externals/javelin/core/init_node.js' => 'f7732951',
|
||||
'rsrc/externals/javelin/core/install.js' => '05270951',
|
||||
'rsrc/externals/javelin/core/util.js' => '93cc50d6',
|
||||
'rsrc/externals/javelin/docs/Base.js' => '74676256',
|
||||
@@ -427,7 +427,7 @@ return array(
|
||||
'rsrc/js/application/transactions/behavior-comment-actions.js' => '59e27e74',
|
||||
'rsrc/js/application/transactions/behavior-reorder-configs.js' => 'd7a74243',
|
||||
'rsrc/js/application/transactions/behavior-reorder-fields.js' => 'b59e1e96',
|
||||
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '8f29b364',
|
||||
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => '0e1eca96',
|
||||
'rsrc/js/application/transactions/behavior-transaction-comment-form.js' => 'b23b49e6',
|
||||
'rsrc/js/application/transactions/behavior-transaction-list.js' => '1f6794f6',
|
||||
'rsrc/js/application/typeahead/behavior-typeahead-browse.js' => '635de1ec',
|
||||
@@ -471,6 +471,7 @@ return array(
|
||||
'rsrc/js/core/behavior-keyboard-shortcuts.js' => '01fca1f0',
|
||||
'rsrc/js/core/behavior-lightbox-attachments.js' => '6b31879a',
|
||||
'rsrc/js/core/behavior-line-linker.js' => '66a62306',
|
||||
'rsrc/js/core/behavior-linked-container.js' => '291da458',
|
||||
'rsrc/js/core/behavior-more.js' => 'a80d0378',
|
||||
'rsrc/js/core/behavior-object-selector.js' => '77c1f0b0',
|
||||
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
|
||||
@@ -618,6 +619,7 @@ return array(
|
||||
'javelin-behavior-launch-icon-composer' => '48086888',
|
||||
'javelin-behavior-lightbox-attachments' => '6b31879a',
|
||||
'javelin-behavior-line-chart' => 'e4232876',
|
||||
'javelin-behavior-linked-container' => '291da458',
|
||||
'javelin-behavior-maniphest-batch-selector' => 'ad54037e',
|
||||
'javelin-behavior-maniphest-list-editor' => 'a9f88de2',
|
||||
'javelin-behavior-maniphest-subpriority-editor' => '71237763',
|
||||
@@ -639,7 +641,7 @@ return array(
|
||||
'javelin-behavior-phabricator-remarkup-assist' => 'acd29eee',
|
||||
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
|
||||
'javelin-behavior-phabricator-search-typeahead' => 'c3e917d9',
|
||||
'javelin-behavior-phabricator-show-older-transactions' => '8f29b364',
|
||||
'javelin-behavior-phabricator-show-older-transactions' => '0e1eca96',
|
||||
'javelin-behavior-phabricator-tooltips' => 'c420b0b9',
|
||||
'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6',
|
||||
'javelin-behavior-phabricator-transaction-list' => '1f6794f6',
|
||||
@@ -835,7 +837,7 @@ return array(
|
||||
'phui-lightbox-css' => '0a035e40',
|
||||
'phui-list-view-css' => '38f8c9bd',
|
||||
'phui-object-box-css' => '9cff003c',
|
||||
'phui-oi-big-ui-css' => '628f59de',
|
||||
'phui-oi-big-ui-css' => '7a7c22af',
|
||||
'phui-oi-color-css' => 'cd2b9b77',
|
||||
'phui-oi-drag-ui-css' => '08f4ccc3',
|
||||
'phui-oi-flush-ui-css' => '9d9685d6',
|
||||
@@ -951,6 +953,12 @@ return array(
|
||||
'javelin-install',
|
||||
'phuix-button-view',
|
||||
),
|
||||
'0e1eca96' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'0f764c35' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
@@ -1027,6 +1035,10 @@ return array(
|
||||
'javelin-uri',
|
||||
'phabricator-notification',
|
||||
),
|
||||
'291da458' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
),
|
||||
'2926fff2' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
@@ -1348,9 +1360,6 @@ return array(
|
||||
'javelin-magical-init',
|
||||
'javelin-util',
|
||||
),
|
||||
'628f59de' => array(
|
||||
'phui-oi-list-view-css',
|
||||
),
|
||||
'62dfea03' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
@@ -1511,6 +1520,9 @@ return array(
|
||||
'owners-path-editor',
|
||||
'javelin-behavior',
|
||||
),
|
||||
'7a7c22af' => array(
|
||||
'phui-oi-list-view-css',
|
||||
),
|
||||
'7cbe244b' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
@@ -1578,12 +1590,6 @@ return array(
|
||||
'8e1baf68' => array(
|
||||
'phui-button-css',
|
||||
),
|
||||
'8f29b364' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
'javelin-dom',
|
||||
'phabricator-busy',
|
||||
),
|
||||
'8ff5e24c' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-stratcom',
|
||||
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_user.phabricator_session
|
||||
ADD phid VARBINARY(64) NOT NULL;
|
18
resources/sql/autopatches/20181213.auth.02.populatephid.php
Normal file
18
resources/sql/autopatches/20181213.auth.02.populatephid.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
$table = new PhabricatorAuthSession();
|
||||
$iterator = new LiskMigrationIterator($table);
|
||||
$conn = $table->establishConnection('w');
|
||||
|
||||
foreach ($iterator as $session) {
|
||||
if (strlen($session->getPHID())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %R SET phid = %s WHERE id = %d',
|
||||
$table,
|
||||
$session->generatePHID(),
|
||||
$session->getID());
|
||||
}
|
2
resources/sql/autopatches/20181213.auth.03.phidkey.sql
Normal file
2
resources/sql/autopatches/20181213.auth.03.phidkey.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_user.phabricator_session
|
||||
ADD UNIQUE KEY `key_phid` (phid);
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_user.phabricator_session
|
||||
CHANGE sessionKey sessionKey VARBINARY(64) NOT NULL;
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_user.user_log
|
||||
CHANGE session session VARBINARY(64);
|
12
resources/sql/autopatches/20181213.auth.06.challenge.sql
Normal file
12
resources/sql/autopatches/20181213.auth.06.challenge.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
CREATE TABLE {$NAMESPACE}_auth.auth_challenge (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
userPHID VARBINARY(64) NOT NULL,
|
||||
factorPHID VARBINARY(64) NOT NULL,
|
||||
sessionPHID VARBINARY(64) NOT NULL,
|
||||
challengeKey VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
challengeTTL INT UNSIGNED NOT NULL,
|
||||
properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_auth.auth_challenge
|
||||
ADD workflowKey VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20181217.auth.01.digest.sql
Normal file
2
resources/sql/autopatches/20181217.auth.01.digest.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_auth.auth_challenge
|
||||
ADD responseDigest VARCHAR(255) COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20181217.auth.02.ttl.sql
Normal file
2
resources/sql/autopatches/20181217.auth.02.ttl.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_auth.auth_challenge
|
||||
ADD responseTTL INT UNSIGNED;
|
2
resources/sql/autopatches/20181217.auth.03.completed.sql
Normal file
2
resources/sql/autopatches/20181217.auth.03.completed.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_auth.auth_challenge
|
||||
ADD isCompleted BOOL NOT NULL;
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_pholio.pholio_image
|
||||
ADD authorPHID VARBINARY(64) NOT NULL;
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_pholio.pholio_image
|
||||
ADD mockPHID VARBINARY(64);
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
// Old images used a "mockID" instead of a "mockPHID" to reference mocks.
|
||||
// Set the "mockPHID" column to the value that corresponds to the "mockID".
|
||||
|
||||
$image = new PholioImage();
|
||||
$mock = new PholioMock();
|
||||
|
||||
$conn = $image->establishConnection('w');
|
||||
$iterator = new LiskRawMigrationIterator($conn, $image->getTableName());
|
||||
|
||||
foreach ($iterator as $image_row) {
|
||||
if ($image_row['mockPHID']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$mock_id = $image_row['mockID'];
|
||||
|
||||
$mock_row = queryfx_one(
|
||||
$conn,
|
||||
'SELECT phid FROM %R WHERE id = %d',
|
||||
$mock,
|
||||
$mock_id);
|
||||
|
||||
if (!$mock_row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %R SET mockPHID = %s WHERE id = %d',
|
||||
$image,
|
||||
$mock_row['phid'],
|
||||
$image_row['id']);
|
||||
}
|
2
resources/sql/autopatches/20181219.pholio.03.imageid.sql
Normal file
2
resources/sql/autopatches/20181219.pholio.03.imageid.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_pholio.pholio_image
|
||||
DROP mockID;
|
28
resources/sql/autopatches/20181220.pholio.01.mailkey.php
Normal file
28
resources/sql/autopatches/20181220.pholio.01.mailkey.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
$mock_table = new PholioMock();
|
||||
$mock_conn = $mock_table->establishConnection('w');
|
||||
|
||||
$properties_table = new PhabricatorMetaMTAMailProperties();
|
||||
$conn = $properties_table->establishConnection('w');
|
||||
|
||||
$iterator = new LiskRawMigrationIterator(
|
||||
$mock_conn,
|
||||
$mock_table->getTableName());
|
||||
|
||||
foreach ($iterator as $row) {
|
||||
queryfx(
|
||||
$conn,
|
||||
'INSERT IGNORE INTO %T
|
||||
(objectPHID, mailProperties, dateCreated, dateModified)
|
||||
VALUES
|
||||
(%s, %s, %d, %d)',
|
||||
$properties_table->getTableName(),
|
||||
$row['phid'],
|
||||
phutil_json_encode(
|
||||
array(
|
||||
'mailKey' => $row['mailKey'],
|
||||
)),
|
||||
PhabricatorTime::getNow(),
|
||||
PhabricatorTime::getNow());
|
||||
}
|
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE {$NAMESPACE}_pholio.pholio_mock
|
||||
DROP mailKey;
|
@@ -12,9 +12,9 @@ foreach ($commits as $commit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $commit->loadOneRelative(
|
||||
new PhabricatorRepositoryCommitData(),
|
||||
'commitID');
|
||||
$data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
|
||||
'commitID = %d',
|
||||
$commit->getID());
|
||||
|
||||
if (!$data) {
|
||||
continue;
|
||||
|
@@ -8,7 +8,9 @@ $commit_table->establishConnection('w');
|
||||
$edges = 0;
|
||||
|
||||
foreach (new LiskMigrationIterator($commit_table) as $commit) {
|
||||
$data = $commit->loadOneRelative($data_table, 'commitID');
|
||||
$data = $data_table->loadOneWhere(
|
||||
'commitID = %d',
|
||||
$commit->getID());
|
||||
if (!$data) {
|
||||
continue;
|
||||
}
|
||||
|
@@ -1,22 +1,7 @@
|
||||
<?php
|
||||
|
||||
$table = new PhabricatorUser();
|
||||
$table->openTransaction();
|
||||
$conn = $table->establishConnection('w');
|
||||
|
||||
$sessions = queryfx_all(
|
||||
$conn,
|
||||
'SELECT userPHID, type, sessionKey FROM %T FOR UPDATE',
|
||||
PhabricatorUser::SESSION_TABLE);
|
||||
|
||||
foreach ($sessions as $session) {
|
||||
queryfx(
|
||||
$conn,
|
||||
'UPDATE %T SET sessionKey = %s WHERE userPHID = %s AND type = %s',
|
||||
PhabricatorUser::SESSION_TABLE,
|
||||
PhabricatorHash::weakDigest($session['sessionKey']),
|
||||
$session['userPHID'],
|
||||
$session['type']);
|
||||
}
|
||||
|
||||
$table->saveTransaction();
|
||||
// See T13225. Long ago, this upgraded session key storage from unhashed to
|
||||
// HMAC-SHA1 here. We later upgraded storage to HMAC-SHA256, so this is initial
|
||||
// upgrade is now fairly pointless. Dropping this migration entirely only logs
|
||||
// users out of installs that waited more than 5 years to upgrade, which seems
|
||||
// like a reasonable behavior.
|
||||
|
@@ -200,9 +200,28 @@ $user->openTransaction();
|
||||
$editor->updateUser($user, $verify_email);
|
||||
}
|
||||
|
||||
$editor->makeAdminUser($user, $set_admin);
|
||||
$editor->makeSystemAgentUser($user, $set_system_agent);
|
||||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhabricatorUserTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorUserEmpowerTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($set_admin);
|
||||
|
||||
$actor = PhabricatorUser::getOmnipotentUser();
|
||||
$content_source = PhabricatorContentSource::newForSource(
|
||||
PhabricatorConsoleContentSource::SOURCECONST);
|
||||
|
||||
$people_application_phid = id(new PhabricatorPeopleApplication())->getPHID();
|
||||
|
||||
$transaction_editor = id(new PhabricatorUserTransactionEditor())
|
||||
->setActor($actor)
|
||||
->setActingAsPHID($people_application_phid)
|
||||
->setContentSource($content_source)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$transaction_editor->applyTransactions($user, $xactions);
|
||||
|
||||
$user->saveTransaction();
|
||||
|
||||
echo pht('Saved changes.')."\n";
|
||||
|
@@ -647,6 +647,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialRevisionSummaryTransaction' => 'applications/differential/xaction/DifferentialRevisionSummaryTransaction.php',
|
||||
'DifferentialRevisionTestPlanHeraldField' => 'applications/differential/herald/DifferentialRevisionTestPlanHeraldField.php',
|
||||
'DifferentialRevisionTestPlanTransaction' => 'applications/differential/xaction/DifferentialRevisionTestPlanTransaction.php',
|
||||
'DifferentialRevisionTimelineEngine' => 'applications/differential/engine/DifferentialRevisionTimelineEngine.php',
|
||||
'DifferentialRevisionTitleHeraldField' => 'applications/differential/herald/DifferentialRevisionTitleHeraldField.php',
|
||||
'DifferentialRevisionTitleTransaction' => 'applications/differential/xaction/DifferentialRevisionTitleTransaction.php',
|
||||
'DifferentialRevisionTransactionType' => 'applications/differential/xaction/DifferentialRevisionTransactionType.php',
|
||||
@@ -769,6 +770,7 @@ phutil_register_library_map(array(
|
||||
'DiffusionCommitSearchConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionCommitSearchConduitAPIMethod.php',
|
||||
'DiffusionCommitStateTransaction' => 'applications/diffusion/xaction/DiffusionCommitStateTransaction.php',
|
||||
'DiffusionCommitTagsController' => 'applications/diffusion/controller/DiffusionCommitTagsController.php',
|
||||
'DiffusionCommitTimelineEngine' => 'applications/diffusion/engine/DiffusionCommitTimelineEngine.php',
|
||||
'DiffusionCommitTransactionType' => 'applications/diffusion/xaction/DiffusionCommitTransactionType.php',
|
||||
'DiffusionCommitVerifyTransaction' => 'applications/diffusion/xaction/DiffusionCommitVerifyTransaction.php',
|
||||
'DiffusionCompareController' => 'applications/diffusion/controller/DiffusionCompareController.php',
|
||||
@@ -1630,10 +1632,8 @@ phutil_register_library_map(array(
|
||||
'LegalpadTransaction' => 'applications/legalpad/storage/LegalpadTransaction.php',
|
||||
'LegalpadTransactionComment' => 'applications/legalpad/storage/LegalpadTransactionComment.php',
|
||||
'LegalpadTransactionQuery' => 'applications/legalpad/query/LegalpadTransactionQuery.php',
|
||||
'LegalpadTransactionView' => 'applications/legalpad/view/LegalpadTransactionView.php',
|
||||
'LiskChunkTestCase' => 'infrastructure/storage/lisk/__tests__/LiskChunkTestCase.php',
|
||||
'LiskDAO' => 'infrastructure/storage/lisk/LiskDAO.php',
|
||||
'LiskDAOSet' => 'infrastructure/storage/lisk/LiskDAOSet.php',
|
||||
'LiskDAOTestCase' => 'infrastructure/storage/lisk/__tests__/LiskDAOTestCase.php',
|
||||
'LiskEphemeralObjectException' => 'infrastructure/storage/lisk/LiskEphemeralObjectException.php',
|
||||
'LiskFixtureTestCase' => 'infrastructure/storage/lisk/__tests__/LiskFixtureTestCase.php',
|
||||
@@ -1761,6 +1761,7 @@ phutil_register_library_map(array(
|
||||
'ManiphestTaskStatusTestCase' => 'applications/maniphest/constants/__tests__/ManiphestTaskStatusTestCase.php',
|
||||
'ManiphestTaskStatusTransaction' => 'applications/maniphest/xaction/ManiphestTaskStatusTransaction.php',
|
||||
'ManiphestTaskSubpriorityTransaction' => 'applications/maniphest/xaction/ManiphestTaskSubpriorityTransaction.php',
|
||||
'ManiphestTaskSubtaskController' => 'applications/maniphest/controller/ManiphestTaskSubtaskController.php',
|
||||
'ManiphestTaskSubtypeDatasource' => 'applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php',
|
||||
'ManiphestTaskTestCase' => 'applications/maniphest/__tests__/ManiphestTaskTestCase.php',
|
||||
'ManiphestTaskTitleHeraldField' => 'applications/maniphest/herald/ManiphestTaskTitleHeraldField.php',
|
||||
@@ -2085,6 +2086,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php',
|
||||
'PhabricatorAnchorView' => 'view/layout/PhabricatorAnchorView.php',
|
||||
'PhabricatorAphlictManagementDebugWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementDebugWorkflow.php',
|
||||
'PhabricatorAphlictManagementNotifyWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementNotifyWorkflow.php',
|
||||
'PhabricatorAphlictManagementRestartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementRestartWorkflow.php',
|
||||
'PhabricatorAphlictManagementStartWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementStartWorkflow.php',
|
||||
'PhabricatorAphlictManagementStatusWorkflow' => 'applications/aphlict/management/PhabricatorAphlictManagementStatusWorkflow.php',
|
||||
@@ -2189,6 +2191,10 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAuthApplication' => 'applications/auth/application/PhabricatorAuthApplication.php',
|
||||
'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php',
|
||||
'PhabricatorAuthAuthProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php',
|
||||
'PhabricatorAuthChallenge' => 'applications/auth/storage/PhabricatorAuthChallenge.php',
|
||||
'PhabricatorAuthChallengeGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthChallengeGarbageCollector.php',
|
||||
'PhabricatorAuthChallengePHIDType' => 'applications/auth/phid/PhabricatorAuthChallengePHIDType.php',
|
||||
'PhabricatorAuthChallengeQuery' => 'applications/auth/query/PhabricatorAuthChallengeQuery.php',
|
||||
'PhabricatorAuthChangePasswordAction' => 'applications/auth/action/PhabricatorAuthChangePasswordAction.php',
|
||||
'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php',
|
||||
'PhabricatorAuthConduitTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php',
|
||||
@@ -2200,6 +2206,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAuthEditController' => 'applications/auth/controller/config/PhabricatorAuthEditController.php',
|
||||
'PhabricatorAuthFactor' => 'applications/auth/factor/PhabricatorAuthFactor.php',
|
||||
'PhabricatorAuthFactorConfig' => 'applications/auth/storage/PhabricatorAuthFactorConfig.php',
|
||||
'PhabricatorAuthFactorResult' => 'applications/auth/factor/PhabricatorAuthFactorResult.php',
|
||||
'PhabricatorAuthFactorTestCase' => 'applications/auth/factor/__tests__/PhabricatorAuthFactorTestCase.php',
|
||||
'PhabricatorAuthFinishController' => 'applications/auth/controller/PhabricatorAuthFinishController.php',
|
||||
'PhabricatorAuthHMACKey' => 'applications/auth/storage/PhabricatorAuthHMACKey.php',
|
||||
@@ -2298,6 +2305,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAuthSessionEngineExtensionModule' => 'applications/auth/engine/PhabricatorAuthSessionEngineExtensionModule.php',
|
||||
'PhabricatorAuthSessionGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthSessionGarbageCollector.php',
|
||||
'PhabricatorAuthSessionInfo' => 'applications/auth/data/PhabricatorAuthSessionInfo.php',
|
||||
'PhabricatorAuthSessionPHIDType' => 'applications/auth/phid/PhabricatorAuthSessionPHIDType.php',
|
||||
'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php',
|
||||
'PhabricatorAuthSessionRevoker' => 'applications/auth/revoker/PhabricatorAuthSessionRevoker.php',
|
||||
'PhabricatorAuthSetPasswordController' => 'applications/auth/controller/PhabricatorAuthSetPasswordController.php',
|
||||
@@ -2980,6 +2988,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorEditEngineStaticCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineStaticCommentAction.php',
|
||||
'PhabricatorEditEngineSubtype' => 'applications/transactions/editengine/PhabricatorEditEngineSubtype.php',
|
||||
'PhabricatorEditEngineSubtypeInterface' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeInterface.php',
|
||||
'PhabricatorEditEngineSubtypeMap' => 'applications/transactions/editengine/PhabricatorEditEngineSubtypeMap.php',
|
||||
'PhabricatorEditEngineSubtypeTestCase' => 'applications/transactions/editengine/__tests__/PhabricatorEditEngineSubtypeTestCase.php',
|
||||
'PhabricatorEditEngineTokenizerCommentAction' => 'applications/transactions/commentaction/PhabricatorEditEngineTokenizerCommentAction.php',
|
||||
'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
|
||||
@@ -3506,7 +3515,6 @@ phutil_register_library_map(array(
|
||||
'PhabricatorNotificationServersConfigType' => 'applications/notification/config/PhabricatorNotificationServersConfigType.php',
|
||||
'PhabricatorNotificationStatusView' => 'applications/notification/view/PhabricatorNotificationStatusView.php',
|
||||
'PhabricatorNotificationTestController' => 'applications/notification/controller/PhabricatorNotificationTestController.php',
|
||||
'PhabricatorNotificationTestFeedStory' => 'applications/notification/feed/PhabricatorNotificationTestFeedStory.php',
|
||||
'PhabricatorNotificationUIExample' => 'applications/uiexample/examples/PhabricatorNotificationUIExample.php',
|
||||
'PhabricatorNotificationsApplication' => 'applications/notification/application/PhabricatorNotificationsApplication.php',
|
||||
'PhabricatorNotificationsSetting' => 'applications/settings/setting/PhabricatorNotificationsSetting.php',
|
||||
@@ -3764,6 +3772,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
|
||||
'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php',
|
||||
'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php',
|
||||
'PhabricatorPeopleAvailabilitySearchEngineAttachment' => 'applications/people/engineextension/PhabricatorPeopleAvailabilitySearchEngineAttachment.php',
|
||||
'PhabricatorPeopleBadgesProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php',
|
||||
'PhabricatorPeopleCommitsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleCommitsProfileMenuItem.php',
|
||||
'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php',
|
||||
@@ -4430,6 +4439,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorStandardCustomFieldUsers' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldUsers.php',
|
||||
'PhabricatorStandardPageView' => 'view/page/PhabricatorStandardPageView.php',
|
||||
'PhabricatorStandardSelectCustomFieldDatasource' => 'infrastructure/customfield/datasource/PhabricatorStandardSelectCustomFieldDatasource.php',
|
||||
'PhabricatorStandardTimelineEngine' => 'applications/transactions/engine/PhabricatorStandardTimelineEngine.php',
|
||||
'PhabricatorStaticEditField' => 'applications/transactions/editfield/PhabricatorStaticEditField.php',
|
||||
'PhabricatorStatusController' => 'applications/system/controller/PhabricatorStatusController.php',
|
||||
'PhabricatorStatusUIExample' => 'applications/uiexample/examples/PhabricatorStatusUIExample.php',
|
||||
@@ -4529,6 +4539,8 @@ phutil_register_library_map(array(
|
||||
'PhabricatorTimeFormatSetting' => 'applications/settings/setting/PhabricatorTimeFormatSetting.php',
|
||||
'PhabricatorTimeGuard' => 'infrastructure/time/PhabricatorTimeGuard.php',
|
||||
'PhabricatorTimeTestCase' => 'infrastructure/time/__tests__/PhabricatorTimeTestCase.php',
|
||||
'PhabricatorTimelineEngine' => 'applications/transactions/engine/PhabricatorTimelineEngine.php',
|
||||
'PhabricatorTimelineInterface' => 'applications/transactions/interface/PhabricatorTimelineInterface.php',
|
||||
'PhabricatorTimezoneIgnoreOffsetSetting' => 'applications/settings/setting/PhabricatorTimezoneIgnoreOffsetSetting.php',
|
||||
'PhabricatorTimezoneSetting' => 'applications/settings/setting/PhabricatorTimezoneSetting.php',
|
||||
'PhabricatorTimezoneSetupCheck' => 'applications/config/check/PhabricatorTimezoneSetupCheck.php',
|
||||
@@ -4599,6 +4611,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUnknownContentSource' => 'infrastructure/contentsource/PhabricatorUnknownContentSource.php',
|
||||
'PhabricatorUnsubscribedFromObjectEdgeType' => 'applications/transactions/edges/PhabricatorUnsubscribedFromObjectEdgeType.php',
|
||||
'PhabricatorUser' => 'applications/people/storage/PhabricatorUser.php',
|
||||
'PhabricatorUserApproveTransaction' => 'applications/people/xaction/PhabricatorUserApproveTransaction.php',
|
||||
'PhabricatorUserBadgesCacheType' => 'applications/people/cache/PhabricatorUserBadgesCacheType.php',
|
||||
'PhabricatorUserBlurbField' => 'applications/people/customfield/PhabricatorUserBlurbField.php',
|
||||
'PhabricatorUserCache' => 'applications/people/storage/PhabricatorUserCache.php',
|
||||
@@ -4618,6 +4631,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUserEditorTestCase' => 'applications/people/editor/__tests__/PhabricatorUserEditorTestCase.php',
|
||||
'PhabricatorUserEmail' => 'applications/people/storage/PhabricatorUserEmail.php',
|
||||
'PhabricatorUserEmailTestCase' => 'applications/people/storage/__tests__/PhabricatorUserEmailTestCase.php',
|
||||
'PhabricatorUserEmpowerTransaction' => 'applications/people/xaction/PhabricatorUserEmpowerTransaction.php',
|
||||
'PhabricatorUserFerretEngine' => 'applications/people/search/PhabricatorUserFerretEngine.php',
|
||||
'PhabricatorUserFulltextEngine' => 'applications/people/search/PhabricatorUserFulltextEngine.php',
|
||||
'PhabricatorUserIconField' => 'applications/people/customfield/PhabricatorUserIconField.php',
|
||||
@@ -4625,6 +4639,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUserLogView' => 'applications/people/view/PhabricatorUserLogView.php',
|
||||
'PhabricatorUserMessageCountCacheType' => 'applications/people/cache/PhabricatorUserMessageCountCacheType.php',
|
||||
'PhabricatorUserNotificationCountCacheType' => 'applications/people/cache/PhabricatorUserNotificationCountCacheType.php',
|
||||
'PhabricatorUserNotifyTransaction' => 'applications/people/xaction/PhabricatorUserNotifyTransaction.php',
|
||||
'PhabricatorUserPHIDResolver' => 'applications/phid/resolver/PhabricatorUserPHIDResolver.php',
|
||||
'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php',
|
||||
'PhabricatorUserPreferencesCacheType' => 'applications/people/cache/PhabricatorUserPreferencesCacheType.php',
|
||||
@@ -4646,6 +4661,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUserTransaction' => 'applications/people/storage/PhabricatorUserTransaction.php',
|
||||
'PhabricatorUserTransactionEditor' => 'applications/people/editor/PhabricatorUserTransactionEditor.php',
|
||||
'PhabricatorUserTransactionType' => 'applications/people/xaction/PhabricatorUserTransactionType.php',
|
||||
'PhabricatorUserUsernameTransaction' => 'applications/people/xaction/PhabricatorUserUsernameTransaction.php',
|
||||
'PhabricatorUsersEditField' => 'applications/transactions/editfield/PhabricatorUsersEditField.php',
|
||||
'PhabricatorUsersPolicyRule' => 'applications/people/policyrule/PhabricatorUsersPolicyRule.php',
|
||||
'PhabricatorUsersSearchField' => 'applications/people/searchfield/PhabricatorUsersSearchField.php',
|
||||
@@ -4864,6 +4880,7 @@ phutil_register_library_map(array(
|
||||
'PholioMockSearchEngine' => 'applications/pholio/query/PholioMockSearchEngine.php',
|
||||
'PholioMockStatusTransaction' => 'applications/pholio/xaction/PholioMockStatusTransaction.php',
|
||||
'PholioMockThumbGridView' => 'applications/pholio/view/PholioMockThumbGridView.php',
|
||||
'PholioMockTimelineEngine' => 'applications/pholio/engine/PholioMockTimelineEngine.php',
|
||||
'PholioMockTransactionType' => 'applications/pholio/xaction/PholioMockTransactionType.php',
|
||||
'PholioMockViewController' => 'applications/pholio/controller/PholioMockViewController.php',
|
||||
'PholioRemarkupRule' => 'applications/pholio/remarkup/PholioRemarkupRule.php',
|
||||
@@ -5986,6 +6003,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorCustomFieldInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorTimelineInterface',
|
||||
'PhabricatorMentionableInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
@@ -6067,6 +6085,7 @@ phutil_register_library_map(array(
|
||||
'DifferentialRevisionSummaryTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialRevisionTestPlanHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionTestPlanTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialRevisionTimelineEngine' => 'PhabricatorTimelineEngine',
|
||||
'DifferentialRevisionTitleHeraldField' => 'DifferentialRevisionHeraldField',
|
||||
'DifferentialRevisionTitleTransaction' => 'DifferentialRevisionTransactionType',
|
||||
'DifferentialRevisionTransactionType' => 'PhabricatorModularTransactionType',
|
||||
@@ -6189,6 +6208,7 @@ phutil_register_library_map(array(
|
||||
'DiffusionCommitSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'DiffusionCommitStateTransaction' => 'DiffusionCommitTransactionType',
|
||||
'DiffusionCommitTagsController' => 'DiffusionController',
|
||||
'DiffusionCommitTimelineEngine' => 'PhabricatorTimelineEngine',
|
||||
'DiffusionCommitTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'DiffusionCommitVerifyTransaction' => 'DiffusionCommitAuditTransaction',
|
||||
'DiffusionCompareController' => 'DiffusionController',
|
||||
@@ -7196,13 +7216,11 @@ phutil_register_library_map(array(
|
||||
'LegalpadTransaction' => 'PhabricatorModularTransaction',
|
||||
'LegalpadTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
'LegalpadTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'LegalpadTransactionView' => 'PhabricatorApplicationTransactionView',
|
||||
'LiskChunkTestCase' => 'PhabricatorTestCase',
|
||||
'LiskDAO' => array(
|
||||
'Phobject',
|
||||
'AphrontDatabaseTableRefInterface',
|
||||
),
|
||||
'LiskDAOSet' => 'Phobject',
|
||||
'LiskDAOTestCase' => 'PhabricatorTestCase',
|
||||
'LiskEphemeralObjectException' => 'Exception',
|
||||
'LiskFixtureTestCase' => 'PhabricatorTestCase',
|
||||
@@ -7353,6 +7371,7 @@ phutil_register_library_map(array(
|
||||
'ManiphestTaskStatusTestCase' => 'PhabricatorTestCase',
|
||||
'ManiphestTaskStatusTransaction' => 'ManiphestTaskTransactionType',
|
||||
'ManiphestTaskSubpriorityTransaction' => 'ManiphestTaskTransactionType',
|
||||
'ManiphestTaskSubtaskController' => 'ManiphestController',
|
||||
'ManiphestTaskSubtypeDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'ManiphestTaskTestCase' => 'PhabricatorTestCase',
|
||||
'ManiphestTaskTitleHeraldField' => 'ManiphestTaskHeraldField',
|
||||
@@ -7706,6 +7725,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAmazonAuthProvider' => 'PhabricatorOAuth2AuthProvider',
|
||||
'PhabricatorAnchorView' => 'AphrontView',
|
||||
'PhabricatorAphlictManagementDebugWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
'PhabricatorAphlictManagementNotifyWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
'PhabricatorAphlictManagementRestartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
'PhabricatorAphlictManagementStartWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
'PhabricatorAphlictManagementStatusWorkflow' => 'PhabricatorAphlictManagementWorkflow',
|
||||
@@ -7825,6 +7845,13 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAuthApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthChallenge' => array(
|
||||
'PhabricatorAuthDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorAuthChallengeGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'PhabricatorAuthChallengePHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthChallengeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorAuthChangePasswordAction' => 'PhabricatorSystemAction',
|
||||
'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker',
|
||||
@@ -7836,6 +7863,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAuthEditController' => 'PhabricatorAuthProviderConfigController',
|
||||
'PhabricatorAuthFactor' => 'Phobject',
|
||||
'PhabricatorAuthFactorConfig' => 'PhabricatorAuthDAO',
|
||||
'PhabricatorAuthFactorResult' => 'Phobject',
|
||||
'PhabricatorAuthFactorTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorAuthFinishController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthHMACKey' => 'PhabricatorAuthDAO',
|
||||
@@ -7953,6 +7981,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorAuthSessionEngineExtensionModule' => 'PhabricatorConfigModule',
|
||||
'PhabricatorAuthSessionGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||
'PhabricatorAuthSessionInfo' => 'Phobject',
|
||||
'PhabricatorAuthSessionPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorAuthSessionRevoker' => 'PhabricatorAuthRevoker',
|
||||
'PhabricatorAuthSetPasswordController' => 'PhabricatorAuthController',
|
||||
@@ -8740,6 +8769,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorEditEngineSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||
'PhabricatorEditEngineStaticCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||
'PhabricatorEditEngineSubtype' => 'Phobject',
|
||||
'PhabricatorEditEngineSubtypeMap' => 'Phobject',
|
||||
'PhabricatorEditEngineSubtypeTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorEditEngineTokenizerCommentAction' => 'PhabricatorEditEngineCommentAction',
|
||||
'PhabricatorEditField' => 'Phobject',
|
||||
@@ -9328,7 +9358,6 @@ phutil_register_library_map(array(
|
||||
'PhabricatorNotificationServersConfigType' => 'PhabricatorJSONConfigType',
|
||||
'PhabricatorNotificationStatusView' => 'AphrontTagView',
|
||||
'PhabricatorNotificationTestController' => 'PhabricatorNotificationController',
|
||||
'PhabricatorNotificationTestFeedStory' => 'PhabricatorFeedStory',
|
||||
'PhabricatorNotificationUIExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorNotificationsApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorNotificationsSetting' => 'PhabricatorInternalSetting',
|
||||
@@ -9649,6 +9678,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorPeopleApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController',
|
||||
'PhabricatorPeopleAvailabilitySearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorPeopleBadgesProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
'PhabricatorPeopleCommitsProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
'PhabricatorPeopleController' => 'PhabricatorController',
|
||||
@@ -10067,6 +10097,7 @@ phutil_register_library_map(array(
|
||||
'HarbormasterBuildkiteBuildableInterface',
|
||||
'PhabricatorCustomFieldInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorTimelineInterface',
|
||||
'PhabricatorFulltextInterface',
|
||||
'PhabricatorFerretInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
@@ -10458,6 +10489,7 @@ phutil_register_library_map(array(
|
||||
'AphrontResponseProducerInterface',
|
||||
),
|
||||
'PhabricatorStandardSelectCustomFieldDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorStandardTimelineEngine' => 'PhabricatorTimelineEngine',
|
||||
'PhabricatorStaticEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorStatusController' => 'PhabricatorController',
|
||||
'PhabricatorStatusUIExample' => 'PhabricatorUIExample',
|
||||
@@ -10556,6 +10588,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorTimeFormatSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorTimeGuard' => 'Phobject',
|
||||
'PhabricatorTimeTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorTimelineEngine' => 'Phobject',
|
||||
'PhabricatorTimezoneIgnoreOffsetSetting' => 'PhabricatorInternalSetting',
|
||||
'PhabricatorTimezoneSetting' => 'PhabricatorOptionGroupSetting',
|
||||
'PhabricatorTimezoneSetupCheck' => 'PhabricatorSetupCheck',
|
||||
@@ -10650,6 +10683,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorConduitResultInterface',
|
||||
'PhabricatorAuthPasswordHashInterface',
|
||||
),
|
||||
'PhabricatorUserApproveTransaction' => 'PhabricatorUserTransactionType',
|
||||
'PhabricatorUserBadgesCacheType' => 'PhabricatorUserCacheType',
|
||||
'PhabricatorUserBlurbField' => 'PhabricatorUserCustomField',
|
||||
'PhabricatorUserCache' => 'PhabricatorUserDAO',
|
||||
@@ -10672,6 +10706,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUserEditorTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorUserEmail' => 'PhabricatorUserDAO',
|
||||
'PhabricatorUserEmailTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorUserEmpowerTransaction' => 'PhabricatorUserTransactionType',
|
||||
'PhabricatorUserFerretEngine' => 'PhabricatorFerretEngine',
|
||||
'PhabricatorUserFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||
'PhabricatorUserIconField' => 'PhabricatorUserCustomField',
|
||||
@@ -10682,6 +10717,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUserLogView' => 'AphrontView',
|
||||
'PhabricatorUserMessageCountCacheType' => 'PhabricatorUserCacheType',
|
||||
'PhabricatorUserNotificationCountCacheType' => 'PhabricatorUserCacheType',
|
||||
'PhabricatorUserNotifyTransaction' => 'PhabricatorUserTransactionType',
|
||||
'PhabricatorUserPHIDResolver' => 'PhabricatorPHIDResolver',
|
||||
'PhabricatorUserPreferences' => array(
|
||||
'PhabricatorUserDAO',
|
||||
@@ -10708,6 +10744,7 @@ phutil_register_library_map(array(
|
||||
'PhabricatorUserTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorUserTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorUserTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'PhabricatorUserUsernameTransaction' => 'PhabricatorUserTransactionType',
|
||||
'PhabricatorUsersEditField' => 'PhabricatorTokenizerEditField',
|
||||
'PhabricatorUsersPolicyRule' => 'PhabricatorPolicyRule',
|
||||
'PhabricatorUsersSearchField' => 'PhabricatorSearchTokenizerField',
|
||||
@@ -10931,8 +10968,8 @@ phutil_register_library_map(array(
|
||||
'PholioDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'PholioImage' => array(
|
||||
'PholioDAO',
|
||||
'PhabricatorMarkupInterface',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorExtendedPolicyInterface',
|
||||
),
|
||||
'PholioImageDescriptionTransaction' => 'PholioImageTransactionType',
|
||||
'PholioImageFileTransaction' => 'PholioImageTransactionType',
|
||||
@@ -10947,12 +10984,12 @@ phutil_register_library_map(array(
|
||||
'PholioInlineListController' => 'PholioController',
|
||||
'PholioMock' => array(
|
||||
'PholioDAO',
|
||||
'PhabricatorMarkupInterface',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorSubscribableInterface',
|
||||
'PhabricatorTokenReceiverInterface',
|
||||
'PhabricatorFlaggableInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorTimelineInterface',
|
||||
'PhabricatorProjectInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorSpacesInterface',
|
||||
@@ -10987,6 +11024,7 @@ phutil_register_library_map(array(
|
||||
'PholioMockSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PholioMockStatusTransaction' => 'PholioMockTransactionType',
|
||||
'PholioMockThumbGridView' => 'AphrontView',
|
||||
'PholioMockTimelineEngine' => 'PhabricatorTimelineEngine',
|
||||
'PholioMockTransactionType' => 'PholioTransactionType',
|
||||
'PholioMockViewController' => 'PholioController',
|
||||
'PholioRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
|
@@ -29,13 +29,28 @@ final class PhabricatorHighSecurityRequestExceptionHandler
|
||||
$throwable) {
|
||||
|
||||
$viewer = $this->getViewer($request);
|
||||
$results = $throwable->getFactorValidationResults();
|
||||
|
||||
$form = id(new PhabricatorAuthSessionEngine())->renderHighSecurityForm(
|
||||
$throwable->getFactors(),
|
||||
$throwable->getFactorValidationResults(),
|
||||
$results,
|
||||
$viewer,
|
||||
$request);
|
||||
|
||||
$is_wait = false;
|
||||
foreach ($results as $result) {
|
||||
if ($result->getIsWait()) {
|
||||
$is_wait = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($is_wait) {
|
||||
$submit = pht('Wait Patiently');
|
||||
} else {
|
||||
$submit = pht('Enter High Security');
|
||||
}
|
||||
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($viewer)
|
||||
->setTitle(pht('Entering High Security'))
|
||||
@@ -62,7 +77,7 @@ final class PhabricatorHighSecurityRequestExceptionHandler
|
||||
'actions, you should leave high security.'))
|
||||
->setSubmitURI($request->getPath())
|
||||
->addCancelButton($throwable->getCancelURI())
|
||||
->addSubmitButton(pht('Enter High Security'));
|
||||
->addSubmitButton($submit);
|
||||
|
||||
$request_parameters = $request->getPassthroughRequestParameters(
|
||||
$respect_quicksand = true);
|
||||
|
@@ -206,20 +206,10 @@ final class AlmanacBinding
|
||||
return new AlmanacBindingEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new AlmanacBindingTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
@@ -204,21 +204,10 @@ final class AlmanacDevice
|
||||
return new AlmanacDeviceEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new AlmanacDeviceTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorSSHPublicKeyInterface )----------------------------------- */
|
||||
|
||||
|
@@ -168,20 +168,10 @@ final class AlmanacInterface
|
||||
return new AlmanacInterfaceEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new AlmanacInterfaceTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorConduitResultInterface )---------------------------------- */
|
||||
|
||||
|
@@ -191,20 +191,10 @@ final class AlmanacNamespace
|
||||
return new AlmanacNamespaceEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new AlmanacNamespaceTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
@@ -61,21 +61,10 @@ final class AlmanacNetwork
|
||||
return new AlmanacNetworkEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new AlmanacNetworkTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -226,21 +226,10 @@ final class AlmanacService
|
||||
return new AlmanacServiceEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new AlmanacServiceTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
@@ -55,11 +55,22 @@ final class AlmanacBindingInterfaceTransaction
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s changed the interface for this binding from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldHandle(),
|
||||
$this->renderNewHandle());
|
||||
if ($this->getOldValue() === null) {
|
||||
return pht(
|
||||
'%s set the interface for this binding to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderNewHandle());
|
||||
} else if ($this->getNewValue() == null) {
|
||||
return pht(
|
||||
'%s removed the interface for this binding.',
|
||||
$this->renderAuthor());
|
||||
} else {
|
||||
return pht(
|
||||
'%s changed the interface for this binding from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldHandle(),
|
||||
$this->renderNewHandle());
|
||||
}
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
|
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAphlictManagementNotifyWorkflow
|
||||
extends PhabricatorAphlictManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->setName('notify')
|
||||
->setSynopsis(pht('Send a notification to a user.'))
|
||||
->setArguments(
|
||||
array(
|
||||
array(
|
||||
'name' => 'user',
|
||||
'param' => 'username',
|
||||
'help' => pht('User to notify.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'message',
|
||||
'param' => 'text',
|
||||
'help' => pht('Message to send.'),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function execute(PhutilArgumentParser $args) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$username = $args->getArg('user');
|
||||
if (!strlen($username)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specify a user to notify with "--user".'));
|
||||
}
|
||||
|
||||
$user = id(new PhabricatorPeopleQuery())
|
||||
->setViewer($viewer)
|
||||
->withUsernames(array($username))
|
||||
->executeOne();
|
||||
|
||||
if (!$user) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'No user with username "%s" exists.',
|
||||
$username));
|
||||
}
|
||||
|
||||
$message = $args->getArg('message');
|
||||
if (!strlen($message)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Specify a message to send with "--message".'));
|
||||
}
|
||||
|
||||
$application_phid = id(new PhabricatorNotificationsApplication())
|
||||
->getPHID();
|
||||
|
||||
$content_source = $this->newContentSource();
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$xactions[] = id(new PhabricatorUserTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorUserNotifyTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue($message)
|
||||
->setForceNotifyPHIDs(array($user->getPHID()));
|
||||
|
||||
$editor = id(new PhabricatorUserTransactionEditor())
|
||||
->setActor($viewer)
|
||||
->setActingAsPHID($application_phid)
|
||||
->setContentSource($content_source);
|
||||
|
||||
$editor->applyTransactions($user, $xactions);
|
||||
|
||||
echo tsprintf(
|
||||
"%s\n",
|
||||
pht('Sent notification.'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@@ -59,10 +59,6 @@ final class PhabricatorAuditEditor
|
||||
|
||||
$this->oldAuditStatus = $object->getAuditStatus();
|
||||
|
||||
$object->loadAndAttachAuditAuthority(
|
||||
$this->getActor(),
|
||||
$this->getActingAsPHID());
|
||||
|
||||
return parent::expandTransactions($object, $xactions);
|
||||
}
|
||||
|
||||
@@ -255,47 +251,16 @@ final class PhabricatorAuditEditor
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
$this->didExpandInlineState = true;
|
||||
|
||||
$actor_phid = $this->getActingAsPHID();
|
||||
$author_phid = $object->getAuthorPHID();
|
||||
$actor_is_author = ($actor_phid == $author_phid);
|
||||
$query_template = id(new DiffusionDiffInlineCommentQuery())
|
||||
->withCommitPHIDs(array($object->getPHID()));
|
||||
|
||||
$state_map = PhabricatorTransactions::getInlineStateMap();
|
||||
$state_xaction = $this->newInlineStateTransaction(
|
||||
$object,
|
||||
$query_template);
|
||||
|
||||
$query = id(new DiffusionDiffInlineCommentQuery())
|
||||
->setViewer($this->getActor())
|
||||
->withCommitPHIDs(array($object->getPHID()))
|
||||
->withFixedStates(array_keys($state_map));
|
||||
|
||||
$inlines = array();
|
||||
|
||||
$inlines[] = id(clone $query)
|
||||
->withAuthorPHIDs(array($actor_phid))
|
||||
->withHasTransaction(false)
|
||||
->execute();
|
||||
|
||||
if ($actor_is_author) {
|
||||
$inlines[] = id(clone $query)
|
||||
->withHasTransaction(true)
|
||||
->execute();
|
||||
if ($state_xaction) {
|
||||
$xactions[] = $state_xaction;
|
||||
}
|
||||
|
||||
$inlines = array_mergev($inlines);
|
||||
|
||||
if (!$inlines) {
|
||||
break;
|
||||
}
|
||||
|
||||
$old_value = mpull($inlines, 'getFixedState', 'getPHID');
|
||||
$new_value = array();
|
||||
foreach ($old_value as $key => $state) {
|
||||
$new_value[$key] = $state_map[$state];
|
||||
}
|
||||
|
||||
$xactions[] = id(new PhabricatorAuditTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE)
|
||||
->setIgnoreOnNoEffect(true)
|
||||
->setOldValue($old_value)
|
||||
->setNewValue($new_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@@ -32,10 +32,6 @@ final class PhabricatorAuditTransaction
|
||||
return new PhabricatorAuditTransactionComment();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionViewObject() {
|
||||
return new PhabricatorAuditTransactionView();
|
||||
}
|
||||
|
||||
public function getRemarkupBlocks() {
|
||||
$blocks = parent::getRemarkupBlocks();
|
||||
|
||||
|
@@ -9,7 +9,7 @@ final class PhabricatorAuthTryFactorAction extends PhabricatorSystemAction {
|
||||
}
|
||||
|
||||
public function getScoreThreshold() {
|
||||
return 10 / phutil_units('1 hour in seconds');
|
||||
return 100 / phutil_units('1 hour in seconds');
|
||||
}
|
||||
|
||||
public function getLimitExplanation() {
|
||||
|
@@ -424,7 +424,26 @@ final class PhabricatorAuthRegisterController
|
||||
}
|
||||
|
||||
if ($is_setup) {
|
||||
$editor->makeAdminUser($user, true);
|
||||
$xactions = array();
|
||||
$xactions[] = id(new PhabricatorUserTransaction())
|
||||
->setTransactionType(
|
||||
PhabricatorUserEmpowerTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(true);
|
||||
|
||||
$actor = PhabricatorUser::getOmnipotentUser();
|
||||
$content_source = PhabricatorContentSource::newFromRequest(
|
||||
$request);
|
||||
|
||||
$people_application_phid = id(new PhabricatorPeopleApplication())
|
||||
->getPHID();
|
||||
|
||||
$transaction_editor = id(new PhabricatorUserTransactionEditor())
|
||||
->setActor($actor)
|
||||
->setActingAsPHID($people_application_phid)
|
||||
->setContentSource($content_source)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$transaction_editor->applyTransactions($user, $xactions);
|
||||
}
|
||||
|
||||
$account->setUserPHID($user->getPHID());
|
||||
|
@@ -16,8 +16,9 @@ final class PhabricatorAuthTerminateSessionController
|
||||
$query->withIDs(array($id));
|
||||
}
|
||||
|
||||
$current_key = PhabricatorHash::weakDigest(
|
||||
$request->getCookie(PhabricatorCookies::COOKIE_SESSION));
|
||||
$current_key = PhabricatorAuthSession::newSessionDigest(
|
||||
new PhutilOpaqueEnvelope(
|
||||
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
|
||||
|
||||
$sessions = $query->execute();
|
||||
foreach ($sessions as $key => $session) {
|
||||
|
@@ -56,7 +56,8 @@ final class PhabricatorAuthUnlinkController
|
||||
|
||||
id(new PhabricatorAuthSessionEngine())->terminateLoginSessions(
|
||||
$viewer,
|
||||
$request->getCookie(PhabricatorCookies::COOKIE_SESSION));
|
||||
new PhutilOpaqueEnvelope(
|
||||
$request->getCookie(PhabricatorCookies::COOKIE_SESSION)));
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($this->getDoneURI());
|
||||
}
|
||||
|
@@ -46,6 +46,26 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
const ONETIME_USERNAME = 'rename';
|
||||
|
||||
|
||||
private $workflowKey;
|
||||
|
||||
public function setWorkflowKey($workflow_key) {
|
||||
$this->workflowKey = $workflow_key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWorkflowKey() {
|
||||
|
||||
// TODO: A workflow key should become required in order to issue an MFA
|
||||
// challenge, but allow things to keep working for now until we can update
|
||||
// callsites.
|
||||
if ($this->workflowKey === null) {
|
||||
return 'legacy';
|
||||
}
|
||||
|
||||
return $this->workflowKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the session kind (e.g., anonymous, user, external account) from a
|
||||
* session token. Returns a `KIND_` constant.
|
||||
@@ -109,36 +129,49 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
|
||||
$session_table = new PhabricatorAuthSession();
|
||||
$user_table = new PhabricatorUser();
|
||||
$conn_r = $session_table->establishConnection('r');
|
||||
$session_key = PhabricatorHash::weakDigest($session_token);
|
||||
$conn = $session_table->establishConnection('r');
|
||||
|
||||
$cache_parts = $this->getUserCacheQueryParts($conn_r);
|
||||
// TODO: See T13225. We're moving sessions to a more modern digest
|
||||
// algorithm, but still accept older cookies for compatibility.
|
||||
$session_key = PhabricatorAuthSession::newSessionDigest(
|
||||
new PhutilOpaqueEnvelope($session_token));
|
||||
$weak_key = PhabricatorHash::weakDigest($session_token);
|
||||
|
||||
$cache_parts = $this->getUserCacheQueryParts($conn);
|
||||
list($cache_selects, $cache_joins, $cache_map, $types_map) = $cache_parts;
|
||||
|
||||
$info = queryfx_one(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'SELECT
|
||||
s.id AS s_id,
|
||||
s.phid AS s_phid,
|
||||
s.sessionExpires AS s_sessionExpires,
|
||||
s.sessionStart AS s_sessionStart,
|
||||
s.highSecurityUntil AS s_highSecurityUntil,
|
||||
s.isPartial AS s_isPartial,
|
||||
s.signedLegalpadDocuments as s_signedLegalpadDocuments,
|
||||
IF(s.sessionKey = %P, 1, 0) as s_weak,
|
||||
u.*
|
||||
%Q
|
||||
FROM %T u JOIN %T s ON u.phid = s.userPHID
|
||||
AND s.type = %s AND s.sessionKey = %P %Q',
|
||||
FROM %R u JOIN %R s ON u.phid = s.userPHID
|
||||
AND s.type = %s AND s.sessionKey IN (%P, %P) %Q',
|
||||
new PhutilOpaqueEnvelope($weak_key),
|
||||
$cache_selects,
|
||||
$user_table->getTableName(),
|
||||
$session_table->getTableName(),
|
||||
$user_table,
|
||||
$session_table,
|
||||
$session_type,
|
||||
new PhutilOpaqueEnvelope($session_key),
|
||||
new PhutilOpaqueEnvelope($weak_key),
|
||||
$cache_joins);
|
||||
|
||||
if (!$info) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Remove this, see T13225.
|
||||
$is_weak = (bool)$info['s_weak'];
|
||||
unset($info['s_weak']);
|
||||
|
||||
$session_dict = array(
|
||||
'userPHID' => $info['phid'],
|
||||
'sessionKey' => $session_key,
|
||||
@@ -201,6 +234,19 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
unset($unguarded);
|
||||
}
|
||||
|
||||
// TODO: Remove this, see T13225.
|
||||
if ($is_weak) {
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$conn_w = $session_table->establishConnection('w');
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'UPDATE %T SET sessionKey = %P WHERE id = %d',
|
||||
$session->getTableName(),
|
||||
new PhutilOpaqueEnvelope($session_key),
|
||||
$session->getID());
|
||||
unset($unguarded);
|
||||
}
|
||||
|
||||
$user->attachSession($session);
|
||||
return $user;
|
||||
}
|
||||
@@ -240,7 +286,8 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
// This has a side effect of validating the session type.
|
||||
$session_ttl = PhabricatorAuthSession::getSessionTypeTTL($session_type);
|
||||
|
||||
$digest_key = PhabricatorHash::weakDigest($session_key);
|
||||
$digest_key = PhabricatorAuthSession::newSessionDigest(
|
||||
new PhutilOpaqueEnvelope($session_key));
|
||||
|
||||
// Logging-in users don't have CSRF stuff yet, so we have to unguard this
|
||||
// write.
|
||||
@@ -298,7 +345,7 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
*/
|
||||
public function terminateLoginSessions(
|
||||
PhabricatorUser $user,
|
||||
$except_session = null) {
|
||||
PhutilOpaqueEnvelope $except_session = null) {
|
||||
|
||||
$sessions = id(new PhabricatorAuthSessionQuery())
|
||||
->setViewer($user)
|
||||
@@ -306,7 +353,8 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
->execute();
|
||||
|
||||
if ($except_session !== null) {
|
||||
$except_session = PhabricatorHash::weakDigest($except_session);
|
||||
$except_session = PhabricatorAuthSession::newSessionDigest(
|
||||
$except_session);
|
||||
}
|
||||
|
||||
foreach ($sessions as $key => $session) {
|
||||
@@ -445,6 +493,10 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
return $this->issueHighSecurityToken($session, true);
|
||||
}
|
||||
|
||||
foreach ($factors as $factor) {
|
||||
$factor->setSessionEngine($this);
|
||||
}
|
||||
|
||||
// Check for a rate limit without awarding points, so the user doesn't
|
||||
// get partway through the workflow only to get blocked.
|
||||
PhabricatorSystemActionEngine::willTakeAction(
|
||||
@@ -452,7 +504,64 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
new PhabricatorAuthTryFactorAction(),
|
||||
0);
|
||||
|
||||
$now = PhabricatorTime::getNow();
|
||||
|
||||
// We need to do challenge validation first, since this happens whether you
|
||||
// submitted responses or not. You can't get a "bad response" error before
|
||||
// you actually submit a response, but you can get a "wait, we can't
|
||||
// issue a challenge yet" response. Load all issued challenges which are
|
||||
// currently valid.
|
||||
$challenges = id(new PhabricatorAuthChallengeQuery())
|
||||
->setViewer($viewer)
|
||||
->withFactorPHIDs(mpull($factors, 'getPHID'))
|
||||
->withUserPHIDs(array($viewer->getPHID()))
|
||||
->withChallengeTTLBetween($now, null)
|
||||
->execute();
|
||||
|
||||
PhabricatorAuthChallenge::newChallengeResponsesFromRequest(
|
||||
$challenges,
|
||||
$request);
|
||||
|
||||
$challenge_map = mgroup($challenges, 'getFactorPHID');
|
||||
|
||||
$validation_results = array();
|
||||
$ok = true;
|
||||
|
||||
// Validate each factor against issued challenges. For example, this
|
||||
// prevents you from receiving or responding to a TOTP challenge if another
|
||||
// challenge was recently issued to a different session.
|
||||
foreach ($factors as $factor) {
|
||||
$factor_phid = $factor->getPHID();
|
||||
$issued_challenges = idx($challenge_map, $factor_phid, array());
|
||||
$impl = $factor->requireImplementation();
|
||||
|
||||
$new_challenges = $impl->getNewIssuedChallenges(
|
||||
$factor,
|
||||
$viewer,
|
||||
$issued_challenges);
|
||||
|
||||
foreach ($new_challenges as $new_challenge) {
|
||||
$issued_challenges[] = $new_challenge;
|
||||
}
|
||||
$challenge_map[$factor_phid] = $issued_challenges;
|
||||
|
||||
if (!$issued_challenges) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $impl->getResultFromIssuedChallenges(
|
||||
$factor,
|
||||
$viewer,
|
||||
$issued_challenges);
|
||||
|
||||
if (!$result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ok = false;
|
||||
$validation_results[$factor_phid] = $result;
|
||||
}
|
||||
|
||||
if ($request->isHTTPPost()) {
|
||||
$request->validateCSRF();
|
||||
if ($request->getExists(AphrontRequest::TYPE_HISEC)) {
|
||||
@@ -463,22 +572,43 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
new PhabricatorAuthTryFactorAction(),
|
||||
1);
|
||||
|
||||
$ok = true;
|
||||
foreach ($factors as $factor) {
|
||||
$id = $factor->getID();
|
||||
$factor_phid = $factor->getPHID();
|
||||
|
||||
// If we already have a validation result from previously issued
|
||||
// challenges, skip validating this factor.
|
||||
if (isset($validation_results[$factor_phid])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$issued_challenges = idx($challenge_map, $factor_phid, array());
|
||||
|
||||
$impl = $factor->requireImplementation();
|
||||
|
||||
$validation_results[$id] = $impl->processValidateFactorForm(
|
||||
$validation_result = $impl->getResultFromChallengeResponse(
|
||||
$factor,
|
||||
$viewer,
|
||||
$request);
|
||||
$request,
|
||||
$issued_challenges);
|
||||
|
||||
if (!$impl->isFactorValid($factor, $validation_results[$id])) {
|
||||
if (!$validation_result->getIsValid()) {
|
||||
$ok = false;
|
||||
}
|
||||
|
||||
$validation_results[$factor_phid] = $validation_result;
|
||||
}
|
||||
|
||||
if ($ok) {
|
||||
// We're letting you through, so mark all the challenges you
|
||||
// responded to as completed. These challenges can never be used
|
||||
// again, even by the same session and workflow: you can't use the
|
||||
// same response to take two different actions, even if those actions
|
||||
// are of the same type.
|
||||
foreach ($validation_results as $validation_result) {
|
||||
$challenge = $validation_result->getAnsweredChallenge()
|
||||
->markChallengeAsCompleted();
|
||||
}
|
||||
|
||||
// Give the user a credit back for a successful factor verification.
|
||||
PhabricatorSystemActionEngine::willTakeAction(
|
||||
array($viewer->getPHID()),
|
||||
@@ -527,6 +657,18 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
return $token;
|
||||
}
|
||||
|
||||
// If we don't have a validation result for some factors yet, fill them
|
||||
// in with an empty result so form rendering doesn't have to care if the
|
||||
// results exist or not. This happens when you first load the form and have
|
||||
// not submitted any responses yet.
|
||||
foreach ($factors as $factor) {
|
||||
$factor_phid = $factor->getPHID();
|
||||
if (isset($validation_results[$factor_phid])) {
|
||||
continue;
|
||||
}
|
||||
$validation_results[$factor_phid] = new PhabricatorAuthFactorResult();
|
||||
}
|
||||
|
||||
throw id(new PhabricatorAuthHighSecurityRequiredException())
|
||||
->setCancelURI($cancel_uri)
|
||||
->setFactors($factors)
|
||||
@@ -567,21 +709,38 @@ final class PhabricatorAuthSessionEngine extends Phobject {
|
||||
array $validation_results,
|
||||
PhabricatorUser $viewer,
|
||||
AphrontRequest $request) {
|
||||
assert_instances_of($validation_results, 'PhabricatorAuthFactorResult');
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendRemarkupInstructions('');
|
||||
|
||||
$answered = array();
|
||||
foreach ($factors as $factor) {
|
||||
$result = $validation_results[$factor->getPHID()];
|
||||
|
||||
$factor->requireImplementation()->renderValidateFactorForm(
|
||||
$factor,
|
||||
$form,
|
||||
$viewer,
|
||||
idx($validation_results, $factor->getID()));
|
||||
$result);
|
||||
|
||||
$answered_challenge = $result->getAnsweredChallenge();
|
||||
if ($answered_challenge) {
|
||||
$answered[] = $answered_challenge;
|
||||
}
|
||||
}
|
||||
|
||||
$form->appendRemarkupInstructions('');
|
||||
|
||||
if ($answered) {
|
||||
$http_params = PhabricatorAuthChallenge::newHTTPParametersFromChallenges(
|
||||
$answered);
|
||||
foreach ($http_params as $key => $value) {
|
||||
$form->addHiddenInput($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@ final class PhabricatorAuthHighSecurityRequiredException extends Exception {
|
||||
private $factorValidationResults;
|
||||
|
||||
public function setFactorValidationResults(array $results) {
|
||||
assert_instances_of($results, 'PhabricatorAuthFactorResult');
|
||||
$this->factorValidationResults = $results;
|
||||
return $this;
|
||||
}
|
||||
|
@@ -14,18 +14,7 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
AphrontFormView $form,
|
||||
PhabricatorUser $viewer,
|
||||
$validation_result);
|
||||
|
||||
abstract public function processValidateFactorForm(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
AphrontRequest $request);
|
||||
|
||||
public function isFactorValid(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
$validation_result) {
|
||||
return (idx($validation_result, 'valid') === true);
|
||||
}
|
||||
PhabricatorAuthFactorResult $validation_result);
|
||||
|
||||
public function getParameterName(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
@@ -46,4 +35,168 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
||||
->setFactorKey($this->getFactorKey());
|
||||
}
|
||||
|
||||
protected function newResult() {
|
||||
return new PhabricatorAuthFactorResult();
|
||||
}
|
||||
|
||||
protected function newChallenge(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer) {
|
||||
|
||||
$engine = $config->getSessionEngine();
|
||||
|
||||
return PhabricatorAuthChallenge::initializeNewChallenge()
|
||||
->setUserPHID($viewer->getPHID())
|
||||
->setSessionPHID($viewer->getSession()->getPHID())
|
||||
->setFactorPHID($config->getPHID())
|
||||
->setWorkflowKey($engine->getWorkflowKey());
|
||||
}
|
||||
|
||||
final public function getNewIssuedChallenges(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
array $challenges) {
|
||||
assert_instances_of($challenges, 'PhabricatorAuthChallenge');
|
||||
|
||||
$now = PhabricatorTime::getNow();
|
||||
|
||||
$new_challenges = $this->newIssuedChallenges(
|
||||
$config,
|
||||
$viewer,
|
||||
$challenges);
|
||||
|
||||
assert_instances_of($new_challenges, 'PhabricatorAuthChallenge');
|
||||
|
||||
foreach ($new_challenges as $new_challenge) {
|
||||
$ttl = $new_challenge->getChallengeTTL();
|
||||
if (!$ttl) {
|
||||
throw new Exception(
|
||||
pht('Newly issued MFA challenges must have a valid TTL!'));
|
||||
}
|
||||
|
||||
if ($ttl < $now) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Newly issued MFA challenges must have a future TTL. This '.
|
||||
'factor issued a bad TTL ("%s"). (Did you use a relative '.
|
||||
'time instead of an epoch?)',
|
||||
$ttl));
|
||||
}
|
||||
}
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
foreach ($new_challenges as $challenge) {
|
||||
$challenge->save();
|
||||
}
|
||||
unset($unguarded);
|
||||
|
||||
return $new_challenges;
|
||||
}
|
||||
|
||||
abstract protected function newIssuedChallenges(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
array $challenges);
|
||||
|
||||
final public function getResultFromIssuedChallenges(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
array $challenges) {
|
||||
assert_instances_of($challenges, 'PhabricatorAuthChallenge');
|
||||
|
||||
$result = $this->newResultFromIssuedChallenges(
|
||||
$config,
|
||||
$viewer,
|
||||
$challenges);
|
||||
|
||||
if ($result === null) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!($result instanceof PhabricatorAuthFactorResult)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Expected "newResultFromIssuedChallenges()" to return null or '.
|
||||
'an object of class "%s"; got something else (in "%s").',
|
||||
'PhabricatorAuthFactorResult',
|
||||
get_class($this)));
|
||||
}
|
||||
|
||||
$result->setIssuedChallenges($challenges);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
abstract protected function newResultFromIssuedChallenges(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
array $challenges);
|
||||
|
||||
final public function getResultFromChallengeResponse(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
AphrontRequest $request,
|
||||
array $challenges) {
|
||||
assert_instances_of($challenges, 'PhabricatorAuthChallenge');
|
||||
|
||||
$result = $this->newResultFromChallengeResponse(
|
||||
$config,
|
||||
$viewer,
|
||||
$request,
|
||||
$challenges);
|
||||
|
||||
if (!($result instanceof PhabricatorAuthFactorResult)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Expected "newResultFromChallengeResponse()" to return an object '.
|
||||
'of class "%s"; got something else (in "%s").',
|
||||
'PhabricatorAuthFactorResult',
|
||||
get_class($this)));
|
||||
}
|
||||
|
||||
$result->setIssuedChallenges($challenges);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
abstract protected function newResultFromChallengeResponse(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
AphrontRequest $request,
|
||||
array $challenges);
|
||||
|
||||
final protected function newAutomaticControl(
|
||||
PhabricatorAuthFactorResult $result) {
|
||||
|
||||
$is_answered = (bool)$result->getAnsweredChallenge();
|
||||
if ($is_answered) {
|
||||
return $this->newAnsweredControl($result);
|
||||
}
|
||||
|
||||
$is_wait = $result->getIsWait();
|
||||
if ($is_wait) {
|
||||
return $this->newWaitControl($result);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function newWaitControl(
|
||||
PhabricatorAuthFactorResult $result) {
|
||||
|
||||
$error = $result->getErrorMessage();
|
||||
|
||||
return id(new AphrontFormMarkupControl())
|
||||
->setValue($error)
|
||||
->setError(pht('Wait'));
|
||||
}
|
||||
|
||||
private function newAnsweredControl(
|
||||
PhabricatorAuthFactorResult $result) {
|
||||
|
||||
return id(new AphrontFormMarkupControl())
|
||||
->setValue(pht('Answered!'));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
75
src/applications/auth/factor/PhabricatorAuthFactorResult.php
Normal file
75
src/applications/auth/factor/PhabricatorAuthFactorResult.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorResult
|
||||
extends Phobject {
|
||||
|
||||
private $answeredChallenge;
|
||||
private $isWait = false;
|
||||
private $errorMessage;
|
||||
private $value;
|
||||
private $issuedChallenges = array();
|
||||
|
||||
public function setAnsweredChallenge(PhabricatorAuthChallenge $challenge) {
|
||||
if (!$challenge->getIsAnsweredChallenge()) {
|
||||
throw new PhutilInvalidStateException('markChallengeAsAnswered');
|
||||
}
|
||||
|
||||
if ($challenge->getIsCompleted()) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'A completed challenge was provided as an answered challenge. '.
|
||||
'The underlying factor is implemented improperly, challenges '.
|
||||
'may not be reused.'));
|
||||
}
|
||||
|
||||
$this->answeredChallenge = $challenge;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAnsweredChallenge() {
|
||||
return $this->answeredChallenge;
|
||||
}
|
||||
|
||||
public function getIsValid() {
|
||||
return (bool)$this->getAnsweredChallenge();
|
||||
}
|
||||
|
||||
public function setIsWait($is_wait) {
|
||||
$this->isWait = $is_wait;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsWait() {
|
||||
return $this->isWait;
|
||||
}
|
||||
|
||||
public function setErrorMessage($error_message) {
|
||||
$this->errorMessage = $error_message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getErrorMessage() {
|
||||
return $this->errorMessage;
|
||||
}
|
||||
|
||||
public function setValue($value) {
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setIssuedChallenges(array $issued_challenges) {
|
||||
assert_instances_of($issued_challenges, 'PhabricatorAuthChallenge');
|
||||
$this->issuedChallenges = $issued_challenges;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIssuedChallenges() {
|
||||
return $this->issuedChallenges;
|
||||
}
|
||||
|
||||
}
|
@@ -2,6 +2,8 @@
|
||||
|
||||
final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
|
||||
const DIGEST_TEMPORARY_KEY = 'mfa.totp.sync';
|
||||
|
||||
public function getFactorKey() {
|
||||
return 'totp';
|
||||
}
|
||||
@@ -34,12 +36,16 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
// (We store and verify the hash of the key, not the key itself, to limit
|
||||
// how useful the data in the table is to an attacker.)
|
||||
|
||||
$token_code = PhabricatorHash::digestWithNamedKey(
|
||||
$key,
|
||||
self::DIGEST_TEMPORARY_KEY);
|
||||
|
||||
$temporary_token = id(new PhabricatorAuthTemporaryTokenQuery())
|
||||
->setViewer($user)
|
||||
->withTokenResources(array($user->getPHID()))
|
||||
->withTokenTypes(array($totp_token_type))
|
||||
->withExpired(false)
|
||||
->withTokenCodes(array(PhabricatorHash::weakDigest($key)))
|
||||
->withTokenCodes(array($token_code))
|
||||
->executeOne();
|
||||
if (!$temporary_token) {
|
||||
// If we don't have a matching token, regenerate the key below.
|
||||
@@ -53,12 +59,16 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
// Mark this key as one we generated, so the user is allowed to submit
|
||||
// a response for it.
|
||||
|
||||
$token_code = PhabricatorHash::digestWithNamedKey(
|
||||
$key,
|
||||
self::DIGEST_TEMPORARY_KEY);
|
||||
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
id(new PhabricatorAuthTemporaryToken())
|
||||
->setTokenResource($user->getPHID())
|
||||
->setTokenType($totp_token_type)
|
||||
->setTokenExpires(time() + phutil_units('1 hour in seconds'))
|
||||
->setTokenCode(PhabricatorHash::weakDigest($key))
|
||||
->setTokenCode($token_code)
|
||||
->save();
|
||||
unset($unguarded);
|
||||
}
|
||||
@@ -67,10 +77,10 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
|
||||
$e_code = true;
|
||||
if ($request->getExists('totp')) {
|
||||
$okay = self::verifyTOTPCode(
|
||||
$user,
|
||||
$okay = (bool)$this->getTimestepAtWhichResponseIsValid(
|
||||
$this->getAllowedTimesteps($this->getCurrentTimestep()),
|
||||
new PhutilOpaqueEnvelope($key),
|
||||
$code);
|
||||
(string)$code);
|
||||
|
||||
if ($okay) {
|
||||
$config = $this->newConfigForUser($user)
|
||||
@@ -140,77 +150,220 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
|
||||
}
|
||||
|
||||
protected function newIssuedChallenges(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
array $challenges) {
|
||||
|
||||
$current_step = $this->getCurrentTimestep();
|
||||
|
||||
// If we already issued a valid challenge, don't issue a new one.
|
||||
if ($challenges) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Otherwise, generate a new challenge for the current timestep and compute
|
||||
// the TTL.
|
||||
|
||||
// When computing the TTL, note that we accept codes within a certain
|
||||
// window of the challenge timestep to account for clock skew and users
|
||||
// needing time to enter codes.
|
||||
|
||||
// We don't want this challenge to expire until after all valid responses
|
||||
// to it are no longer valid responses to any other challenge we might
|
||||
// issue in the future. If the challenge expires too quickly, we may issue
|
||||
// a new challenge which can accept the same TOTP code response.
|
||||
|
||||
// This means that we need to keep this challenge alive for double the
|
||||
// window size: if we're currently at timestep 3, the user might respond
|
||||
// with the code for timestep 5. This is valid, since timestep 5 is within
|
||||
// the window for timestep 3.
|
||||
|
||||
// But the code for timestep 5 can be used to respond at timesteps 3, 4, 5,
|
||||
// 6, and 7. To prevent any valid response to this challenge from being
|
||||
// used again, we need to keep this challenge active until timestep 8.
|
||||
|
||||
$window_size = $this->getTimestepWindowSize();
|
||||
$step_duration = $this->getTimestepDuration();
|
||||
|
||||
$ttl_steps = ($window_size * 2) + 1;
|
||||
$ttl_seconds = ($ttl_steps * $step_duration);
|
||||
|
||||
return array(
|
||||
$this->newChallenge($config, $viewer)
|
||||
->setChallengeKey($current_step)
|
||||
->setChallengeTTL(PhabricatorTime::getNow() + $ttl_seconds),
|
||||
);
|
||||
}
|
||||
|
||||
public function renderValidateFactorForm(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
AphrontFormView $form,
|
||||
PhabricatorUser $viewer,
|
||||
$validation_result) {
|
||||
PhabricatorAuthFactorResult $result) {
|
||||
|
||||
if (!$validation_result) {
|
||||
$validation_result = array();
|
||||
$control = $this->newAutomaticControl($result);
|
||||
if (!$control) {
|
||||
$value = $result->getValue();
|
||||
$error = $result->getErrorMessage();
|
||||
|
||||
$control = id(new PHUIFormNumberControl())
|
||||
->setName($this->getParameterName($config, 'totpcode'))
|
||||
->setDisableAutocomplete(true)
|
||||
->setValue($value)
|
||||
->setError($error);
|
||||
}
|
||||
|
||||
$form->appendChild(
|
||||
id(new PHUIFormNumberControl())
|
||||
->setName($this->getParameterName($config, 'totpcode'))
|
||||
->setLabel(pht('App Code'))
|
||||
->setDisableAutocomplete(true)
|
||||
->setCaption(pht('Factor Name: %s', $config->getFactorName()))
|
||||
->setValue(idx($validation_result, 'value'))
|
||||
->setError(idx($validation_result, 'error', true)));
|
||||
$control
|
||||
->setLabel(pht('App Code'))
|
||||
->setCaption(pht('Factor Name: %s', $config->getFactorName()));
|
||||
|
||||
$form->appendChild($control);
|
||||
}
|
||||
|
||||
public function processValidateFactorForm(
|
||||
protected function newResultFromIssuedChallenges(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
AphrontRequest $request) {
|
||||
array $challenges) {
|
||||
|
||||
$code = $request->getStr($this->getParameterName($config, 'totpcode'));
|
||||
$key = new PhutilOpaqueEnvelope($config->getFactorSecret());
|
||||
// If we've already issued a challenge at the current timestep or any
|
||||
// nearby timestep, require that it was issued to the current session.
|
||||
// This is defusing attacks where you (broadly) look at someone's phone
|
||||
// and type the code in more quickly than they do.
|
||||
$session_phid = $viewer->getSession()->getPHID();
|
||||
$now = PhabricatorTime::getNow();
|
||||
|
||||
if (self::verifyTOTPCode($viewer, $key, $code)) {
|
||||
return array(
|
||||
'error' => null,
|
||||
'value' => $code,
|
||||
'valid' => true,
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
'error' => strlen($code) ? pht('Invalid') : pht('Required'),
|
||||
'value' => $code,
|
||||
'valid' => false,
|
||||
);
|
||||
$engine = $config->getSessionEngine();
|
||||
$workflow_key = $engine->getWorkflowKey();
|
||||
|
||||
$current_timestep = $this->getCurrentTimestep();
|
||||
|
||||
foreach ($challenges as $challenge) {
|
||||
$challenge_timestep = (int)$challenge->getChallengeKey();
|
||||
$wait_duration = ($challenge->getChallengeTTL() - $now) + 1;
|
||||
|
||||
if ($challenge->getSessionPHID() !== $session_phid) {
|
||||
return $this->newResult()
|
||||
->setIsWait(true)
|
||||
->setErrorMessage(
|
||||
pht(
|
||||
'This factor recently issued a challenge to a different login '.
|
||||
'session. Wait %s second(s) for the code to cycle, then try '.
|
||||
'again.',
|
||||
new PhutilNumber($wait_duration)));
|
||||
}
|
||||
|
||||
if ($challenge->getWorkflowKey() !== $workflow_key) {
|
||||
return $this->newResult()
|
||||
->setIsWait(true)
|
||||
->setErrorMessage(
|
||||
pht(
|
||||
'This factor recently issued a challenge for a different '.
|
||||
'workflow. Wait %s second(s) for the code to cycle, then try '.
|
||||
'again.',
|
||||
new PhutilNumber($wait_duration)));
|
||||
}
|
||||
|
||||
// If the current realtime timestep isn't a valid response to the current
|
||||
// challenge but the challenge hasn't expired yet, we're locking out
|
||||
// the factor to prevent challenge windows from overlapping. Let the user
|
||||
// know that they should wait for a new challenge.
|
||||
$challenge_timesteps = $this->getAllowedTimesteps($challenge_timestep);
|
||||
if (!isset($challenge_timesteps[$current_timestep])) {
|
||||
return $this->newResult()
|
||||
->setIsWait(true)
|
||||
->setErrorMessage(
|
||||
pht(
|
||||
'This factor recently issued a challenge which has expired. '.
|
||||
'A new challenge can not be issued yet. Wait %s second(s) for '.
|
||||
'the code to cycle, then try again.',
|
||||
new PhutilNumber($wait_duration)));
|
||||
}
|
||||
|
||||
if ($challenge->getIsReusedChallenge()) {
|
||||
return $this->newResult()
|
||||
->setIsWait(true)
|
||||
->setErrorMessage(
|
||||
pht(
|
||||
'You recently provided a response to this factor. Responses '.
|
||||
'may not be reused. Wait %s second(s) for the code to cycle, '.
|
||||
'then try again.',
|
||||
new PhutilNumber($wait_duration)));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function newResultFromChallengeResponse(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer,
|
||||
AphrontRequest $request,
|
||||
array $challenges) {
|
||||
|
||||
$code = $request->getStr($this->getParameterName($config, 'totpcode'));
|
||||
|
||||
$result = $this->newResult()
|
||||
->setValue($code);
|
||||
|
||||
// We expect to reach TOTP validation with exactly one valid challenge.
|
||||
if (count($challenges) !== 1) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Reached TOTP challenge validation with an unexpected number of '.
|
||||
'unexpired challenges (%d), expected exactly one.',
|
||||
phutil_count($challenges)));
|
||||
}
|
||||
|
||||
$challenge = head($challenges);
|
||||
|
||||
// If the client has already provided a valid answer to this challenge and
|
||||
// submitted a token proving they answered it, we're all set.
|
||||
if ($challenge->getIsAnsweredChallenge()) {
|
||||
return $result->setAnsweredChallenge($challenge);
|
||||
}
|
||||
|
||||
$challenge_timestep = (int)$challenge->getChallengeKey();
|
||||
$current_timestep = $this->getCurrentTimestep();
|
||||
|
||||
$challenge_timesteps = $this->getAllowedTimesteps($challenge_timestep);
|
||||
$current_timesteps = $this->getAllowedTimesteps($current_timestep);
|
||||
|
||||
// We require responses be both valid for the challenge and valid for the
|
||||
// current timestep. A longer challenge TTL doesn't let you use older
|
||||
// codes for a longer period of time.
|
||||
$valid_timestep = $this->getTimestepAtWhichResponseIsValid(
|
||||
array_intersect_key($challenge_timesteps, $current_timesteps),
|
||||
new PhutilOpaqueEnvelope($config->getFactorSecret()),
|
||||
(string)$code);
|
||||
|
||||
if ($valid_timestep) {
|
||||
$now = PhabricatorTime::getNow();
|
||||
$step_duration = $this->getTimestepDuration();
|
||||
$step_window = $this->getTimestepWindowSize();
|
||||
$ttl = $now + ($step_duration * $step_window);
|
||||
|
||||
$challenge
|
||||
->setProperty('totp.timestep', $valid_timestep)
|
||||
->markChallengeAsAnswered($ttl);
|
||||
|
||||
$result->setAnsweredChallenge($challenge);
|
||||
} else {
|
||||
if (strlen($code)) {
|
||||
$error_message = pht('Invalid');
|
||||
} else {
|
||||
$error_message = pht('Required');
|
||||
}
|
||||
$result->setErrorMessage($error_message);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function generateNewTOTPKey() {
|
||||
return strtoupper(Filesystem::readRandomCharacters(32));
|
||||
}
|
||||
|
||||
public static function verifyTOTPCode(
|
||||
PhabricatorUser $user,
|
||||
PhutilOpaqueEnvelope $key,
|
||||
$code) {
|
||||
|
||||
$now = (int)(time() / 30);
|
||||
|
||||
// Allow the user to enter a code a few minutes away on either side, in
|
||||
// case the server or client has some clock skew.
|
||||
for ($offset = -2; $offset <= 2; $offset++) {
|
||||
$real = self::getTOTPCode($key, $now + $offset);
|
||||
if (phutil_hashes_are_identical($real, $code)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: After validating a code, this should mark it as used and prevent
|
||||
// it from being reused.
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static function base32Decode($buf) {
|
||||
$buf = strtoupper($buf);
|
||||
|
||||
@@ -303,4 +456,43 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
||||
$rows);
|
||||
}
|
||||
|
||||
private function getTimestepDuration() {
|
||||
return 30;
|
||||
}
|
||||
|
||||
private function getCurrentTimestep() {
|
||||
$duration = $this->getTimestepDuration();
|
||||
return (int)(PhabricatorTime::getNow() / $duration);
|
||||
}
|
||||
|
||||
private function getAllowedTimesteps($at_timestep) {
|
||||
$window = $this->getTimestepWindowSize();
|
||||
$range = range($at_timestep - $window, $at_timestep + $window);
|
||||
return array_fuse($range);
|
||||
}
|
||||
|
||||
private function getTimestepWindowSize() {
|
||||
// The user is allowed to provide a code from the recent past or the
|
||||
// near future to account for minor clock skew between the client
|
||||
// and server, and the time it takes to actually enter a code.
|
||||
return 2;
|
||||
}
|
||||
|
||||
private function getTimestepAtWhichResponseIsValid(
|
||||
array $timesteps,
|
||||
PhutilOpaqueEnvelope $key,
|
||||
$code) {
|
||||
|
||||
foreach ($timesteps as $timestep) {
|
||||
$expect_code = self::getTOTPCode($key, $timestep);
|
||||
if (phutil_hashes_are_identical($code, $expect_code)) {
|
||||
return $timestep;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthChallengeGarbageCollector
|
||||
extends PhabricatorGarbageCollector {
|
||||
|
||||
const COLLECTORCONST = 'auth.challenges';
|
||||
|
||||
public function getCollectorName() {
|
||||
return pht('Authentication Challenges');
|
||||
}
|
||||
|
||||
public function hasAutomaticPolicy() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function collectGarbage() {
|
||||
$challenge_table = new PhabricatorAuthChallenge();
|
||||
$conn = $challenge_table->establishConnection('w');
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'DELETE FROM %R WHERE challengeTTL < UNIX_TIMESTAMP() LIMIT 100',
|
||||
$challenge_table);
|
||||
|
||||
return ($conn->getAffectedRows() == 100);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthChallengePHIDType extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'CHAL';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Auth Challenge');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new PhabricatorAuthChallenge();
|
||||
}
|
||||
|
||||
public function getPHIDTypeApplicationClass() {
|
||||
return 'PhabricatorAuthApplication';
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
return new PhabricatorAuthChallengeQuery();
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthSessionPHIDType
|
||||
extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'SSSN';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('Session');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new PhabricatorAuthSession();
|
||||
}
|
||||
|
||||
public function getPHIDTypeApplicationClass() {
|
||||
return 'PhabricatorAuthApplication';
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
return id(new PhabricatorAuthSessionQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthChallengeQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $userPHIDs;
|
||||
private $factorPHIDs;
|
||||
private $challengeTTLMin;
|
||||
private $challengeTTLMax;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withUserPHIDs(array $user_phids) {
|
||||
$this->userPHIDs = $user_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withFactorPHIDs(array $factor_phids) {
|
||||
$this->factorPHIDs = $factor_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withChallengeTTLBetween($challenge_min, $challenge_max) {
|
||||
$this->challengeTTLMin = $challenge_min;
|
||||
$this->challengeTTLMax = $challenge_max;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorAuthChallenge();
|
||||
}
|
||||
|
||||
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->userPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'userPHID IN (%Ls)',
|
||||
$this->userPHIDs);
|
||||
}
|
||||
|
||||
if ($this->factorPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'factorPHID IN (%Ls)',
|
||||
$this->factorPHIDs);
|
||||
}
|
||||
|
||||
if ($this->challengeTTLMin !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'challengeTTL >= %d',
|
||||
$this->challengeTTLMin);
|
||||
}
|
||||
|
||||
if ($this->challengeTTLMax !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'challengeTTL <= %d',
|
||||
$this->challengeTTLMax);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorAuthApplication';
|
||||
}
|
||||
|
||||
}
|
@@ -4,6 +4,7 @@ final class PhabricatorAuthSessionQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $identityPHIDs;
|
||||
private $sessionKeys;
|
||||
private $sessionTypes;
|
||||
@@ -28,19 +29,17 @@ final class PhabricatorAuthSessionQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function newResultObject() {
|
||||
return new PhabricatorAuthSession();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$table = new PhabricatorAuthSession();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
return $table->loadAllFromArray($data);
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $sessions) {
|
||||
@@ -65,8 +64,8 @@ final class PhabricatorAuthSessionQuery
|
||||
return $sessions;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||
$where = array();
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
@@ -75,6 +74,13 @@ final class PhabricatorAuthSessionQuery
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->identityPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
@@ -85,7 +91,8 @@ final class PhabricatorAuthSessionQuery
|
||||
if ($this->sessionKeys !== null) {
|
||||
$hashes = array();
|
||||
foreach ($this->sessionKeys as $session_key) {
|
||||
$hashes[] = PhabricatorHash::weakDigest($session_key);
|
||||
$hashes[] = PhabricatorAuthSession::newSessionDigest(
|
||||
new PhutilOpaqueEnvelope($session_key));
|
||||
}
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
@@ -100,9 +107,7 @@ final class PhabricatorAuthSessionQuery
|
||||
$this->sessionTypes);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn);
|
||||
|
||||
return $this->formatWhereClause($conn, $where);
|
||||
return $where;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
|
256
src/applications/auth/storage/PhabricatorAuthChallenge.php
Normal file
256
src/applications/auth/storage/PhabricatorAuthChallenge.php
Normal file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
final class PhabricatorAuthChallenge
|
||||
extends PhabricatorAuthDAO
|
||||
implements PhabricatorPolicyInterface {
|
||||
|
||||
protected $userPHID;
|
||||
protected $factorPHID;
|
||||
protected $sessionPHID;
|
||||
protected $workflowKey;
|
||||
protected $challengeKey;
|
||||
protected $challengeTTL;
|
||||
protected $responseDigest;
|
||||
protected $responseTTL;
|
||||
protected $isCompleted;
|
||||
protected $properties = array();
|
||||
|
||||
private $responseToken;
|
||||
|
||||
const HTTPKEY = '__hisec.challenges__';
|
||||
const TOKEN_DIGEST_KEY = 'auth.challenge.token';
|
||||
|
||||
public static function initializeNewChallenge() {
|
||||
return id(new self())
|
||||
->setIsCompleted(0);
|
||||
}
|
||||
|
||||
public static function newHTTPParametersFromChallenges(array $challenges) {
|
||||
assert_instances_of($challenges, __CLASS__);
|
||||
|
||||
$token_list = array();
|
||||
foreach ($challenges as $challenge) {
|
||||
$token = $challenge->getResponseToken();
|
||||
if ($token) {
|
||||
$token_list[] = sprintf(
|
||||
'%s:%s',
|
||||
$challenge->getPHID(),
|
||||
$token->openEnvelope());
|
||||
}
|
||||
}
|
||||
|
||||
if (!$token_list) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$token_list = implode(' ', $token_list);
|
||||
|
||||
return array(
|
||||
self::HTTPKEY => $token_list,
|
||||
);
|
||||
}
|
||||
|
||||
public static function newChallengeResponsesFromRequest(
|
||||
array $challenges,
|
||||
AphrontRequest $request) {
|
||||
assert_instances_of($challenges, __CLASS__);
|
||||
|
||||
$token_list = $request->getStr(self::HTTPKEY);
|
||||
$token_list = explode(' ', $token_list);
|
||||
|
||||
$token_map = array();
|
||||
foreach ($token_list as $token_element) {
|
||||
$token_element = trim($token_element, ' ');
|
||||
|
||||
if (!strlen($token_element)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: This error message is intentionally not printing the token to
|
||||
// avoid disclosing it. As a result, it isn't terribly useful, but no
|
||||
// normal user should ever end up here.
|
||||
if (!preg_match('/^[^:]+:/', $token_element)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This request included an improperly formatted MFA challenge '.
|
||||
'token and can not be processed.'));
|
||||
}
|
||||
|
||||
list($phid, $token) = explode(':', $token_element, 2);
|
||||
|
||||
if (isset($token_map[$phid])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This request improperly specifies an MFA challenge token ("%s") '.
|
||||
'multiple times and can not be processed.',
|
||||
$phid));
|
||||
}
|
||||
|
||||
$token_map[$phid] = new PhutilOpaqueEnvelope($token);
|
||||
}
|
||||
|
||||
$challenges = mpull($challenges, null, 'getPHID');
|
||||
|
||||
$now = PhabricatorTime::getNow();
|
||||
foreach ($challenges as $challenge_phid => $challenge) {
|
||||
// If the response window has expired, don't attach the token.
|
||||
if ($challenge->getResponseTTL() < $now) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$token = idx($token_map, $challenge_phid);
|
||||
if (!$token) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$challenge->setResponseToken($token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'properties' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'challengeKey' => 'text255',
|
||||
'challengeTTL' => 'epoch',
|
||||
'workflowKey' => 'text255',
|
||||
'responseDigest' => 'text255?',
|
||||
'responseTTL' => 'epoch?',
|
||||
'isCompleted' => 'bool',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_issued' => array(
|
||||
'columns' => array('userPHID', 'challengeTTL'),
|
||||
),
|
||||
'key_collection' => array(
|
||||
'columns' => array('challengeTTL'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getPHIDType() {
|
||||
return PhabricatorAuthChallengePHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getIsReusedChallenge() {
|
||||
if ($this->getIsCompleted()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->getIsAnsweredChallenge()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the challenge has been answered but the client has provided a token
|
||||
// proving that they answered it, this is still a valid response.
|
||||
if ($this->getResponseToken()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getIsAnsweredChallenge() {
|
||||
return (bool)$this->getResponseDigest();
|
||||
}
|
||||
|
||||
public function markChallengeAsAnswered($ttl) {
|
||||
$token = Filesystem::readRandomCharacters(32);
|
||||
$token = new PhutilOpaqueEnvelope($token);
|
||||
|
||||
return $this
|
||||
->setResponseToken($token)
|
||||
->setResponseTTL($ttl)
|
||||
->save();
|
||||
}
|
||||
|
||||
public function markChallengeAsCompleted() {
|
||||
return $this
|
||||
->setIsCompleted(true)
|
||||
->save();
|
||||
}
|
||||
|
||||
public function setResponseToken(PhutilOpaqueEnvelope $token) {
|
||||
if (!$this->getUserPHID()) {
|
||||
throw new PhutilInvalidStateException('setUserPHID');
|
||||
}
|
||||
|
||||
if ($this->responseToken) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'This challenge already has a response token; you can not '.
|
||||
'set a new response token.'));
|
||||
}
|
||||
|
||||
if (preg_match('/ /', $token->openEnvelope())) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'The response token for this challenge is invalid: response '.
|
||||
'tokens may not include spaces.'));
|
||||
}
|
||||
|
||||
$digest = PhabricatorHash::digestWithNamedKey(
|
||||
$token->openEnvelope(),
|
||||
self::TOKEN_DIGEST_KEY);
|
||||
|
||||
if ($this->responseDigest !== null) {
|
||||
if (!phutil_hashes_are_identical($digest, $this->responseDigest)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Invalid response token for this challenge: token digest does '.
|
||||
'not match stored digest.'));
|
||||
}
|
||||
} else {
|
||||
$this->responseDigest = $digest;
|
||||
}
|
||||
|
||||
$this->responseToken = $token;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResponseToken() {
|
||||
return $this->responseToken;
|
||||
}
|
||||
|
||||
public function setResponseDigest($value) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'You can not set the response digest for a challenge directly. '.
|
||||
'Instead, set a response token. A response digest will be computed '.
|
||||
'automatically.'));
|
||||
}
|
||||
|
||||
public function setProperty($key, $value) {
|
||||
$this->properties[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProperty($key, $default = null) {
|
||||
return $this->properties[$key];
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
return PhabricatorPolicies::POLICY_NOONE;
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return ($viewer->getPHID() === $this->getUserPHID());
|
||||
}
|
||||
|
||||
}
|
@@ -8,6 +8,8 @@ final class PhabricatorAuthFactorConfig extends PhabricatorAuthDAO {
|
||||
protected $factorSecret;
|
||||
protected $properties = array();
|
||||
|
||||
private $sessionEngine;
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
@@ -49,4 +51,17 @@ final class PhabricatorAuthFactorConfig extends PhabricatorAuthDAO {
|
||||
return $impl;
|
||||
}
|
||||
|
||||
public function setSessionEngine(PhabricatorAuthSessionEngine $engine) {
|
||||
$this->sessionEngine = $engine;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSessionEngine() {
|
||||
if (!$this->sessionEngine) {
|
||||
throw new PhutilInvalidStateException('setSessionEngine');
|
||||
}
|
||||
|
||||
return $this->sessionEngine;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -223,20 +223,8 @@ final class PhabricatorAuthPassword
|
||||
return new PhabricatorAuthPasswordEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorAuthPasswordTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -91,21 +91,10 @@ final class PhabricatorAuthProviderConfig
|
||||
return new PhabricatorAuthProviderConfigEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorAuthProviderConfigTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -159,18 +159,8 @@ final class PhabricatorAuthSSHKey
|
||||
return new PhabricatorAuthSSHKeyEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorAuthSSHKeyTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -6,6 +6,8 @@ final class PhabricatorAuthSession extends PhabricatorAuthDAO
|
||||
const TYPE_WEB = 'web';
|
||||
const TYPE_CONDUIT = 'conduit';
|
||||
|
||||
const SESSION_DIGEST_KEY = 'session.digest';
|
||||
|
||||
protected $userPHID;
|
||||
protected $type;
|
||||
protected $sessionKey;
|
||||
@@ -17,12 +19,19 @@ final class PhabricatorAuthSession extends PhabricatorAuthDAO
|
||||
|
||||
private $identityObject = self::ATTACHABLE;
|
||||
|
||||
public static function newSessionDigest(PhutilOpaqueEnvelope $session_token) {
|
||||
return PhabricatorHash::digestWithNamedKey(
|
||||
$session_token->openEnvelope(),
|
||||
self::SESSION_DIGEST_KEY);
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_TIMESTAMPS => false,
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'type' => 'text32',
|
||||
'sessionKey' => 'bytes40',
|
||||
'sessionKey' => 'text64',
|
||||
'sessionStart' => 'epoch',
|
||||
'sessionExpires' => 'epoch',
|
||||
'highSecurityUntil' => 'epoch?',
|
||||
@@ -74,6 +83,10 @@ final class PhabricatorAuthSession extends PhabricatorAuthDAO
|
||||
}
|
||||
}
|
||||
|
||||
public function getPHIDType() {
|
||||
return PhabricatorAuthSessionPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function isHighSecuritySession() {
|
||||
$until = $this->getHighSecurityUntil();
|
||||
|
||||
|
@@ -51,6 +51,7 @@ final class PhabricatorBadgesCommentController
|
||||
|
||||
if ($request->isAjax() && $is_preview) {
|
||||
return id(new PhabricatorApplicationTransactionResponse())
|
||||
->setObject($badge)
|
||||
->setViewer($viewer)
|
||||
->setTransactions($xactions)
|
||||
->setIsPreview($is_preview);
|
||||
|
@@ -125,21 +125,10 @@ final class PhabricatorBadgesBadge extends PhabricatorBadgesDAO
|
||||
return new PhabricatorBadgesEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorBadgesTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||
|
||||
|
@@ -649,18 +649,8 @@ abstract class PhabricatorApplication
|
||||
return new PhabricatorApplicationEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorApplicationApplicationTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
}
|
||||
|
@@ -482,14 +482,14 @@ abstract class PhabricatorController extends AphrontController {
|
||||
PhabricatorApplicationTransactionInterface $object,
|
||||
PhabricatorApplicationTransactionQuery $query,
|
||||
PhabricatorMarkupEngine $engine = null,
|
||||
$render_data = array()) {
|
||||
$view_data = array()) {
|
||||
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
$request = $this->getRequest();
|
||||
$viewer = $this->getViewer();
|
||||
$xaction = $object->getApplicationTransactionTemplate();
|
||||
$view = $xaction->getApplicationTransactionViewObject();
|
||||
|
||||
$pager = id(new AphrontCursorPagerView())
|
||||
->readFromRequest($this->getRequest())
|
||||
->readFromRequest($request)
|
||||
->setURI(new PhutilURI(
|
||||
'/transactions/showolder/'.$object->getPHID().'/'));
|
||||
|
||||
@@ -500,6 +500,13 @@ abstract class PhabricatorController extends AphrontController {
|
||||
->executeWithCursorPager($pager);
|
||||
$xactions = array_reverse($xactions);
|
||||
|
||||
$timeline_engine = PhabricatorTimelineEngine::newForObject($object)
|
||||
->setViewer($viewer)
|
||||
->setTransactions($xactions)
|
||||
->setViewData($view_data);
|
||||
|
||||
$view = $timeline_engine->buildTimelineView();
|
||||
|
||||
if ($engine) {
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->getComment()) {
|
||||
@@ -513,14 +520,9 @@ abstract class PhabricatorController extends AphrontController {
|
||||
}
|
||||
|
||||
$timeline = $view
|
||||
->setUser($viewer)
|
||||
->setObjectPHID($object->getPHID())
|
||||
->setTransactions($xactions)
|
||||
->setPager($pager)
|
||||
->setRenderData($render_data)
|
||||
->setQuoteTargetID($this->getRequest()->getStr('quoteTargetID'))
|
||||
->setQuoteRef($this->getRequest()->getStr('quoteRef'));
|
||||
$object->willRenderTimeline($timeline, $this->getRequest());
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
@@ -1311,20 +1311,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||
return new PhabricatorCalendarEventEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorCalendarEventTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||
|
||||
|
@@ -166,21 +166,10 @@ final class PhabricatorCalendarExport extends PhabricatorCalendarDAO
|
||||
return new PhabricatorCalendarExportEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorCalendarExportTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
@@ -140,20 +140,10 @@ final class PhabricatorCalendarImport
|
||||
return new PhabricatorCalendarImportEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorCalendarImportTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
public function newLogMessage($type, array $parameters) {
|
||||
$parameters = array(
|
||||
'type' => $type,
|
||||
|
@@ -16,18 +16,14 @@ final class PhabricatorConfigHistoryController
|
||||
|
||||
$xaction = $object->getApplicationTransactionTemplate();
|
||||
|
||||
$view = $xaction->getApplicationTransactionViewObject();
|
||||
|
||||
$timeline = $view
|
||||
->setUser($viewer)
|
||||
$timeline = id(new PhabricatorApplicationTransactionView())
|
||||
->setViewer($viewer)
|
||||
->setTransactions($xactions)
|
||||
->setRenderAsFeed(true)
|
||||
->setObjectPHID(PhabricatorPHIDConstants::PHID_VOID);
|
||||
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$object->willRenderTimeline($timeline, $this->getRequest());
|
||||
|
||||
$title = pht('Settings History');
|
||||
$header = $this->buildHeaderView($title);
|
||||
|
||||
|
@@ -61,21 +61,10 @@ final class PhabricatorConfigEntry
|
||||
return new PhabricatorConfigEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorConfigTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -311,19 +311,10 @@ final class ConpherenceThread extends ConpherenceDAO
|
||||
return new ConpherenceEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new ConpherenceTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
/* -( PhabricatorNgramInterface )------------------------------------------ */
|
||||
|
||||
|
@@ -95,20 +95,10 @@ final class PhabricatorCountdown extends PhabricatorCountdownDAO
|
||||
return new PhabricatorCountdownEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorCountdownTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
|
||||
|
||||
|
@@ -130,21 +130,10 @@ final class PhabricatorDashboard extends PhabricatorDashboardDAO
|
||||
return new PhabricatorDashboardTransactionEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorDashboardTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -113,21 +113,10 @@ final class PhabricatorDashboardPanel
|
||||
return new PhabricatorDashboardPanelTransactionEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorDashboardPanelTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -279,36 +279,17 @@ final class DifferentialRevisionEditEngine
|
||||
$object);
|
||||
$inlines = msort($inlines, 'getID');
|
||||
|
||||
foreach ($inlines as $inline) {
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType(DifferentialTransaction::TYPE_INLINE)
|
||||
->attachComment($inline);
|
||||
}
|
||||
$editor = $object->getApplicationTransactionEditor()
|
||||
->setActor($viewer);
|
||||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
$viewer_is_author = ($object->getAuthorPHID() == $viewer_phid);
|
||||
if ($viewer_is_author) {
|
||||
$state_map = PhabricatorTransactions::getInlineStateMap();
|
||||
$query_template = id(new DifferentialDiffInlineCommentQuery())
|
||||
->withRevisionPHIDs(array($object->getPHID()));
|
||||
|
||||
$inlines = id(new DifferentialDiffInlineCommentQuery())
|
||||
->setViewer($viewer)
|
||||
->withRevisionPHIDs(array($object->getPHID()))
|
||||
->withFixedStates(array_keys($state_map))
|
||||
->execute();
|
||||
if ($inlines) {
|
||||
$old_value = mpull($inlines, 'getFixedState', 'getPHID');
|
||||
$new_value = array();
|
||||
foreach ($old_value as $key => $state) {
|
||||
$new_value[$key] = $state_map[$state];
|
||||
}
|
||||
|
||||
$xactions[] = id(new DifferentialTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE)
|
||||
->setIgnoreOnNoEffect(true)
|
||||
->setOldValue($old_value)
|
||||
->setNewValue($new_value);
|
||||
}
|
||||
}
|
||||
$xactions = $editor->newAutomaticInlineTransactions(
|
||||
$object,
|
||||
$inlines,
|
||||
DifferentialTransaction::TYPE_INLINE,
|
||||
$query_template);
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
@@ -247,50 +247,16 @@ final class DifferentialTransactionEditor
|
||||
case DifferentialTransaction::TYPE_INLINE:
|
||||
$this->didExpandInlineState = true;
|
||||
|
||||
$actor_phid = $this->getActingAsPHID();
|
||||
$author_phid = $object->getAuthorPHID();
|
||||
$actor_is_author = ($actor_phid == $author_phid);
|
||||
$query_template = id(new DifferentialDiffInlineCommentQuery())
|
||||
->withRevisionPHIDs(array($object->getPHID()));
|
||||
|
||||
$state_map = PhabricatorTransactions::getInlineStateMap();
|
||||
$state_xaction = $this->newInlineStateTransaction(
|
||||
$object,
|
||||
$query_template);
|
||||
|
||||
$query = id(new DifferentialDiffInlineCommentQuery())
|
||||
->setViewer($this->getActor())
|
||||
->withRevisionPHIDs(array($object->getPHID()))
|
||||
->withFixedStates(array_keys($state_map));
|
||||
|
||||
$inlines = array();
|
||||
|
||||
// We're going to undraft any "done" marks on your own inlines.
|
||||
$inlines[] = id(clone $query)
|
||||
->withAuthorPHIDs(array($actor_phid))
|
||||
->withHasTransaction(false)
|
||||
->execute();
|
||||
|
||||
// If you're the author, we also undraft any "done" marks on other
|
||||
// inlines.
|
||||
if ($actor_is_author) {
|
||||
$inlines[] = id(clone $query)
|
||||
->withHasTransaction(true)
|
||||
->execute();
|
||||
if ($state_xaction) {
|
||||
$results[] = $state_xaction;
|
||||
}
|
||||
|
||||
$inlines = array_mergev($inlines);
|
||||
|
||||
if (!$inlines) {
|
||||
break;
|
||||
}
|
||||
|
||||
$old_value = mpull($inlines, 'getFixedState', 'getPHID');
|
||||
$new_value = array();
|
||||
foreach ($old_value as $key => $state) {
|
||||
$new_value[$key] = $state_map[$state];
|
||||
}
|
||||
|
||||
$results[] = id(new DifferentialTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE)
|
||||
->setIgnoreOnNoEffect(true)
|
||||
->setOldValue($old_value)
|
||||
->setNewValue($new_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1367,9 +1333,9 @@ final class DifferentialTransactionEditor
|
||||
foreach (array_chunk($sql, 256) as $chunk) {
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %Q',
|
||||
'INSERT INTO %T (repositoryID, pathID, epoch, revisionID) VALUES %LQ',
|
||||
$table->getTableName(),
|
||||
implode(', ', $chunk));
|
||||
$chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1444,9 +1410,9 @@ final class DifferentialTransactionEditor
|
||||
if ($sql) {
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'INSERT INTO %T (revisionID, type, hash) VALUES %Q',
|
||||
'INSERT INTO %T (revisionID, type, hash) VALUES %LQ',
|
||||
ArcanistDifferentialRevisionHash::TABLE_NAME,
|
||||
implode(', ', $sql));
|
||||
$sql);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
final class DifferentialRevisionTimelineEngine
|
||||
extends PhabricatorTimelineEngine {
|
||||
|
||||
protected function newTimelineView() {
|
||||
$viewer = $this->getViewer();
|
||||
$xactions = $this->getTransactions();
|
||||
$revision = $this->getObject();
|
||||
|
||||
$view_data = $this->getViewData();
|
||||
if (!$view_data) {
|
||||
$view_data = array();
|
||||
}
|
||||
|
||||
$left = idx($view_data, 'left');
|
||||
$right = idx($view_data, 'right');
|
||||
|
||||
$diffs = id(new DifferentialDiffQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($left, $right))
|
||||
->execute();
|
||||
$diffs = mpull($diffs, null, 'getID');
|
||||
$left_diff = $diffs[$left];
|
||||
$right_diff = $diffs[$right];
|
||||
|
||||
$old_ids = idx($view_data, 'old');
|
||||
$new_ids = idx($view_data, 'new');
|
||||
$old_ids = array_filter(explode(',', $old_ids));
|
||||
$new_ids = array_filter(explode(',', $new_ids));
|
||||
|
||||
$type_inline = DifferentialTransaction::TYPE_INLINE;
|
||||
$changeset_ids = array_merge($old_ids, $new_ids);
|
||||
$inlines = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->getTransactionType() == $type_inline) {
|
||||
$inlines[] = $xaction->getComment();
|
||||
$changeset_ids[] = $xaction->getComment()->getChangesetID();
|
||||
}
|
||||
}
|
||||
|
||||
if ($changeset_ids) {
|
||||
$changesets = id(new DifferentialChangesetQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs($changeset_ids)
|
||||
->execute();
|
||||
$changesets = mpull($changesets, null, 'getID');
|
||||
} else {
|
||||
$changesets = array();
|
||||
}
|
||||
|
||||
foreach ($inlines as $key => $inline) {
|
||||
$inlines[$key] = DifferentialInlineComment::newFromModernComment(
|
||||
$inline);
|
||||
}
|
||||
|
||||
$query = id(new DifferentialInlineCommentQuery())
|
||||
->needHidden(true)
|
||||
->setViewer($viewer);
|
||||
|
||||
// NOTE: This is a bit sketchy: this method adjusts the inlines as a
|
||||
// side effect, which means it will ultimately adjust the transaction
|
||||
// comments and affect timeline rendering.
|
||||
$query->adjustInlinesForChangesets(
|
||||
$inlines,
|
||||
array_select_keys($changesets, $old_ids),
|
||||
array_select_keys($changesets, $new_ids),
|
||||
$revision);
|
||||
|
||||
return id(new DifferentialTransactionView())
|
||||
->setViewData($view_data)
|
||||
->setChangesets($changesets)
|
||||
->setRevision($revision)
|
||||
->setLeftDiff($left_diff)
|
||||
->setRightDiff($right_diff);
|
||||
}
|
||||
|
||||
}
|
@@ -699,21 +699,10 @@ final class DifferentialDiff
|
||||
return new DifferentialDiffEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new DifferentialDiffTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
@@ -11,6 +11,7 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
PhabricatorSubscribableInterface,
|
||||
PhabricatorCustomFieldInterface,
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorTimelineInterface,
|
||||
PhabricatorMentionableInterface,
|
||||
PhabricatorDestructibleInterface,
|
||||
PhabricatorProjectInterface,
|
||||
@@ -990,81 +991,10 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
return new DifferentialTransactionEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new DifferentialTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$render_data = $timeline->getRenderData();
|
||||
$left = $request->getInt('left', idx($render_data, 'left'));
|
||||
$right = $request->getInt('right', idx($render_data, 'right'));
|
||||
|
||||
$diffs = id(new DifferentialDiffQuery())
|
||||
->setViewer($request->getUser())
|
||||
->withIDs(array($left, $right))
|
||||
->execute();
|
||||
$diffs = mpull($diffs, null, 'getID');
|
||||
$left_diff = $diffs[$left];
|
||||
$right_diff = $diffs[$right];
|
||||
|
||||
$old_ids = $request->getStr('old', idx($render_data, 'old'));
|
||||
$new_ids = $request->getStr('new', idx($render_data, 'new'));
|
||||
$old_ids = array_filter(explode(',', $old_ids));
|
||||
$new_ids = array_filter(explode(',', $new_ids));
|
||||
|
||||
$type_inline = DifferentialTransaction::TYPE_INLINE;
|
||||
$changeset_ids = array_merge($old_ids, $new_ids);
|
||||
$inlines = array();
|
||||
foreach ($timeline->getTransactions() as $xaction) {
|
||||
if ($xaction->getTransactionType() == $type_inline) {
|
||||
$inlines[] = $xaction->getComment();
|
||||
$changeset_ids[] = $xaction->getComment()->getChangesetID();
|
||||
}
|
||||
}
|
||||
|
||||
if ($changeset_ids) {
|
||||
$changesets = id(new DifferentialChangesetQuery())
|
||||
->setViewer($request->getUser())
|
||||
->withIDs($changeset_ids)
|
||||
->execute();
|
||||
$changesets = mpull($changesets, null, 'getID');
|
||||
} else {
|
||||
$changesets = array();
|
||||
}
|
||||
|
||||
foreach ($inlines as $key => $inline) {
|
||||
$inlines[$key] = DifferentialInlineComment::newFromModernComment(
|
||||
$inline);
|
||||
}
|
||||
|
||||
$query = id(new DifferentialInlineCommentQuery())
|
||||
->needHidden(true)
|
||||
->setViewer($viewer);
|
||||
|
||||
// NOTE: This is a bit sketchy: this method adjusts the inlines as a
|
||||
// side effect, which means it will ultimately adjust the transaction
|
||||
// comments and affect timeline rendering.
|
||||
$query->adjustInlinesForChangesets(
|
||||
$inlines,
|
||||
array_select_keys($changesets, $old_ids),
|
||||
array_select_keys($changesets, $new_ids),
|
||||
$this);
|
||||
|
||||
return $timeline
|
||||
->setChangesets($changesets)
|
||||
->setRevision($this)
|
||||
->setLeftDiff($left_diff)
|
||||
->setRightDiff($right_diff);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
@@ -1206,4 +1136,13 @@ final class DifferentialRevision extends DifferentialDAO
|
||||
return new DifferentialRevisionDraftEngine();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorTimelineInterface )--------------------------------------- */
|
||||
|
||||
|
||||
public function newTimelineEngine() {
|
||||
return new DifferentialRevisionTimelineEngine();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -65,10 +65,6 @@ final class DifferentialTransaction
|
||||
return new DifferentialTransactionComment();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionViewObject() {
|
||||
return new DifferentialTransactionView();
|
||||
}
|
||||
|
||||
public function shouldHide() {
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
@@ -45,6 +45,7 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
->withIdentifiers(array($commit_identifier))
|
||||
->needCommitData(true)
|
||||
->needAuditRequests(true)
|
||||
->needAuditAuthority(array($viewer))
|
||||
->setLimit(100)
|
||||
->needIdentities(true)
|
||||
->execute();
|
||||
@@ -111,7 +112,6 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
}
|
||||
|
||||
$audit_requests = $commit->getAudits();
|
||||
$commit->loadAndAttachAuditAuthority($viewer);
|
||||
|
||||
$commit_data = $commit->getCommitData();
|
||||
$is_foreign = $commit_data->getCommitDetail('foreign-svn-stub');
|
||||
@@ -740,8 +740,6 @@ final class DiffusionCommitController extends DiffusionController {
|
||||
$commit,
|
||||
new PhabricatorAuditTransactionQuery());
|
||||
|
||||
$commit->willRenderTimeline($timeline, $this->getRequest());
|
||||
|
||||
$timeline->setQuoteRef($commit->getMonogram());
|
||||
|
||||
return $timeline;
|
||||
|
@@ -43,9 +43,12 @@ final class DiffusionCommitEditEngine
|
||||
}
|
||||
|
||||
protected function newObjectQuery() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
return id(new DiffusionCommitQuery())
|
||||
->needCommitData(true)
|
||||
->needAuditRequests(true);
|
||||
->needAuditRequests(true)
|
||||
->needAuditAuthority(array($viewer));
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
@@ -167,36 +170,17 @@ final class DiffusionCommitEditEngine
|
||||
$raw = true);
|
||||
$inlines = msort($inlines, 'getID');
|
||||
|
||||
foreach ($inlines as $inline) {
|
||||
$xactions[] = $object->getApplicationTransactionTemplate()
|
||||
->setTransactionType(PhabricatorAuditActionConstants::INLINE)
|
||||
->attachComment($inline);
|
||||
}
|
||||
$editor = $object->getApplicationTransactionEditor()
|
||||
->setActor($viewer);
|
||||
|
||||
$viewer_phid = $viewer->getPHID();
|
||||
$viewer_is_author = ($object->getAuthorPHID() == $viewer_phid);
|
||||
if ($viewer_is_author) {
|
||||
$state_map = PhabricatorTransactions::getInlineStateMap();
|
||||
$query_template = id(new DiffusionDiffInlineCommentQuery())
|
||||
->withCommitPHIDs(array($object->getPHID()));
|
||||
|
||||
$inlines = id(new DiffusionDiffInlineCommentQuery())
|
||||
->setViewer($viewer)
|
||||
->withCommitPHIDs(array($object->getPHID()))
|
||||
->withFixedStates(array_keys($state_map))
|
||||
->execute();
|
||||
if ($inlines) {
|
||||
$old_value = mpull($inlines, 'getFixedState', 'getPHID');
|
||||
$new_value = array();
|
||||
foreach ($old_value as $key => $state) {
|
||||
$new_value[$key] = $state_map[$state];
|
||||
}
|
||||
|
||||
$xactions[] = $object->getApplicationTransactionTemplate()
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_INLINESTATE)
|
||||
->setIgnoreOnNoEffect(true)
|
||||
->setOldValue($old_value)
|
||||
->setNewValue($new_value);
|
||||
}
|
||||
}
|
||||
$xactions = $editor->newAutomaticInlineTransactions(
|
||||
$object,
|
||||
$inlines,
|
||||
PhabricatorAuditActionConstants::INLINE,
|
||||
$query_template);
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
@@ -212,11 +212,8 @@ final class DiffusionRepositoryEditEngine
|
||||
->setObject($object)
|
||||
->execute();
|
||||
|
||||
$track_value = $object->getDetail('branch-filter', array());
|
||||
$track_value = array_keys($track_value);
|
||||
|
||||
$autoclose_value = $object->getDetail('close-commits-filter', array());
|
||||
$autoclose_value = array_keys($autoclose_value);
|
||||
$track_value = $object->getTrackOnlyRules();
|
||||
$autoclose_value = $object->getAutocloseOnlyRules();
|
||||
|
||||
$automation_instructions = pht(
|
||||
"Configure **Repository Automation** to allow Phabricator to ".
|
||||
|
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
final class DiffusionCommitTimelineEngine
|
||||
extends PhabricatorTimelineEngine {
|
||||
|
||||
protected function newTimelineView() {
|
||||
$xactions = $this->getTransactions();
|
||||
|
||||
$path_ids = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
if ($xaction->hasComment()) {
|
||||
$path_id = $xaction->getComment()->getPathID();
|
||||
if ($path_id) {
|
||||
$path_ids[] = $path_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$path_map = array();
|
||||
if ($path_ids) {
|
||||
$path_map = id(new DiffusionPathQuery())
|
||||
->withPathIDs($path_ids)
|
||||
->execute();
|
||||
$path_map = ipull($path_map, 'path', 'id');
|
||||
}
|
||||
|
||||
return id(new PhabricatorAuditTransactionView())
|
||||
->setPathMap($path_map);
|
||||
}
|
||||
}
|
@@ -23,8 +23,8 @@ final class DiffusionRepositoryBranchesManagementPanel
|
||||
|
||||
$has_any =
|
||||
$repository->getDetail('default-branch') ||
|
||||
$repository->getDetail('branch-filter') ||
|
||||
$repository->getDetail('close-commits-filter');
|
||||
$repository->getTrackOnlyRules() ||
|
||||
$repository->getAutocloseOnlyRules();
|
||||
|
||||
if ($has_any) {
|
||||
return 'fa-code-fork';
|
||||
@@ -74,17 +74,21 @@ final class DiffusionRepositoryBranchesManagementPanel
|
||||
->setViewer($viewer);
|
||||
|
||||
$default_branch = nonempty(
|
||||
$repository->getHumanReadableDetail('default-branch'),
|
||||
$repository->getDetail('default-branch'),
|
||||
phutil_tag('em', array(), $repository->getDefaultBranch()));
|
||||
$view->addProperty(pht('Default Branch'), $default_branch);
|
||||
|
||||
$track_only_rules = $repository->getTrackOnlyRules();
|
||||
$track_only_rules = implode(', ', $track_only_rules);
|
||||
$track_only = nonempty(
|
||||
$repository->getHumanReadableDetail('branch-filter', array()),
|
||||
$track_only_rules,
|
||||
phutil_tag('em', array(), pht('Track All Branches')));
|
||||
$view->addProperty(pht('Track Only'), $track_only);
|
||||
|
||||
$autoclose_rules = $repository->getAutocloseOnlyRules();
|
||||
$autoclose_rules = implode(', ', $autoclose_rules);
|
||||
$autoclose_only = nonempty(
|
||||
$repository->getHumanReadableDetail('close-commits-filter', array()),
|
||||
$autoclose_rules,
|
||||
phutil_tag('em', array(), pht('Autoclose On All Branches')));
|
||||
|
||||
$autoclose_disabled = false;
|
||||
|
@@ -68,7 +68,7 @@ final class DiffusionRepositorySubversionManagementPanel
|
||||
->setViewer($viewer);
|
||||
|
||||
$default_branch = nonempty(
|
||||
$repository->getHumanReadableDetail('svn-subpath'),
|
||||
$repository->getDetail('svn-subpath'),
|
||||
phutil_tag('em', array(), pht('Import Entire Repository')));
|
||||
$view->addProperty(pht('Import Only'), $default_branch);
|
||||
|
||||
|
@@ -17,6 +17,7 @@ final class DiffusionCommitQuery
|
||||
private $unreachable;
|
||||
|
||||
private $needAuditRequests;
|
||||
private $needAuditAuthority;
|
||||
private $auditIDs;
|
||||
private $auditorPHIDs;
|
||||
private $epochMin;
|
||||
@@ -121,6 +122,12 @@ final class DiffusionCommitQuery
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function needAuditAuthority(array $users) {
|
||||
assert_instances_of($users, 'PhabricatorUser');
|
||||
$this->needAuditAuthority = $users;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withAuditIDs(array $ids) {
|
||||
$this->auditIDs = $ids;
|
||||
return $this;
|
||||
@@ -231,14 +238,27 @@ final class DiffusionCommitQuery
|
||||
}
|
||||
|
||||
if (count($subqueries) > 1) {
|
||||
foreach ($subqueries as $key => $subquery) {
|
||||
$subqueries[$key] = '('.$subquery.')';
|
||||
$unions = null;
|
||||
foreach ($subqueries as $subquery) {
|
||||
if (!$unions) {
|
||||
$unions = qsprintf(
|
||||
$conn,
|
||||
'(%Q)',
|
||||
$subquery);
|
||||
continue;
|
||||
}
|
||||
|
||||
$unions = qsprintf(
|
||||
$conn,
|
||||
'%Q UNION DISTINCT (%Q)',
|
||||
$unions,
|
||||
$subquery);
|
||||
}
|
||||
|
||||
$query = qsprintf(
|
||||
$conn,
|
||||
'%Q %Q %Q',
|
||||
implode(' UNION DISTINCT ', $subqueries),
|
||||
$unions,
|
||||
$this->buildOrderClause($conn, true),
|
||||
$this->buildLimitClause($conn));
|
||||
} else {
|
||||
@@ -423,6 +443,72 @@ final class DiffusionCommitQuery
|
||||
$commits);
|
||||
}
|
||||
|
||||
if ($this->needAuditAuthority) {
|
||||
$authority_users = $this->needAuditAuthority;
|
||||
|
||||
// NOTE: This isn't very efficient since we're running two queries per
|
||||
// user, but there's currently no way to figure out authority for
|
||||
// multiple users in one query. Today, we only ever request authority for
|
||||
// a single user and single commit, so this has no practical impact.
|
||||
|
||||
// NOTE: We're querying with the viewership of query viewer, not the
|
||||
// actual users. If the viewer can't see a project or package, they
|
||||
// won't be able to see who has authority on it. This is safer than
|
||||
// showing them true authority, and should never matter today, but it
|
||||
// also doesn't seem like a significant disclosure and might be
|
||||
// reasonable to adjust later if it causes something weird or confusing
|
||||
// to happen.
|
||||
|
||||
$authority_map = array();
|
||||
foreach ($authority_users as $authority_user) {
|
||||
$authority_phid = $authority_user->getPHID();
|
||||
if (!$authority_phid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result_phids = array();
|
||||
|
||||
// Users have authority over themselves.
|
||||
$result_phids[] = $authority_phid;
|
||||
|
||||
// Users have authority over packages they own.
|
||||
$owned_packages = id(new PhabricatorOwnersPackageQuery())
|
||||
->setViewer($viewer)
|
||||
->withAuthorityPHIDs(array($authority_phid))
|
||||
->execute();
|
||||
foreach ($owned_packages as $package) {
|
||||
$result_phids[] = $package->getPHID();
|
||||
}
|
||||
|
||||
// Users have authority over projects they're members of.
|
||||
$projects = id(new PhabricatorProjectQuery())
|
||||
->setViewer($viewer)
|
||||
->withMemberPHIDs(array($authority_phid))
|
||||
->execute();
|
||||
foreach ($projects as $project) {
|
||||
$result_phids[] = $project->getPHID();
|
||||
}
|
||||
|
||||
$result_phids = array_fuse($result_phids);
|
||||
|
||||
foreach ($commits as $commit) {
|
||||
$attach_phids = $result_phids;
|
||||
|
||||
// NOTE: When modifying your own commits, you act only on behalf of
|
||||
// yourself, not your packages or projects. The idea here is that you
|
||||
// can't accept your own commits. In the future, this might change or
|
||||
// depend on configuration.
|
||||
$author_phid = $commit->getAuthorPHID();
|
||||
if ($author_phid == $authority_phid) {
|
||||
$attach_phids = array($author_phid);
|
||||
$attach_phids = array_fuse($attach_phids);
|
||||
}
|
||||
|
||||
$commit->attachAuditAuthority($authority_user, $attach_phids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $commits;
|
||||
}
|
||||
|
||||
|
@@ -43,12 +43,13 @@ final class DiffusionPathChangeQuery extends Phobject {
|
||||
|
||||
$conn_r = $repository->establishConnection('r');
|
||||
|
||||
$limit = '';
|
||||
if ($this->limit) {
|
||||
$limit = qsprintf(
|
||||
$conn_r,
|
||||
'LIMIT %d',
|
||||
$this->limit + 1);
|
||||
} else {
|
||||
$limit = qsprintf($conn_r, '');
|
||||
}
|
||||
|
||||
$raw_changes = queryfx_all(
|
||||
|
@@ -143,21 +143,10 @@ final class DivinerLiveBook extends DivinerDAO
|
||||
return new DivinerLiveBookEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new DivinerLiveBookTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
/* -( PhabricatorFulltextInterface )--------------------------------------- */
|
||||
|
||||
|
||||
|
@@ -34,6 +34,7 @@ final class DrydockConsoleController extends DrydockController {
|
||||
->setHeader(pht('Blueprints'))
|
||||
->setImageIcon('fa-map-o')
|
||||
->setHref($this->getApplicationURI('blueprint/'))
|
||||
->setClickable(true)
|
||||
->addAttribute(
|
||||
pht(
|
||||
'Configure blueprints so Drydock can build resources, like '.
|
||||
@@ -44,6 +45,7 @@ final class DrydockConsoleController extends DrydockController {
|
||||
->setHeader(pht('Resources'))
|
||||
->setImageIcon('fa-map')
|
||||
->setHref($this->getApplicationURI('resource/'))
|
||||
->setClickable(true)
|
||||
->addAttribute(
|
||||
pht('View and manage resources Drydock has built, like hosts.')));
|
||||
|
||||
@@ -52,6 +54,7 @@ final class DrydockConsoleController extends DrydockController {
|
||||
->setHeader(pht('Leases'))
|
||||
->setImageIcon('fa-link')
|
||||
->setHref($this->getApplicationURI('lease/'))
|
||||
->setClickable(true)
|
||||
->addAttribute(pht('Manage leases on resources.')));
|
||||
|
||||
$menu->addItem(
|
||||
@@ -59,6 +62,7 @@ final class DrydockConsoleController extends DrydockController {
|
||||
->setHeader(pht('Repository Operations'))
|
||||
->setImageIcon('fa-fighter-jet')
|
||||
->setHref($this->getApplicationURI('operation/'))
|
||||
->setClickable(true)
|
||||
->addAttribute(pht('Review the repository operation queue.')));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
|
@@ -295,21 +295,10 @@ final class DrydockBlueprint extends DrydockDAO
|
||||
return new DrydockBlueprintEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new DrydockBlueprintTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -12,11 +12,8 @@ final class PhabricatorFactUpdateIterator extends PhutilBufferedIterator {
|
||||
private $position;
|
||||
private $ignoreUpdatesDuration = 15;
|
||||
|
||||
private $set;
|
||||
|
||||
public function __construct(LiskDAO $object) {
|
||||
$this->set = new LiskDAOSet();
|
||||
$this->object = $object->putInSet($this->set);
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
public function setPosition($position) {
|
||||
@@ -41,8 +38,6 @@ final class PhabricatorFactUpdateIterator extends PhutilBufferedIterator {
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
$this->set->clearSet();
|
||||
|
||||
if ($this->object->hasProperty('dateModified')) {
|
||||
if ($this->cursor) {
|
||||
list($after_epoch, $after_id) = explode(':', $this->cursor);
|
||||
|
@@ -34,7 +34,15 @@ final class PhabricatorFeedQuery
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $data) {
|
||||
return PhabricatorFeedStory::loadAllFromRows($data, $this->getViewer());
|
||||
$stories = PhabricatorFeedStory::loadAllFromRows($data, $this->getViewer());
|
||||
|
||||
foreach ($stories as $key => $story) {
|
||||
if (!$story->isVisibleInFeed()) {
|
||||
unset($stories[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $stories;
|
||||
}
|
||||
|
||||
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||
|
@@ -418,6 +418,14 @@ abstract class PhabricatorFeedStory
|
||||
return array();
|
||||
}
|
||||
|
||||
public function isVisibleInFeed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isVisibleInNotifications() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
|
||||
|
||||
|
@@ -1544,21 +1544,10 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||
return new PhabricatorFileEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorFileTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
|
||||
|
||||
|
@@ -110,19 +110,8 @@ final class FundBacker extends FundDAO
|
||||
return new FundBackerEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new FundBackerTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -160,21 +160,10 @@ final class FundInitiative extends FundDAO
|
||||
return new FundInitiativeEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new FundInitiativeTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||
|
||||
|
@@ -96,9 +96,7 @@ abstract class HarbormasterBuildableEngine
|
||||
$publishable = $this->getPublishableObject();
|
||||
$editor = $this->newEditor();
|
||||
|
||||
$editor->applyTransactions(
|
||||
$publishable->getApplicationTransactionObject(),
|
||||
$xactions);
|
||||
$editor->applyTransactions($publishable, $xactions);
|
||||
}
|
||||
|
||||
public function getAuthorIdentity() {
|
||||
|
@@ -283,21 +283,10 @@ final class HarbormasterBuildable
|
||||
return new HarbormasterBuildableTransactionEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new HarbormasterBuildableTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -393,21 +393,10 @@ final class HarbormasterBuild extends HarbormasterDAO
|
||||
return new HarbormasterBuildTransactionEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new HarbormasterBuildTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -136,22 +136,10 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
|
||||
return new HarbormasterBuildPlanEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new HarbormasterBuildPlanTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
|
@@ -121,21 +121,10 @@ final class HarbormasterBuildStep extends HarbormasterDAO
|
||||
return new HarbormasterBuildStepEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new HarbormasterBuildStepTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -243,9 +243,9 @@ final class HeraldEngine extends Phobject {
|
||||
}
|
||||
queryfx(
|
||||
$conn_w,
|
||||
'INSERT IGNORE INTO %T (phid, ruleID) VALUES %Q',
|
||||
'INSERT IGNORE INTO %T (phid, ruleID) VALUES %LQ',
|
||||
HeraldRule::TABLE_RULE_APPLIED,
|
||||
implode(', ', $sql));
|
||||
$sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -318,21 +318,10 @@ final class HeraldRule extends HeraldDAO
|
||||
return new HeraldRuleEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new HeraldRuleTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@@ -200,20 +200,10 @@ final class HeraldWebhook
|
||||
return new HeraldWebhookEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new HeraldWebhookTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
@@ -151,16 +151,6 @@ final class LegalpadDocumentSignController extends LegalpadController {
|
||||
$errors = array();
|
||||
$hisec_token = null;
|
||||
if ($request->isFormOrHisecPost() && !$has_signed) {
|
||||
|
||||
// Require two-factor auth to sign legal documents.
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$hisec_token = id(new PhabricatorAuthSessionEngine())
|
||||
->requireHighSecurityToken(
|
||||
$viewer,
|
||||
$request,
|
||||
$document->getURI());
|
||||
}
|
||||
|
||||
list($form_data, $errors, $field_errors) = $this->readSignatureForm(
|
||||
$document,
|
||||
$request);
|
||||
@@ -187,6 +177,20 @@ final class LegalpadDocumentSignController extends LegalpadController {
|
||||
$signature->setVerified($verified);
|
||||
|
||||
if (!$errors) {
|
||||
// Require MFA to sign legal documents.
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$workflow_key = sprintf(
|
||||
'legalpad.sign(%s)',
|
||||
$document->getPHID());
|
||||
|
||||
$hisec_token = id(new PhabricatorAuthSessionEngine())
|
||||
->setWorkflowKey($workflow_key)
|
||||
->requireHighSecurityToken(
|
||||
$viewer,
|
||||
$request,
|
||||
$document->getURI());
|
||||
}
|
||||
|
||||
$signature->save();
|
||||
|
||||
// If the viewer is logged in, signing for themselves, send them to
|
||||
|
@@ -209,21 +209,10 @@ final class LegalpadDocument extends LegalpadDAO
|
||||
return new LegalpadDocumentEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new LegalpadTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
@@ -14,10 +14,6 @@ final class LegalpadTransaction extends PhabricatorModularTransaction {
|
||||
return new LegalpadTransactionComment();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionViewObject() {
|
||||
return new LegalpadTransactionView();
|
||||
}
|
||||
|
||||
public function getBaseTransactionClass() {
|
||||
return 'LegalpadDocumentTransactionType';
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user