@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.

Migrate repositories to use Passphrase for credential management

Summary: Fixes T4122. Ref T2230. Instead of storing credentials on each repository, store them in Passphrase. This allows easy creation/management of many repositories which share credentials.

Test Plan:
- Upgraded repositories.
- Created and edited repositories.
- Pulled HTTP and SSH repositories.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2230, T4122

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

+294 -229
+2
resources/sql/patches/20131121.repocredentials.1.col.sql
··· 1 + ALTER TABLE {$NAMESPACE}_repository.repository 2 + ADD credentialPHID VARCHAR(64) COLLATE utf8_bin;
+104
resources/sql/patches/20131121.repocredentials.2.mig.php
··· 1 + <?php 2 + 3 + $table = new PhabricatorRepository(); 4 + $conn_w = $table->establishConnection('w'); 5 + $viewer = PhabricatorUser::getOmnipotentUser(); 6 + 7 + $map = array(); 8 + foreach (new LiskMigrationIterator($table) as $repository) { 9 + $callsign = $repository->getCallsign(); 10 + echo "Examining repository {$callsign}...\n"; 11 + 12 + if ($repository->getCredentialPHID()) { 13 + echo "...already has a Credential.\n"; 14 + continue; 15 + } 16 + 17 + $uri = $repository->getRemoteURI(); 18 + if (!$uri) { 19 + echo "...no remote URI.\n"; 20 + continue; 21 + } 22 + 23 + $uri = new PhutilURI($uri); 24 + 25 + $proto = strtolower($uri->getProtocol()); 26 + if ($proto == 'http' || $proto == 'https' || $proto == 'svn') { 27 + $username = $repository->getDetail('http-login'); 28 + $secret = $repository->getDetail('http-pass'); 29 + $type = PassphraseCredentialTypePassword::CREDENTIAL_TYPE; 30 + } else { 31 + $username = $repository->getDetail('ssh-login'); 32 + $file = $repository->getDetail('ssh-keyfile'); 33 + if ($file) { 34 + $secret = $file; 35 + $type = PassphraseCredentialTypeSSHPrivateKeyFile::CREDENTIAL_TYPE; 36 + } else { 37 + $secret = $repository->getDetail('ssh-key'); 38 + $type = PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE; 39 + } 40 + } 41 + 42 + if (!$username || !$secret) { 43 + echo "...no credentials set.\n"; 44 + continue; 45 + } 46 + 47 + $map[$type][$username][$secret][] = $repository; 48 + echo "...will migrate.\n"; 49 + } 50 + 51 + $passphrase = new PassphraseSecret(); 52 + $passphrase->openTransaction(); 53 + $table->openTransaction(); 54 + 55 + foreach ($map as $credential_type => $credential_usernames) { 56 + $type = PassphraseCredentialType::getTypeByConstant($credential_type); 57 + foreach ($credential_usernames as $username => $credential_secrets) { 58 + foreach ($credential_secrets as $secret_plaintext => $repositories) { 59 + $callsigns = mpull($repositories, 'getCallsign'); 60 + $name = pht( 61 + 'Migrated Repository Credential (%s)', 62 + implode(', ', $callsigns)); 63 + 64 + echo "Creating: {$name}...\n"; 65 + 66 + $secret = id(new PassphraseSecret()) 67 + ->setSecretData($secret_plaintext) 68 + ->save(); 69 + 70 + $secret_id = $secret->getID(); 71 + 72 + $credential = PassphraseCredential::initializeNewCredential($viewer) 73 + ->setCredentialType($type->getCredentialType()) 74 + ->setProvidesType($type->getProvidesType()) 75 + ->setViewPolicy(PhabricatorPolicies::POLICY_ADMIN) 76 + ->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN) 77 + ->setName($name) 78 + ->setUsername($username) 79 + ->setSecretID($secret_id) 80 + ->save(); 81 + 82 + foreach ($repositories as $repository) { 83 + queryfx( 84 + $conn_w, 85 + 'UPDATE %T SET credentialPHID = %s WHERE id = %d', 86 + $table->getTableName(), 87 + $credential->getPHID(), 88 + $repository->getID()); 89 + 90 + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_USES_CREDENTIAL; 91 + 92 + id(new PhabricatorEdgeEditor()) 93 + ->setActor($viewer) 94 + ->addEdge($repository->getPHID(), $edge_type, $credential->getPHID()) 95 + ->save(); 96 + } 97 + } 98 + } 99 + } 100 + 101 + $table->saveTransaction(); 102 + $passphrase->saveTransaction(); 103 + 104 + echo "Done.\n";
+10 -24
scripts/ssh/ssh-connect.php
··· 12 12 throw new Exception(pht("No 'PHABRICATOR_SSH_TARGET' in environment!")); 13 13 } 14 14 15 + $viewer = PhabricatorUser::getOmnipotentUser(); 16 + 15 17 $repository = id(new PhabricatorRepositoryQuery()) 16 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 18 + ->setViewer($viewer) 17 19 ->withCallsigns(array($target_name)) 18 20 ->executeOne(); 19 21 if (!$repository) { ··· 28 30 $pattern[] = '-o'; 29 31 $pattern[] = 'StrictHostKeyChecking=no'; 30 32 31 - $login = $repository->getSSHLogin(); 32 - if (strlen($login)) { 33 - $pattern[] = '-l'; 34 - $pattern[] = '%P'; 35 - $arguments[] = new PhutilOpaqueEnvelope($login); 36 - } 37 - 38 - $ssh_identity = null; 39 - 40 - $key = $repository->getDetail('ssh-key'); 41 - $keyfile = $repository->getDetail('ssh-keyfile'); 42 - if ($keyfile) { 43 - $ssh_identity = $keyfile; 44 - } else if ($key) { 45 - $tmpfile = new TempFile('phabricator-repository-ssh-key'); 46 - chmod($tmpfile, 0600); 47 - Filesystem::writeFile($tmpfile, $key); 48 - $ssh_identity = (string)$tmpfile; 49 - } 33 + $credential_phid = $repository->getCredentialPHID(); 34 + if ($credential_phid) { 35 + $key = PassphraseSSHKey::loadFromPHID($credential_phid, $viewer); 50 36 51 - if ($ssh_identity) { 52 - $pattern[] = '-i'; 53 - $pattern[] = '%P'; 54 - $arguments[] = new PhutilOpaqueEnvelope($keyfile); 37 + $pattern[] = '-l %P'; 38 + $arguments[] = $key->getUsernameEnvelope(); 39 + $pattern[] = '-i %P'; 40 + $arguments[] = $key->getKeyfileEnvelope(); 55 41 } 56 42 57 43 $pattern[] = '--';
+73 -123
src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
··· 94 94 $type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE; 95 95 $type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH; 96 96 $type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI; 97 - $type_ssh_login = PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN; 98 - $type_ssh_key = PhabricatorRepositoryTransaction::TYPE_SSH_KEY; 99 - $type_ssh_keyfile = PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE; 100 - $type_http_login = PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN; 101 - $type_http_pass = PhabricatorRepositoryTransaction::TYPE_HTTP_PASS; 102 97 $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING; 98 + $type_credential = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL; 103 99 104 100 $xactions = array(); 105 101 ··· 159 155 ->getValue()); 160 156 161 157 $xactions[] = id(clone $template) 162 - ->setTransactionType($type_ssh_login) 163 - ->setNewValue( 164 - $form->getPage('auth')->getControl('ssh-login')->getValue()); 165 - 166 - $xactions[] = id(clone $template) 167 - ->setTransactionType($type_ssh_key) 168 - ->setNewValue( 169 - $form->getPage('auth')->getControl('ssh-key')->getValue()); 170 - 171 - $xactions[] = id(clone $template) 172 - ->setTransactionType($type_ssh_keyfile) 173 - ->setNewValue( 174 - $form->getPage('auth')->getControl('ssh-keyfile')->getValue()); 175 - 176 - $xactions[] = id(clone $template) 177 - ->setTransactionType($type_http_login) 158 + ->setTransactionType($type_credential) 178 159 ->setNewValue( 179 - $form->getPage('auth')->getControl('http-login')->getValue()); 180 - 181 - $xactions[] = id(clone $template) 182 - ->setTransactionType($type_http_pass) 183 - ->setNewValue( 184 - $form->getPage('auth')->getControl('http-pass')->getValue()); 160 + $form->getPage('auth')->getControl('credential')->getValue()); 185 161 } 186 162 187 163 id(new PhabricatorRepositoryEditor()) ··· 198 174 if ($repository) { 199 175 $dict = array( 200 176 'remoteURI' => $repository->getRemoteURI(), 201 - 'ssh-login' => $repository->getDetail('ssh-login'), 202 - 'ssh-key' => $repository->getDetail('ssh-key'), 203 - 'ssh-keyfile' => $repository->getDetail('ssh-keyfile'), 204 - 'http-login' => $repository->getDetail('http-login'), 205 - 'http-pass' => $repository->getDetail('http-pass'), 177 + 'credential' => $repository->getCredentialPHID(), 206 178 ); 207 179 } 208 180 $form->readFromObject($dict); ··· 550 522 ->setUser($this->getRequest()->getUser()) 551 523 ->setAdjustFormPageCallback(array($this, 'adjustAuthPage')) 552 524 ->addControl( 553 - id(new AphrontFormTextControl()) 554 - ->setName('ssh-login') 555 - ->setLabel('SSH User')) 556 - ->addControl( 557 - id(new AphrontFormTextAreaControl()) 558 - ->setName('ssh-key') 559 - ->setLabel('SSH Private Key') 560 - ->setHeight(AphrontFormTextAreaControl::HEIGHT_SHORT) 561 - ->setCaption( 562 - hsprintf('Specify the entire private key, <em>or</em>...'))) 563 - ->addControl( 564 - id(new AphrontFormTextControl()) 565 - ->setName('ssh-keyfile') 566 - ->setLabel('SSH Private Key Path') 567 - ->setCaption( 568 - '...specify a path on disk where the daemon should '. 569 - 'look for a private key.')) 570 - ->addControl( 571 - id(new AphrontFormTextControl()) 572 - ->setName('http-login') 573 - ->setLabel('Username')) 574 - ->addControl( 575 - id(new AphrontFormPasswordControl()) 576 - ->setName('http-pass') 577 - ->setLabel('Password')); 525 + id(new PassphraseCredentialControl()) 526 + ->setName('credential')); 578 527 } 579 528 580 529 581 530 public function adjustAuthPage($page) { 582 531 $form = $page->getForm(); 583 532 584 - $remote_uri = $form->getPage('remote-uri') 585 - ->getControl('remoteURI') 586 - ->getValue(); 587 - 588 533 if ($this->getRepository()) { 589 534 $vcs = $this->getRepository()->getVersionControlSystem(); 590 535 } else { 591 536 $vcs = $form->getPage('vcs')->getControl('vcs')->getValue(); 592 537 } 593 538 539 + $remote_uri = $form->getPage('remote-uri') 540 + ->getControl('remoteURI') 541 + ->getValue(); 594 542 $proto = $this->getRemoteURIProtocol($remote_uri); 595 543 $remote_user = $this->getRemoteURIUser($remote_uri); 596 544 597 - $page->getControl('ssh-login')->setHidden(true); 598 - $page->getControl('ssh-key')->setHidden(true); 599 - $page->getControl('ssh-keyfile')->setHidden(true); 600 - $page->getControl('http-login')->setHidden(true); 601 - $page->getControl('http-pass')->setHidden(true); 545 + $c_credential = $page->getControl('credential'); 546 + $c_credential->setDefaultUsername($remote_user); 602 547 603 548 if ($this->isSSHProtocol($proto)) { 604 - $page->getControl('ssh-login')->setHidden(false); 605 - $page->getControl('ssh-key')->setHidden(false); 606 - $page->getControl('ssh-keyfile')->setHidden(false); 607 - 608 - $c_login = $page->getControl('ssh-login'); 609 - if (!strlen($c_login->getValue())) { 610 - $c_login->setValue($remote_user); 611 - } 549 + $c_credential->setLabel(pht('SSH Key')); 550 + $c_credential->setCredentialType( 551 + PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE); 552 + $provides_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE; 612 553 613 554 $page->addRemarkupInstructions( 614 555 pht( 615 - 'Enter the username and private key to use to connect to the '. 616 - 'the repository hosted at:'. 556 + 'Choose or add the SSH credentials to use to connect to the the '. 557 + 'repository hosted at:'. 617 558 "\n\n". 618 559 " lang=text\n". 619 - " %s". 620 - "\n\n". 621 - 'You can either copy/paste the entire private key, or put it '. 622 - 'somewhere on disk and provide the path to it.', 560 + " %s", 623 561 $remote_uri), 624 - 'ssh-login'); 625 - 562 + 'credential'); 626 563 } else if ($this->isUsernamePasswordProtocol($proto)) { 627 - $page->getControl('http-login')->setHidden(false); 628 - $page->getControl('http-pass')->setHidden(false); 564 + $c_credential->setLabel(pht('Password')); 565 + $c_credential->setAllowNull(true); 566 + $c_credential->setCredentialType( 567 + PassphraseCredentialTypePassword::CREDENTIAL_TYPE); 568 + $provides_type = PassphraseCredentialTypePassword::PROVIDES_TYPE; 629 569 630 570 $page->addRemarkupInstructions( 631 571 pht( 632 - 'Enter the a username and pasword used to connect to the '. 572 + 'Choose the a username and pasword used to connect to the '. 633 573 'repository hosted at:'. 634 574 "\n\n". 635 575 " lang=text\n". 636 576 " %s". 637 577 "\n\n". 638 578 "If this repository does not require a username or password, ". 639 - "you can leave these fields blank.", 579 + "you can continue to the next step.", 640 580 $remote_uri), 641 - 'http-login'); 581 + 'credential'); 642 582 } else if ($proto == 'file') { 583 + $c_credential->setHidden(true); 643 584 $page->addRemarkupInstructions( 644 585 pht( 645 - 'You do not need to configure any authentication options for '. 646 - 'repositories accessed over the `file://` protocol. Continue '. 647 - 'to the next step.'), 648 - 'ssh-login'); 586 + 'You do not need to configure any credentials for repositories '. 587 + 'accessed over the `file://` protocol. Continue to the next step.'), 588 + 'credential'); 649 589 } else { 650 590 throw new Exception("Unknown URI protocol!"); 651 591 } 592 + 593 + if ($provides_type) { 594 + $viewer = $this->getRequest()->getUser(); 595 + 596 + $options = id(new PassphraseCredentialQuery()) 597 + ->setViewer($viewer) 598 + ->withIsDestroyed(false) 599 + ->withProvidesTypes(array($provides_type)) 600 + ->execute(); 601 + 602 + $c_credential->setOptions($options); 603 + } 604 + 652 605 } 653 606 654 607 public function validateAuthPage(PHUIFormPageView $page) { ··· 656 609 $remote_uri = $form->getPage('remote')->getControl('remoteURI')->getValue(); 657 610 $proto = $this->getRemoteURIProtocol($remote_uri); 658 611 659 - if ($this->isSSHProtocol($proto)) { 660 - $c_user = $page->getControl('ssh-login'); 661 - $c_key = $page->getControl('ssh-key'); 662 - $c_file = $page->getControl('ssh-keyfile'); 612 + $c_credential = $page->getControl('credential'); 613 + $v_credential = $c_credential->getValue(); 663 614 664 - $v_user = $c_user->getValue(); 665 - $v_key = $c_key->getValue(); 666 - $v_file = $c_file->getValue(); 615 + // NOTE: We're using the omnipotent user here because the viewer might be 616 + // editing a repository they're allowed to edit which uses a credential they 617 + // are not allowed to see. This is fine, as long as they don't change it. 618 + $credential = id(new PassphraseCredentialQuery()) 619 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 620 + ->withPHIDs(array($v_credential)) 621 + ->executeOne(); 667 622 668 - if (!strlen($v_user)) { 669 - $c_user->setError(pht('Required')); 623 + if ($this->isSSHProtocol($proto)) { 624 + if (!$credential) { 625 + $c_credential->setError(pht('Required')); 670 626 $page->addPageError( 671 - pht('You must provide an SSH login username to connect over SSH.')); 627 + pht('You must choose an SSH credential to connect over SSH.')); 672 628 } 673 629 674 - if (!strlen($v_key) && !strlen($v_file)) { 675 - $c_key->setError(pht('No Key')); 676 - $c_file->setError(pht('No Key')); 677 - $page->addPageError( 678 - pht( 679 - 'You must provide either a private key or the path to a private '. 680 - 'key to connect over SSH.')); 681 - } else if (strlen($v_key) && strlen($v_file)) { 682 - $c_key->setError(pht('Ambiguous')); 683 - $c_file->setError(pht('Ambiguous')); 630 + $ssh_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE; 631 + if ($credential->getProvidesType() !== $ssh_type) { 632 + $c_credential->setError(pht('Invalid')); 684 633 $page->addPageError( 685 634 pht( 686 - 'Provide either a private key or the path to a private key, not '. 687 - 'both.')); 688 - } else if (!preg_match('/PRIVATE KEY/', $v_key)) { 689 - $c_key->setError(pht('Invalid')); 635 + 'You must choose an SSH credential, not some other type '. 636 + 'of credential.')); 637 + } 638 + 639 + } else if ($this->isUsernamePasswordProtocol($proto)) { 640 + if ($credential) { 641 + $password_type = PassphraseCredentialTypePassword::PROVIDES_TYPE; 642 + if ($credential->getProvidesType() !== $password_type) { 643 + $c_credential->setError(pht('Invalid')); 690 644 $page->addPageError( 691 645 pht( 692 - 'The private key you provided is missing the PRIVATE KEY header. '. 693 - 'You should include the header and footer. (Did you provide a '. 694 - 'public key by mistake?)')); 646 + 'You must choose a username/password credential, not some other '. 647 + 'type of credential.')); 648 + } 695 649 } 696 650 697 - return $c_user->isValid() && 698 - $c_key->isValid() && 699 - $c_file->isValid(); 700 - } else if ($this->isUsernamePasswordProtocol($proto)) { 701 - return true; 651 + return $c_credential->isValid(); 702 652 } else { 703 653 return true; 704 654 }
+8
src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
··· 481 481 pht('Remote URI'), 482 482 $repository->getHumanReadableDetail('remote-uri')); 483 483 484 + $credential_phid = $repository->getCredentialPHID(); 485 + if ($credential_phid) { 486 + $this->loadHandles(array($credential_phid)); 487 + $view->addProperty( 488 + pht('Credential'), 489 + $this->getHandle($credential_phid)->renderLink()); 490 + } 491 + 484 492 return $view; 485 493 } 486 494
+2 -2
src/applications/passphrase/application/PhabricatorApplicationPassphrase.php
··· 26 26 return self::GROUP_UTILITIES; 27 27 } 28 28 29 - public function isBeta() { 30 - return true; 29 + public function canUninstall() { 30 + return false; 31 31 } 32 32 33 33 public function getRoutes() {
+12 -4
src/applications/passphrase/keys/PassphraseAbstractKey.php
··· 32 32 PassphraseCredential $credential, 33 33 $provides_type) { 34 34 35 - $type = $credential->getCredentialType(); 36 - if ($type->getProvides() !== $provides_type) { 35 + $type = $credential->getCredentialTypeImplementation(); 36 + 37 + if (!$type) { 38 + throw new Exception( 39 + pht( 40 + 'Credential "%s" is of unknown type "%s"!', 41 + 'K'.$credential->getID(), 42 + $credential->getCredentialType())); 43 + } 44 + 45 + if ($type->getProvidesType() !== $provides_type) { 37 46 throw new Exception( 38 47 pht( 39 48 'Credential "%s" must provide "%s", but provides "%s"!', 40 49 'K'.$credential->getID(), 41 50 $provides_type, 42 - $type->getProvides())); 51 + $type->getProvidesType())); 43 52 } 44 - 45 53 } 46 54 47 55 protected function loadAndValidateFromPHID(
+5
src/applications/passphrase/storage/PassphraseCredential.php
··· 45 45 return $this->assertAttached($this->secret); 46 46 } 47 47 48 + public function getCredentialTypeImplementation() { 49 + $type = $this->getCredentialType(); 50 + return PassphraseCredentialType::getTypeByConstant($type); 51 + } 52 + 48 53 49 54 /* -( PhabricatorPolicyInterface )----------------------------------------- */ 50 55
+3 -8
src/applications/repository/conduit/ConduitAPI_repository_create_Method.php
··· 27 27 'encoding' => 'optional string', 28 28 'tracking' => 'optional bool', 29 29 'uri' => 'optional string', 30 - 'sshUser' => 'optional string', 31 - 'sshKey' => 'optional string', 32 - 'sshKeyFile' => 'optional string', 33 - 'httpUser' => 'optional string', 34 - 'httpPassword' => 'optional string', 30 + 'credentialPHID' => 'optional string', 35 31 'localPath' => 'optional string', 36 32 'svnSubpath' => 'optional string', 37 33 'branchFilter' => 'optional list<string>', ··· 100 96 } 101 97 $repository->setVersionControlSystem($map[$vcs]); 102 98 99 + $repository->setCredentialPHID($request->getValue('credentialPHID')); 100 + 103 101 $details = array( 104 102 'encoding' => $request->getValue('encoding'), 105 103 'description' => $request->getValue('description'), ··· 114 112 true), 115 113 'pull-frequency' => $request->getValue('pullFrequency'), 116 114 'default-branch' => $request->getValue('defaultBranch'), 117 - 'ssh-login' => $request->getValue('sshUser'), 118 - 'ssh-key' => $request->getValue('sshKey'), 119 - 'ssh-keyfile' => $request->getValue('sshKeyFile'), 120 115 'herald-disabled' => !$request->getValue('heraldEnabled', true), 121 116 'svn-subpath' => $request->getValue('svnSubpath'), 122 117 'disable-autoclose' => !$request->getValue('autocloseEnabled', true),
+33 -26
src/applications/repository/editor/PhabricatorRepositoryEditor.php
··· 29 29 $types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP; 30 30 $types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH; 31 31 $types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY; 32 + $types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL; 32 33 33 34 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 34 35 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; ··· 67 68 return (int)!$object->getDetail('disable-autoclose'); 68 69 case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: 69 70 return $object->getDetail('remote-uri'); 70 - case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN: 71 - return $object->getDetail('ssh-login'); 72 - case PhabricatorRepositoryTransaction::TYPE_SSH_KEY: 73 - return $object->getDetail('ssh-key'); 74 - case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE: 75 - return $object->getDetail('ssh-keyfile'); 76 - case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN: 77 - return $object->getDetail('http-login'); 78 - case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS: 79 - return $object->getDetail('http-pass'); 80 71 case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH: 81 72 return $object->getDetail('local-path'); 82 73 case PhabricatorRepositoryTransaction::TYPE_HOSTING: ··· 87 78 return $object->getServeOverSSH(); 88 79 case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: 89 80 return $object->getPushPolicy(); 81 + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: 82 + return $object->getCredentialPHID(); 90 83 } 91 84 } 92 85 ··· 116 109 case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP: 117 110 case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH: 118 111 case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: 112 + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: 119 113 return $xaction->getNewValue(); 120 114 case PhabricatorRepositoryTransaction::TYPE_NOTIFY: 121 115 case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: ··· 168 162 case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: 169 163 $object->setDetail('remote-uri', $xaction->getNewValue()); 170 164 break; 171 - case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN: 172 - $object->setDetail('ssh-login', $xaction->getNewValue()); 173 - break; 174 - case PhabricatorRepositoryTransaction::TYPE_SSH_KEY: 175 - $object->setDetail('ssh-key', $xaction->getNewValue()); 176 - break; 177 - case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE: 178 - $object->setDetail('ssh-keyfile', $xaction->getNewValue()); 179 - break; 180 - case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN: 181 - $object->setDetail('http-login', $xaction->getNewValue()); 182 - break; 183 - case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS: 184 - $object->setDetail('http-pass', $xaction->getNewValue()); 185 - break; 186 165 case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH: 187 166 $object->setDetail('local-path', $xaction->getNewValue()); 188 167 break; ··· 194 173 return $object->setServeOverSSH($xaction->getNewValue()); 195 174 case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: 196 175 return $object->setPushPolicy($xaction->getNewValue()); 176 + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: 177 + return $object->setCredentialPHID($xaction->getNewValue()); 197 178 case PhabricatorRepositoryTransaction::TYPE_ENCODING: 198 179 // Make sure the encoding is valid by converting to UTF-8. This tests 199 180 // that the user has mbstring installed, and also that they didn't type ··· 221 202 protected function applyCustomExternalTransaction( 222 203 PhabricatorLiskDAO $object, 223 204 PhabricatorApplicationTransaction $xaction) { 224 - return; 205 + 206 + switch ($xaction->getTransactionType()) { 207 + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: 208 + // Adjust the object <-> credential edge for this repository. 209 + 210 + $old_phid = $xaction->getOldValue(); 211 + $new_phid = $xaction->getNewValue(); 212 + 213 + $editor = id(new PhabricatorEdgeEditor()) 214 + ->setActor($this->requireActor()); 215 + 216 + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_USES_CREDENTIAL; 217 + $src_phid = $object->getPHID(); 218 + 219 + if ($old_phid) { 220 + $editor->removeEdge($src_phid, $edge_type, $old_phid); 221 + } 222 + 223 + if ($new_phid) { 224 + $editor->addEdge($src_phid, $edge_type, $new_phid); 225 + } 226 + 227 + $editor->save(); 228 + break; 229 + } 230 + 225 231 } 226 232 227 233 protected function mergeTransactions( ··· 278 284 case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP: 279 285 case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH: 280 286 case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: 287 + case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: 281 288 PhabricatorPolicyFilter::requireCapability( 282 289 $this->requireActor(), 283 290 $object,
+26 -37
src/applications/repository/storage/PhabricatorRepository.php
··· 39 39 40 40 protected $versionControlSystem; 41 41 protected $details = array(); 42 - 43 - private $sshKeyfile; 42 + protected $credentialPHID; 44 43 45 44 private $commitCount = self::ATTACHABLE; 46 45 private $mostRecentCommit = self::ATTACHABLE; ··· 366 365 367 366 switch ($this->getVersionControlSystem()) { 368 367 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: 369 - if ($this->shouldUseHTTP()) { 370 - $pattern = 371 - "svn ". 372 - "--non-interactive ". 373 - "--no-auth-cache ". 374 - "--trust-server-cert ". 375 - "--username %P ". 376 - "--password %P ". 377 - $pattern; 378 - array_unshift( 379 - $args, 380 - new PhutilOpaqueEnvelope($this->getDetail('http-login')), 381 - new PhutilOpaqueEnvelope($this->getDetail('http-pass'))); 382 - } else if ($this->shouldUseSVNProtocol()) { 383 - $pattern = 384 - "svn ". 385 - "--non-interactive ". 386 - "--no-auth-cache ". 387 - "--username %P ". 388 - "--password %P ". 389 - $pattern; 390 - array_unshift( 391 - $args, 392 - new PhutilOpaqueEnvelope($this->getDetail('http-login')), 393 - new PhutilOpaqueEnvelope($this->getDetail('http-pass'))); 368 + if ($this->shouldUseHTTP() || $this->shouldUseSVNProtocol()) { 369 + $flags = array(); 370 + $flag_args = array(); 371 + $flags[] = '--non-interactive'; 372 + $flags[] = '--no-auth-cache'; 373 + if ($this->shouldUseHTTP()) { 374 + $flags[] = '--trust-server-cert'; 375 + } 376 + 377 + $credential_phid = $this->getCredentialPHID(); 378 + if ($credential_phid) { 379 + $key = PassphrasePasswordKey::loadFromPHID( 380 + $credential_phid, 381 + PhabricatorUser::getOmnipotentUser()); 382 + $flags[] = '--username %P'; 383 + $flags[] = '--password %P'; 384 + $flag_args[] = $key->getUsernameEnvelope(); 385 + $flag_args[] = $key->getPasswordEnvelope(); 386 + } 387 + 388 + $flags = implode(' ', $flags); 389 + $pattern = "svn {$flags} {$pattern}"; 390 + $args = array_mergev(array($flag_args, $args)); 394 391 } else { 395 392 $pattern = "svn --non-interactive {$pattern}"; 396 393 } ··· 687 684 } 688 685 689 686 $protocol = $this->getRemoteProtocol(); 690 - if ($protocol == 'http' || $protocol == 'https') { 691 - return (bool)$this->getDetail('http-login'); 692 - } else { 693 - return false; 694 - } 687 + return ($protocol == 'http' || $protocol == 'https'); 695 688 } 696 689 697 690 ··· 708 701 } 709 702 710 703 $protocol = $this->getRemoteProtocol(); 711 - if ($protocol == 'svn') { 712 - return (bool)$this->getDetail('http-login'); 713 - } else { 714 - return false; 715 - } 704 + return ($protocol == 'svn'); 716 705 } 717 706 718 707
+8 -5
src/applications/repository/storage/PhabricatorRepositoryTransaction.php
··· 16 16 const TYPE_NOTIFY = 'repo:notify'; 17 17 const TYPE_AUTOCLOSE = 'repo:autoclose'; 18 18 const TYPE_REMOTE_URI = 'repo:remote-uri'; 19 + const TYPE_LOCAL_PATH = 'repo:local-path'; 20 + const TYPE_HOSTING = 'repo:hosting'; 21 + const TYPE_PROTOCOL_HTTP = 'repo:serve-http'; 22 + const TYPE_PROTOCOL_SSH = 'repo:serve-ssh'; 23 + const TYPE_PUSH_POLICY = 'repo:push-policy'; 24 + const TYPE_CREDENTIAL = 'repo:credential'; 25 + 26 + // TODO: Clean up these legacy transaction types. 19 27 const TYPE_SSH_LOGIN = 'repo:ssh-login'; 20 28 const TYPE_SSH_KEY = 'repo:ssh-key'; 21 29 const TYPE_SSH_KEYFILE = 'repo:ssh-keyfile'; 22 30 const TYPE_HTTP_LOGIN = 'repo:http-login'; 23 31 const TYPE_HTTP_PASS = 'repo:http-pass'; 24 - const TYPE_LOCAL_PATH = 'repo:local-path'; 25 - const TYPE_HOSTING = 'repo:hosting'; 26 - const TYPE_PROTOCOL_HTTP = 'repo:serve-http'; 27 - const TYPE_PROTOCOL_SSH = 'repo:serve-ssh'; 28 - const TYPE_PUSH_POLICY = 'repo:push-policy'; 29 32 30 33 public function getApplicationName() { 31 34 return 'repository';
+8
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1776 1776 'type' => 'sql', 1777 1777 'name' => $this->getPatchPath('20131121.passphraseedge.sql'), 1778 1778 ), 1779 + '20131121.repocredentials.1.col.sql' => array( 1780 + 'type' => 'sql', 1781 + 'name' => $this->getPatchPath('20131121.repocredentials.1.col.sql'), 1782 + ), 1783 + '20131121.repocredentials.2.mig.php' => array( 1784 + 'type' => 'php', 1785 + 'name' => $this->getPatchPath('20131121.repocredentials.2.mig.php'), 1786 + ), 1779 1787 ); 1780 1788 } 1781 1789 }