@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implementation of VCS passwords against user.

Summary: This allows users to set their HTTP access passwords via Diffusion interface.

Test Plan: Clicked the "Set HTTP Access Password" link, set a password and saw it appear in the DB.

Reviewers: #blessed_reviewers, hach-que, btrahan

Reviewed By: hach-que

CC: Korvin, epriestley, aran, jamesr

Maniphest Tasks: T2230

Differential Revision: https://secure.phabricator.com/D7462

+295
+8
resources/sql/patches/20131031.vcspassword.sql
··· 1 + CREATE TABLE {$NAMESPACE}_repository.repository_vcspassword ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + userPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 4 + passwordHash VARCHAR(50) NOT NULL COLLATE utf8_bin, 5 + dateCreated INT UNSIGNED NOT NULL, 6 + dateModified INT UNSIGNED NOT NULL, 7 + UNIQUE KEY `key_phid` (userPHID) 8 + ) ENGINE=InnoDB, CHARSET utf8;
+4
src/__phutil_library_map__.php
··· 535 535 'DiffusionSSHGitUploadPackWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php', 536 536 'DiffusionSSHGitWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitWorkflow.php', 537 537 'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php', 538 + 'DiffusionSetPasswordPanel' => 'applications/diffusion/panel/DiffusionSetPasswordPanel.php', 538 539 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', 539 540 'DiffusionStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionStableCommitNameQuery.php', 540 541 'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php', ··· 1674 1675 'PhabricatorRepositoryTransaction' => 'applications/repository/storage/PhabricatorRepositoryTransaction.php', 1675 1676 'PhabricatorRepositoryTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryTransactionQuery.php', 1676 1677 'PhabricatorRepositoryType' => 'applications/repository/constants/PhabricatorRepositoryType.php', 1678 + 'PhabricatorRepositoryVCSPassword' => 'applications/repository/storage/PhabricatorRepositoryVCSPassword.php', 1677 1679 'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php', 1678 1680 'PhabricatorSQLPatchList' => 'infrastructure/storage/patch/PhabricatorSQLPatchList.php', 1679 1681 'PhabricatorSSHWorkflow' => 'infrastructure/ssh/PhabricatorSSHWorkflow.php', ··· 2729 2731 'DiffusionSSHGitUploadPackWorkflow' => 'DiffusionSSHGitWorkflow', 2730 2732 'DiffusionSSHGitWorkflow' => 'DiffusionSSHWorkflow', 2731 2733 'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow', 2734 + 'DiffusionSetPasswordPanel' => 'PhabricatorSettingsPanel', 2732 2735 'DiffusionSetupException' => 'AphrontUsageException', 2733 2736 'DiffusionStableCommitNameQuery' => 'DiffusionQuery', 2734 2737 'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery', ··· 4015 4018 'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase', 4016 4019 'PhabricatorRepositoryTransaction' => 'PhabricatorApplicationTransaction', 4017 4020 'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 4021 + 'PhabricatorRepositoryVCSPassword' => 'PhabricatorRepositoryDAO', 4018 4022 'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine', 4019 4023 'PhabricatorSSHWorkflow' => 'PhutilArgumentWorkflow', 4020 4024 'PhabricatorSavedQuery' =>
+18
src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php
··· 84 84 'Regular expression to link external bug tracker. See '. 85 85 'http://tortoisesvn.net/docs/release/TortoiseSVN_en/'. 86 86 'tsvn-dug-bugtracker.html for further explanation.')), 87 + $this->newOption('diffusion.allow-http-auth', 'bool', false) 88 + ->setBoolOptions( 89 + array( 90 + pht('Allow HTTP Basic Auth'), 91 + pht('Disable HTTP Basic Auth'), 92 + )) 93 + ->setSummary(pht('Enable HTTP Basic Auth for repositories.')) 94 + ->setDescription( 95 + pht( 96 + "Phabricator can serve repositories over HTTP, using HTTP basic ". 97 + "auth.\n\n". 98 + "Because HTTP basic auth is less secure than SSH auth, it is ". 99 + "disabled by default. You can enable it here if you'd like to use ". 100 + "it anyway. There's nothing fundamentally insecure about it as ". 101 + "long as Phabricator uses HTTPS, but it presents a much lower ". 102 + "barrier to attackers than SSH does.\n\n". 103 + "Consider using SSH for authenticated access to repositories ". 104 + "instead of HTTP.")) 87 105 ); 88 106 } 89 107
+209
src/applications/diffusion/panel/DiffusionSetPasswordPanel.php
··· 1 + <?php 2 + 3 + final class DiffusionSetPasswordPanel extends PhabricatorSettingsPanel { 4 + 5 + public function getPanelKey() { 6 + return 'vcspassword'; 7 + } 8 + 9 + public function getPanelName() { 10 + return pht('VCS Password'); 11 + } 12 + 13 + public function getPanelGroup() { 14 + return pht('Authentication'); 15 + } 16 + 17 + public function isEnabled() { 18 + return PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth'); 19 + } 20 + 21 + public function processRequest(AphrontRequest $request) { 22 + $user = $request->getUser(); 23 + 24 + $vcspassword = id(new PhabricatorRepositoryVCSPassword()) 25 + ->loadOneWhere( 26 + 'userPHID = %s', 27 + $user->getPHID()); 28 + if (!$vcspassword) { 29 + $vcspassword = id(new PhabricatorRepositoryVCSPassword()); 30 + $vcspassword->setUserPHID($user->getPHID()); 31 + } 32 + 33 + $panel_uri = $this->getPanelURI('?saved=true'); 34 + 35 + $errors = array(); 36 + 37 + $e_password = true; 38 + $e_confirm = true; 39 + 40 + if ($request->isFormPost()) { 41 + if ($request->getBool('remove')) { 42 + if ($vcspassword->getID()) { 43 + $vcspassword->delete(); 44 + return id(new AphrontRedirectResponse())->setURI($panel_uri); 45 + } 46 + } 47 + 48 + $new_password = $request->getStr('password'); 49 + $confirm = $request->getStr('confirm'); 50 + if (!strlen($new_password)) { 51 + $e_password = pht('Required'); 52 + $errors[] = pht('Password is required.'); 53 + } else { 54 + $e_password = null; 55 + } 56 + 57 + if (!strlen($confirm)) { 58 + $e_confirm = pht('Required'); 59 + $errors[] = pht('You must confirm the new password.'); 60 + } else { 61 + $e_confirm = null; 62 + } 63 + 64 + if (!$errors) { 65 + $envelope = new PhutilOpaqueEnvelope($new_password); 66 + 67 + if ($new_password !== $confirm) { 68 + $e_password = pht('Does Not Match'); 69 + $e_confirm = pht('Does Not Match'); 70 + $errors[] = pht('Password and confirmation do not match.'); 71 + } else if ($user->comparePassword($envelope)) { 72 + $e_password = pht('Not Unique'); 73 + $e_confirm = pht('Not Unique'); 74 + $errors[] = pht( 75 + 'This password is not unique. You must use a unique password.'); 76 + } 77 + 78 + if (!$errors) { 79 + $vcspassword->setPassword($envelope, $user); 80 + $vcspassword->save(); 81 + 82 + return id(new AphrontRedirectResponse())->setURI($panel_uri); 83 + } 84 + } 85 + } 86 + 87 + $title = pht('Set VCS Password'); 88 + 89 + $error_view = null; 90 + if ($errors) { 91 + $error_view = id(new AphrontErrorView()) 92 + ->setTitle(pht('Form Errors')) 93 + ->setErrors($errors); 94 + } 95 + 96 + $form = id(new AphrontFormView()) 97 + ->setUser($user) 98 + ->appendRemarkupInstructions( 99 + pht( 100 + 'To access repositories hosted by Phabricator over HTTP, you must '. 101 + 'set a version control password. This password should be unique.'. 102 + "\n\n". 103 + "This password applies to all repositories available over ". 104 + "HTTP.")); 105 + 106 + if ($vcspassword->getID()) { 107 + $form 108 + ->appendChild( 109 + id(new AphrontFormPasswordControl()) 110 + ->setLabel(pht('Current Password')) 111 + ->setDisabled(true) 112 + ->setValue('********************')); 113 + } else { 114 + $form 115 + ->appendChild( 116 + id(new AphrontFormMarkupControl()) 117 + ->setLabel(pht('Current Password')) 118 + ->setValue(phutil_tag('em', array(), pht('No Password Set')))); 119 + } 120 + 121 + $form 122 + ->appendChild( 123 + id(new AphrontFormPasswordControl()) 124 + ->setName('password') 125 + ->setLabel(pht('New VCS Password')) 126 + ->setError($e_password)) 127 + ->appendChild( 128 + id(new AphrontFormPasswordControl()) 129 + ->setName('confirm') 130 + ->setLabel(pht('Confirm VCS Password')) 131 + ->setError($e_confirm)) 132 + ->appendChild( 133 + id(new AphrontFormSubmitControl()) 134 + ->setValue(pht('Change Password'))); 135 + 136 + 137 + if (!$vcspassword->getID()) { 138 + $is_serious = PhabricatorEnv::getEnvConfig( 139 + 'phabricator.serious-business'); 140 + 141 + $suggest = Filesystem::readRandomBytes(128); 142 + $suggest = preg_replace('([^A-Za-z0-9/!().,;{}^&*%~])', '', $suggest); 143 + $suggest = substr($suggest, 0, 20); 144 + 145 + if ($is_serious) { 146 + $form->appendRemarkupInstructions( 147 + pht( 148 + 'Having trouble coming up with a good password? Try this randomly '. 149 + 'generated one, made by a computer:'. 150 + "\n\n". 151 + "`%s`", 152 + $suggest)); 153 + } else { 154 + $form->appendRemarkupInstructions( 155 + pht( 156 + 'Having trouble coming up with a good password? Try this '. 157 + 'artisinal password, hand made in small batches by our expert '. 158 + 'craftspeople: '. 159 + "\n\n". 160 + "`%s`", 161 + $suggest)); 162 + } 163 + } 164 + 165 + $object_box = id(new PHUIObjectBoxView()) 166 + ->setHeaderText($title) 167 + ->setForm($form) 168 + ->setFormError($error_view); 169 + 170 + $remove_form = id(new AphrontFormView()) 171 + ->setUser($user); 172 + 173 + if ($vcspassword->getID()) { 174 + $remove_form 175 + ->addHiddenInput('remove', true) 176 + ->appendRemarkupInstructions( 177 + pht( 178 + 'You can remove your VCS password, which will prevent your '. 179 + 'account from accessing repositories.')) 180 + ->appendChild( 181 + id(new AphrontFormSubmitControl()) 182 + ->setValue(pht('Remove Password'))); 183 + } else { 184 + $remove_form->appendRemarkupInstructions( 185 + pht( 186 + 'You do not currently have a VCS password set. If you set one, you '. 187 + 'can remove it here later.')); 188 + } 189 + 190 + $remove_box = id(new PHUIObjectBoxView()) 191 + ->setHeaderText(pht('Remove VCS Password')) 192 + ->setForm($remove_form); 193 + 194 + $saved = null; 195 + if ($request->getBool('saved')) { 196 + $saved = id(new AphrontErrorView()) 197 + ->setSeverity(AphrontErrorView::SEVERITY_NOTICE) 198 + ->setTitle(pht('Password Updated')) 199 + ->appendChild(pht('Your VCS password has been updated.')); 200 + } 201 + 202 + return array( 203 + $saved, 204 + $object_box, 205 + $remove_box, 206 + ); 207 + } 208 + 209 + }
+34
src/applications/repository/storage/PhabricatorRepositoryVCSPassword.php
··· 1 + <?php 2 + 3 + final class PhabricatorRepositoryVCSPassword extends PhabricatorRepositoryDAO { 4 + 5 + protected $id; 6 + protected $userPHID; 7 + protected $passwordHash; 8 + 9 + public function setPassword( 10 + PhutilOpaqueEnvelope $password, 11 + PhabricatorUser $user) { 12 + return $this->setPasswordHash($this->hashPassword($password, $user)); 13 + } 14 + 15 + public function comparePassword( 16 + PhutilOpaqueEnvelope $password, 17 + PhabricatorUser $user) { 18 + 19 + $hash = $this->hashPassword($password, $user); 20 + return ($hash == $this->getPasswordHash()); 21 + } 22 + 23 + private function hashPassword( 24 + PhutilOpaqueEnvelope $password, 25 + PhabricatorUser $user) { 26 + 27 + if ($user->getPHID() != $this->getUserPHID()) { 28 + throw new Exception("User does not match password user PHID!"); 29 + } 30 + 31 + return PhabricatorHash::digestPassword($password, $user->getPHID()); 32 + } 33 + 34 + }
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1716 1716 'type' => 'sql', 1717 1717 'name' => $this->getPatchPath('20131030.repostatusmessage.sql'), 1718 1718 ), 1719 + '20131031.vcspassword.sql' => array( 1720 + 'type' => 'sql', 1721 + 'name' => $this->getPatchPath('20131031.vcspassword.sql'), 1722 + ), 1719 1723 ); 1720 1724 } 1721 1725 }
+18
src/infrastructure/util/PhabricatorHash.php
··· 24 24 25 25 26 26 /** 27 + * Digest a string into a password hash. This is similar to @{method:digest}, 28 + * but requires a salt and iterates the hash to increase cost. 29 + */ 30 + public static function digestPassword(PhutilOpaqueEnvelope $envelope, $salt) { 31 + $result = $envelope->openEnvelope(); 32 + if (!$result) { 33 + throw new Exception("Trying to digest empty password!"); 34 + } 35 + 36 + for ($ii = 0; $ii < 1000; $ii++) { 37 + $result = PhabricatorHash::digest($result, $salt); 38 + } 39 + 40 + return $result; 41 + } 42 + 43 + 44 + /** 27 45 * Digest a string for use in, e.g., a MySQL index. This produces a short 28 46 * (12-byte), case-sensitive alphanumeric string with 72 bits of entropy, 29 47 * which is generally safe in most contexts (notably, URLs).