1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-30 17:30:59 +01:00

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
This commit is contained in:
epriestley 2013-11-22 15:23:33 -08:00
parent 819f899013
commit 51fb1ca16d
13 changed files with 296 additions and 231 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_repository.repository
ADD credentialPHID VARCHAR(64) COLLATE utf8_bin;

View file

@ -0,0 +1,104 @@
<?php
$table = new PhabricatorRepository();
$conn_w = $table->establishConnection('w');
$viewer = PhabricatorUser::getOmnipotentUser();
$map = array();
foreach (new LiskMigrationIterator($table) as $repository) {
$callsign = $repository->getCallsign();
echo "Examining repository {$callsign}...\n";
if ($repository->getCredentialPHID()) {
echo "...already has a Credential.\n";
continue;
}
$uri = $repository->getRemoteURI();
if (!$uri) {
echo "...no remote URI.\n";
continue;
}
$uri = new PhutilURI($uri);
$proto = strtolower($uri->getProtocol());
if ($proto == 'http' || $proto == 'https' || $proto == 'svn') {
$username = $repository->getDetail('http-login');
$secret = $repository->getDetail('http-pass');
$type = PassphraseCredentialTypePassword::CREDENTIAL_TYPE;
} else {
$username = $repository->getDetail('ssh-login');
$file = $repository->getDetail('ssh-keyfile');
if ($file) {
$secret = $file;
$type = PassphraseCredentialTypeSSHPrivateKeyFile::CREDENTIAL_TYPE;
} else {
$secret = $repository->getDetail('ssh-key');
$type = PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE;
}
}
if (!$username || !$secret) {
echo "...no credentials set.\n";
continue;
}
$map[$type][$username][$secret][] = $repository;
echo "...will migrate.\n";
}
$passphrase = new PassphraseSecret();
$passphrase->openTransaction();
$table->openTransaction();
foreach ($map as $credential_type => $credential_usernames) {
$type = PassphraseCredentialType::getTypeByConstant($credential_type);
foreach ($credential_usernames as $username => $credential_secrets) {
foreach ($credential_secrets as $secret_plaintext => $repositories) {
$callsigns = mpull($repositories, 'getCallsign');
$name = pht(
'Migrated Repository Credential (%s)',
implode(', ', $callsigns));
echo "Creating: {$name}...\n";
$secret = id(new PassphraseSecret())
->setSecretData($secret_plaintext)
->save();
$secret_id = $secret->getID();
$credential = PassphraseCredential::initializeNewCredential($viewer)
->setCredentialType($type->getCredentialType())
->setProvidesType($type->getProvidesType())
->setViewPolicy(PhabricatorPolicies::POLICY_ADMIN)
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
->setName($name)
->setUsername($username)
->setSecretID($secret_id)
->save();
foreach ($repositories as $repository) {
queryfx(
$conn_w,
'UPDATE %T SET credentialPHID = %s WHERE id = %d',
$table->getTableName(),
$credential->getPHID(),
$repository->getID());
$edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_USES_CREDENTIAL;
id(new PhabricatorEdgeEditor())
->setActor($viewer)
->addEdge($repository->getPHID(), $edge_type, $credential->getPHID())
->save();
}
}
}
}
$table->saveTransaction();
$passphrase->saveTransaction();
echo "Done.\n";

View file

@ -12,8 +12,10 @@ if (!$target_name) {
throw new Exception(pht("No 'PHABRICATOR_SSH_TARGET' in environment!")); throw new Exception(pht("No 'PHABRICATOR_SSH_TARGET' in environment!"));
} }
$viewer = PhabricatorUser::getOmnipotentUser();
$repository = id(new PhabricatorRepositoryQuery()) $repository = id(new PhabricatorRepositoryQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer($viewer)
->withCallsigns(array($target_name)) ->withCallsigns(array($target_name))
->executeOne(); ->executeOne();
if (!$repository) { if (!$repository) {
@ -28,30 +30,14 @@ $pattern[] = 'ssh';
$pattern[] = '-o'; $pattern[] = '-o';
$pattern[] = 'StrictHostKeyChecking=no'; $pattern[] = 'StrictHostKeyChecking=no';
$login = $repository->getSSHLogin(); $credential_phid = $repository->getCredentialPHID();
if (strlen($login)) { if ($credential_phid) {
$pattern[] = '-l'; $key = PassphraseSSHKey::loadFromPHID($credential_phid, $viewer);
$pattern[] = '%P';
$arguments[] = new PhutilOpaqueEnvelope($login);
}
$ssh_identity = null; $pattern[] = '-l %P';
$arguments[] = $key->getUsernameEnvelope();
$key = $repository->getDetail('ssh-key'); $pattern[] = '-i %P';
$keyfile = $repository->getDetail('ssh-keyfile'); $arguments[] = $key->getKeyfileEnvelope();
if ($keyfile) {
$ssh_identity = $keyfile;
} else if ($key) {
$tmpfile = new TempFile('phabricator-repository-ssh-key');
chmod($tmpfile, 0600);
Filesystem::writeFile($tmpfile, $key);
$ssh_identity = (string)$tmpfile;
}
if ($ssh_identity) {
$pattern[] = '-i';
$pattern[] = '%P';
$arguments[] = new PhutilOpaqueEnvelope($keyfile);
} }
$pattern[] = '--'; $pattern[] = '--';

View file

@ -94,12 +94,8 @@ final class DiffusionRepositoryCreateController
$type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE; $type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
$type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH; $type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
$type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI; $type_remote_uri = PhabricatorRepositoryTransaction::TYPE_REMOTE_URI;
$type_ssh_login = PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN;
$type_ssh_key = PhabricatorRepositoryTransaction::TYPE_SSH_KEY;
$type_ssh_keyfile = PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE;
$type_http_login = PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN;
$type_http_pass = PhabricatorRepositoryTransaction::TYPE_HTTP_PASS;
$type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING; $type_hosting = PhabricatorRepositoryTransaction::TYPE_HOSTING;
$type_credential = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
$xactions = array(); $xactions = array();
@ -159,29 +155,9 @@ final class DiffusionRepositoryCreateController
->getValue()); ->getValue());
$xactions[] = id(clone $template) $xactions[] = id(clone $template)
->setTransactionType($type_ssh_login) ->setTransactionType($type_credential)
->setNewValue( ->setNewValue(
$form->getPage('auth')->getControl('ssh-login')->getValue()); $form->getPage('auth')->getControl('credential')->getValue());
$xactions[] = id(clone $template)
->setTransactionType($type_ssh_key)
->setNewValue(
$form->getPage('auth')->getControl('ssh-key')->getValue());
$xactions[] = id(clone $template)
->setTransactionType($type_ssh_keyfile)
->setNewValue(
$form->getPage('auth')->getControl('ssh-keyfile')->getValue());
$xactions[] = id(clone $template)
->setTransactionType($type_http_login)
->setNewValue(
$form->getPage('auth')->getControl('http-login')->getValue());
$xactions[] = id(clone $template)
->setTransactionType($type_http_pass)
->setNewValue(
$form->getPage('auth')->getControl('http-pass')->getValue());
} }
id(new PhabricatorRepositoryEditor()) id(new PhabricatorRepositoryEditor())
@ -198,11 +174,7 @@ final class DiffusionRepositoryCreateController
if ($repository) { if ($repository) {
$dict = array( $dict = array(
'remoteURI' => $repository->getRemoteURI(), 'remoteURI' => $repository->getRemoteURI(),
'ssh-login' => $repository->getDetail('ssh-login'), 'credential' => $repository->getCredentialPHID(),
'ssh-key' => $repository->getDetail('ssh-key'),
'ssh-keyfile' => $repository->getDetail('ssh-keyfile'),
'http-login' => $repository->getDetail('http-login'),
'http-pass' => $repository->getDetail('http-pass'),
); );
} }
$form->readFromObject($dict); $form->readFromObject($dict);
@ -550,105 +522,86 @@ final class DiffusionRepositoryCreateController
->setUser($this->getRequest()->getUser()) ->setUser($this->getRequest()->getUser())
->setAdjustFormPageCallback(array($this, 'adjustAuthPage')) ->setAdjustFormPageCallback(array($this, 'adjustAuthPage'))
->addControl( ->addControl(
id(new AphrontFormTextControl()) id(new PassphraseCredentialControl())
->setName('ssh-login') ->setName('credential'));
->setLabel('SSH User'))
->addControl(
id(new AphrontFormTextAreaControl())
->setName('ssh-key')
->setLabel('SSH Private Key')
->setHeight(AphrontFormTextAreaControl::HEIGHT_SHORT)
->setCaption(
hsprintf('Specify the entire private key, <em>or</em>...')))
->addControl(
id(new AphrontFormTextControl())
->setName('ssh-keyfile')
->setLabel('SSH Private Key Path')
->setCaption(
'...specify a path on disk where the daemon should '.
'look for a private key.'))
->addControl(
id(new AphrontFormTextControl())
->setName('http-login')
->setLabel('Username'))
->addControl(
id(new AphrontFormPasswordControl())
->setName('http-pass')
->setLabel('Password'));
} }
public function adjustAuthPage($page) { public function adjustAuthPage($page) {
$form = $page->getForm(); $form = $page->getForm();
$remote_uri = $form->getPage('remote-uri')
->getControl('remoteURI')
->getValue();
if ($this->getRepository()) { if ($this->getRepository()) {
$vcs = $this->getRepository()->getVersionControlSystem(); $vcs = $this->getRepository()->getVersionControlSystem();
} else { } else {
$vcs = $form->getPage('vcs')->getControl('vcs')->getValue(); $vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
} }
$remote_uri = $form->getPage('remote-uri')
->getControl('remoteURI')
->getValue();
$proto = $this->getRemoteURIProtocol($remote_uri); $proto = $this->getRemoteURIProtocol($remote_uri);
$remote_user = $this->getRemoteURIUser($remote_uri); $remote_user = $this->getRemoteURIUser($remote_uri);
$page->getControl('ssh-login')->setHidden(true); $c_credential = $page->getControl('credential');
$page->getControl('ssh-key')->setHidden(true); $c_credential->setDefaultUsername($remote_user);
$page->getControl('ssh-keyfile')->setHidden(true);
$page->getControl('http-login')->setHidden(true);
$page->getControl('http-pass')->setHidden(true);
if ($this->isSSHProtocol($proto)) { if ($this->isSSHProtocol($proto)) {
$page->getControl('ssh-login')->setHidden(false); $c_credential->setLabel(pht('SSH Key'));
$page->getControl('ssh-key')->setHidden(false); $c_credential->setCredentialType(
$page->getControl('ssh-keyfile')->setHidden(false); PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE);
$provides_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE;
$c_login = $page->getControl('ssh-login');
if (!strlen($c_login->getValue())) {
$c_login->setValue($remote_user);
}
$page->addRemarkupInstructions( $page->addRemarkupInstructions(
pht( pht(
'Enter the username and private key to use to connect to the '. 'Choose or add the SSH credentials to use to connect to the the '.
'the repository hosted at:'. 'repository hosted at:'.
"\n\n". "\n\n".
" lang=text\n". " lang=text\n".
" %s". " %s",
"\n\n".
'You can either copy/paste the entire private key, or put it '.
'somewhere on disk and provide the path to it.',
$remote_uri), $remote_uri),
'ssh-login'); 'credential');
} else if ($this->isUsernamePasswordProtocol($proto)) { } else if ($this->isUsernamePasswordProtocol($proto)) {
$page->getControl('http-login')->setHidden(false); $c_credential->setLabel(pht('Password'));
$page->getControl('http-pass')->setHidden(false); $c_credential->setAllowNull(true);
$c_credential->setCredentialType(
PassphraseCredentialTypePassword::CREDENTIAL_TYPE);
$provides_type = PassphraseCredentialTypePassword::PROVIDES_TYPE;
$page->addRemarkupInstructions( $page->addRemarkupInstructions(
pht( pht(
'Enter the a username and pasword used to connect to the '. 'Choose the a username and pasword used to connect to the '.
'repository hosted at:'. 'repository hosted at:'.
"\n\n". "\n\n".
" lang=text\n". " lang=text\n".
" %s". " %s".
"\n\n". "\n\n".
"If this repository does not require a username or password, ". "If this repository does not require a username or password, ".
"you can leave these fields blank.", "you can continue to the next step.",
$remote_uri), $remote_uri),
'http-login'); 'credential');
} else if ($proto == 'file') { } else if ($proto == 'file') {
$c_credential->setHidden(true);
$page->addRemarkupInstructions( $page->addRemarkupInstructions(
pht( pht(
'You do not need to configure any authentication options for '. 'You do not need to configure any credentials for repositories '.
'repositories accessed over the `file://` protocol. Continue '. 'accessed over the `file://` protocol. Continue to the next step.'),
'to the next step.'), 'credential');
'ssh-login');
} else { } else {
throw new Exception("Unknown URI protocol!"); throw new Exception("Unknown URI protocol!");
} }
if ($provides_type) {
$viewer = $this->getRequest()->getUser();
$options = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withIsDestroyed(false)
->withProvidesTypes(array($provides_type))
->execute();
$c_credential->setOptions($options);
}
} }
public function validateAuthPage(PHUIFormPageView $page) { public function validateAuthPage(PHUIFormPageView $page) {
@ -656,49 +609,46 @@ final class DiffusionRepositoryCreateController
$remote_uri = $form->getPage('remote')->getControl('remoteURI')->getValue(); $remote_uri = $form->getPage('remote')->getControl('remoteURI')->getValue();
$proto = $this->getRemoteURIProtocol($remote_uri); $proto = $this->getRemoteURIProtocol($remote_uri);
$c_credential = $page->getControl('credential');
$v_credential = $c_credential->getValue();
// NOTE: We're using the omnipotent user here because the viewer might be
// editing a repository they're allowed to edit which uses a credential they
// are not allowed to see. This is fine, as long as they don't change it.
$credential = id(new PassphraseCredentialQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($v_credential))
->executeOne();
if ($this->isSSHProtocol($proto)) { if ($this->isSSHProtocol($proto)) {
$c_user = $page->getControl('ssh-login'); if (!$credential) {
$c_key = $page->getControl('ssh-key'); $c_credential->setError(pht('Required'));
$c_file = $page->getControl('ssh-keyfile');
$v_user = $c_user->getValue();
$v_key = $c_key->getValue();
$v_file = $c_file->getValue();
if (!strlen($v_user)) {
$c_user->setError(pht('Required'));
$page->addPageError( $page->addPageError(
pht('You must provide an SSH login username to connect over SSH.')); pht('You must choose an SSH credential to connect over SSH.'));
} }
if (!strlen($v_key) && !strlen($v_file)) { $ssh_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE;
$c_key->setError(pht('No Key')); if ($credential->getProvidesType() !== $ssh_type) {
$c_file->setError(pht('No Key')); $c_credential->setError(pht('Invalid'));
$page->addPageError( $page->addPageError(
pht( pht(
'You must provide either a private key or the path to a private '. 'You must choose an SSH credential, not some other type '.
'key to connect over SSH.')); 'of credential.'));
} else if (strlen($v_key) && strlen($v_file)) {
$c_key->setError(pht('Ambiguous'));
$c_file->setError(pht('Ambiguous'));
$page->addPageError(
pht(
'Provide either a private key or the path to a private key, not '.
'both.'));
} else if (!preg_match('/PRIVATE KEY/', $v_key)) {
$c_key->setError(pht('Invalid'));
$page->addPageError(
pht(
'The private key you provided is missing the PRIVATE KEY header. '.
'You should include the header and footer. (Did you provide a '.
'public key by mistake?)'));
} }
return $c_user->isValid() &&
$c_key->isValid() &&
$c_file->isValid();
} else if ($this->isUsernamePasswordProtocol($proto)) { } else if ($this->isUsernamePasswordProtocol($proto)) {
return true; if ($credential) {
$password_type = PassphraseCredentialTypePassword::PROVIDES_TYPE;
if ($credential->getProvidesType() !== $password_type) {
$c_credential->setError(pht('Invalid'));
$page->addPageError(
pht(
'You must choose a username/password credential, not some other '.
'type of credential.'));
}
}
return $c_credential->isValid();
} else { } else {
return true; return true;
} }

View file

@ -481,6 +481,14 @@ final class DiffusionRepositoryEditMainController
pht('Remote URI'), pht('Remote URI'),
$repository->getHumanReadableDetail('remote-uri')); $repository->getHumanReadableDetail('remote-uri'));
$credential_phid = $repository->getCredentialPHID();
if ($credential_phid) {
$this->loadHandles(array($credential_phid));
$view->addProperty(
pht('Credential'),
$this->getHandle($credential_phid)->renderLink());
}
return $view; return $view;
} }

View file

@ -26,8 +26,8 @@ final class PhabricatorApplicationPassphrase extends PhabricatorApplication {
return self::GROUP_UTILITIES; return self::GROUP_UTILITIES;
} }
public function isBeta() { public function canUninstall() {
return true; return false;
} }
public function getRoutes() { public function getRoutes() {

View file

@ -32,16 +32,24 @@ abstract class PassphraseAbstractKey extends Phobject {
PassphraseCredential $credential, PassphraseCredential $credential,
$provides_type) { $provides_type) {
$type = $credential->getCredentialType(); $type = $credential->getCredentialTypeImplementation();
if ($type->getProvides() !== $provides_type) {
if (!$type) {
throw new Exception(
pht(
'Credential "%s" is of unknown type "%s"!',
'K'.$credential->getID(),
$credential->getCredentialType()));
}
if ($type->getProvidesType() !== $provides_type) {
throw new Exception( throw new Exception(
pht( pht(
'Credential "%s" must provide "%s", but provides "%s"!', 'Credential "%s" must provide "%s", but provides "%s"!',
'K'.$credential->getID(), 'K'.$credential->getID(),
$provides_type, $provides_type,
$type->getProvides())); $type->getProvidesType()));
} }
} }
protected function loadAndValidateFromPHID( protected function loadAndValidateFromPHID(

View file

@ -45,6 +45,11 @@ final class PassphraseCredential extends PassphraseDAO
return $this->assertAttached($this->secret); return $this->assertAttached($this->secret);
} }
public function getCredentialTypeImplementation() {
$type = $this->getCredentialType();
return PassphraseCredentialType::getTypeByConstant($type);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -27,11 +27,7 @@ final class ConduitAPI_repository_create_Method
'encoding' => 'optional string', 'encoding' => 'optional string',
'tracking' => 'optional bool', 'tracking' => 'optional bool',
'uri' => 'optional string', 'uri' => 'optional string',
'sshUser' => 'optional string', 'credentialPHID' => 'optional string',
'sshKey' => 'optional string',
'sshKeyFile' => 'optional string',
'httpUser' => 'optional string',
'httpPassword' => 'optional string',
'localPath' => 'optional string', 'localPath' => 'optional string',
'svnSubpath' => 'optional string', 'svnSubpath' => 'optional string',
'branchFilter' => 'optional list<string>', 'branchFilter' => 'optional list<string>',
@ -100,6 +96,8 @@ final class ConduitAPI_repository_create_Method
} }
$repository->setVersionControlSystem($map[$vcs]); $repository->setVersionControlSystem($map[$vcs]);
$repository->setCredentialPHID($request->getValue('credentialPHID'));
$details = array( $details = array(
'encoding' => $request->getValue('encoding'), 'encoding' => $request->getValue('encoding'),
'description' => $request->getValue('description'), 'description' => $request->getValue('description'),
@ -114,9 +112,6 @@ final class ConduitAPI_repository_create_Method
true), true),
'pull-frequency' => $request->getValue('pullFrequency'), 'pull-frequency' => $request->getValue('pullFrequency'),
'default-branch' => $request->getValue('defaultBranch'), 'default-branch' => $request->getValue('defaultBranch'),
'ssh-login' => $request->getValue('sshUser'),
'ssh-key' => $request->getValue('sshKey'),
'ssh-keyfile' => $request->getValue('sshKeyFile'),
'herald-disabled' => !$request->getValue('heraldEnabled', true), 'herald-disabled' => !$request->getValue('heraldEnabled', true),
'svn-subpath' => $request->getValue('svnSubpath'), 'svn-subpath' => $request->getValue('svnSubpath'),
'disable-autoclose' => !$request->getValue('autocloseEnabled', true), 'disable-autoclose' => !$request->getValue('autocloseEnabled', true),

View file

@ -29,6 +29,7 @@ final class PhabricatorRepositoryEditor
$types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP; $types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
$types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH; $types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
$types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY; $types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
$types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
@ -67,16 +68,6 @@ final class PhabricatorRepositoryEditor
return (int)!$object->getDetail('disable-autoclose'); return (int)!$object->getDetail('disable-autoclose');
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
return $object->getDetail('remote-uri'); return $object->getDetail('remote-uri');
case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN:
return $object->getDetail('ssh-login');
case PhabricatorRepositoryTransaction::TYPE_SSH_KEY:
return $object->getDetail('ssh-key');
case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE:
return $object->getDetail('ssh-keyfile');
case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN:
return $object->getDetail('http-login');
case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS:
return $object->getDetail('http-pass');
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH: case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
return $object->getDetail('local-path'); return $object->getDetail('local-path');
case PhabricatorRepositoryTransaction::TYPE_HOSTING: case PhabricatorRepositoryTransaction::TYPE_HOSTING:
@ -87,6 +78,8 @@ final class PhabricatorRepositoryEditor
return $object->getServeOverSSH(); return $object->getServeOverSSH();
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
return $object->getPushPolicy(); return $object->getPushPolicy();
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
return $object->getCredentialPHID();
} }
} }
@ -116,6 +109,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP: case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH: case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
return $xaction->getNewValue(); return $xaction->getNewValue();
case PhabricatorRepositoryTransaction::TYPE_NOTIFY: case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
@ -168,21 +162,6 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
$object->setDetail('remote-uri', $xaction->getNewValue()); $object->setDetail('remote-uri', $xaction->getNewValue());
break; break;
case PhabricatorRepositoryTransaction::TYPE_SSH_LOGIN:
$object->setDetail('ssh-login', $xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_SSH_KEY:
$object->setDetail('ssh-key', $xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_SSH_KEYFILE:
$object->setDetail('ssh-keyfile', $xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_HTTP_LOGIN:
$object->setDetail('http-login', $xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_HTTP_PASS:
$object->setDetail('http-pass', $xaction->getNewValue());
break;
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH: case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
$object->setDetail('local-path', $xaction->getNewValue()); $object->setDetail('local-path', $xaction->getNewValue());
break; break;
@ -194,6 +173,8 @@ final class PhabricatorRepositoryEditor
return $object->setServeOverSSH($xaction->getNewValue()); return $object->setServeOverSSH($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
return $object->setPushPolicy($xaction->getNewValue()); return $object->setPushPolicy($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
return $object->setCredentialPHID($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_ENCODING: case PhabricatorRepositoryTransaction::TYPE_ENCODING:
// Make sure the encoding is valid by converting to UTF-8. This tests // Make sure the encoding is valid by converting to UTF-8. This tests
// that the user has mbstring installed, and also that they didn't type // that the user has mbstring installed, and also that they didn't type
@ -221,7 +202,32 @@ final class PhabricatorRepositoryEditor
protected function applyCustomExternalTransaction( protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) { PhabricatorApplicationTransaction $xaction) {
return;
switch ($xaction->getTransactionType()) {
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
// Adjust the object <-> credential edge for this repository.
$old_phid = $xaction->getOldValue();
$new_phid = $xaction->getNewValue();
$editor = id(new PhabricatorEdgeEditor())
->setActor($this->requireActor());
$edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_USES_CREDENTIAL;
$src_phid = $object->getPHID();
if ($old_phid) {
$editor->removeEdge($src_phid, $edge_type, $old_phid);
}
if ($new_phid) {
$editor->addEdge($src_phid, $edge_type, $new_phid);
}
$editor->save();
break;
}
} }
protected function mergeTransactions( protected function mergeTransactions(
@ -278,6 +284,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP: case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH: case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY: case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
PhabricatorPolicyFilter::requireCapability( PhabricatorPolicyFilter::requireCapability(
$this->requireActor(), $this->requireActor(),
$object, $object,

View file

@ -39,8 +39,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
protected $versionControlSystem; protected $versionControlSystem;
protected $details = array(); protected $details = array();
protected $credentialPHID;
private $sshKeyfile;
private $commitCount = self::ATTACHABLE; private $commitCount = self::ATTACHABLE;
private $mostRecentCommit = self::ATTACHABLE; private $mostRecentCommit = self::ATTACHABLE;
@ -366,31 +365,29 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
switch ($this->getVersionControlSystem()) { switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
if ($this->shouldUseHTTP() || $this->shouldUseSVNProtocol()) {
$flags = array();
$flag_args = array();
$flags[] = '--non-interactive';
$flags[] = '--no-auth-cache';
if ($this->shouldUseHTTP()) { if ($this->shouldUseHTTP()) {
$pattern = $flags[] = '--trust-server-cert';
"svn ". }
"--non-interactive ".
"--no-auth-cache ". $credential_phid = $this->getCredentialPHID();
"--trust-server-cert ". if ($credential_phid) {
"--username %P ". $key = PassphrasePasswordKey::loadFromPHID(
"--password %P ". $credential_phid,
$pattern; PhabricatorUser::getOmnipotentUser());
array_unshift( $flags[] = '--username %P';
$args, $flags[] = '--password %P';
new PhutilOpaqueEnvelope($this->getDetail('http-login')), $flag_args[] = $key->getUsernameEnvelope();
new PhutilOpaqueEnvelope($this->getDetail('http-pass'))); $flag_args[] = $key->getPasswordEnvelope();
} else if ($this->shouldUseSVNProtocol()) { }
$pattern =
"svn ". $flags = implode(' ', $flags);
"--non-interactive ". $pattern = "svn {$flags} {$pattern}";
"--no-auth-cache ". $args = array_mergev(array($flag_args, $args));
"--username %P ".
"--password %P ".
$pattern;
array_unshift(
$args,
new PhutilOpaqueEnvelope($this->getDetail('http-login')),
new PhutilOpaqueEnvelope($this->getDetail('http-pass')));
} else { } else {
$pattern = "svn --non-interactive {$pattern}"; $pattern = "svn --non-interactive {$pattern}";
} }
@ -687,11 +684,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
} }
$protocol = $this->getRemoteProtocol(); $protocol = $this->getRemoteProtocol();
if ($protocol == 'http' || $protocol == 'https') { return ($protocol == 'http' || $protocol == 'https');
return (bool)$this->getDetail('http-login');
} else {
return false;
}
} }
@ -708,11 +701,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
} }
$protocol = $this->getRemoteProtocol(); $protocol = $this->getRemoteProtocol();
if ($protocol == 'svn') { return ($protocol == 'svn');
return (bool)$this->getDetail('http-login');
} else {
return false;
}
} }

View file

@ -16,16 +16,19 @@ final class PhabricatorRepositoryTransaction
const TYPE_NOTIFY = 'repo:notify'; const TYPE_NOTIFY = 'repo:notify';
const TYPE_AUTOCLOSE = 'repo:autoclose'; const TYPE_AUTOCLOSE = 'repo:autoclose';
const TYPE_REMOTE_URI = 'repo:remote-uri'; const TYPE_REMOTE_URI = 'repo:remote-uri';
const TYPE_SSH_LOGIN = 'repo:ssh-login';
const TYPE_SSH_KEY = 'repo:ssh-key';
const TYPE_SSH_KEYFILE = 'repo:ssh-keyfile';
const TYPE_HTTP_LOGIN = 'repo:http-login';
const TYPE_HTTP_PASS = 'repo:http-pass';
const TYPE_LOCAL_PATH = 'repo:local-path'; const TYPE_LOCAL_PATH = 'repo:local-path';
const TYPE_HOSTING = 'repo:hosting'; const TYPE_HOSTING = 'repo:hosting';
const TYPE_PROTOCOL_HTTP = 'repo:serve-http'; const TYPE_PROTOCOL_HTTP = 'repo:serve-http';
const TYPE_PROTOCOL_SSH = 'repo:serve-ssh'; const TYPE_PROTOCOL_SSH = 'repo:serve-ssh';
const TYPE_PUSH_POLICY = 'repo:push-policy'; const TYPE_PUSH_POLICY = 'repo:push-policy';
const TYPE_CREDENTIAL = 'repo:credential';
// TODO: Clean up these legacy transaction types.
const TYPE_SSH_LOGIN = 'repo:ssh-login';
const TYPE_SSH_KEY = 'repo:ssh-key';
const TYPE_SSH_KEYFILE = 'repo:ssh-keyfile';
const TYPE_HTTP_LOGIN = 'repo:http-login';
const TYPE_HTTP_PASS = 'repo:http-pass';
public function getApplicationName() { public function getApplicationName() {
return 'repository'; return 'repository';

View file

@ -1776,6 +1776,14 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'sql', 'type' => 'sql',
'name' => $this->getPatchPath('20131121.passphraseedge.sql'), 'name' => $this->getPatchPath('20131121.passphraseedge.sql'),
), ),
'20131121.repocredentials.1.col.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20131121.repocredentials.1.col.sql'),
),
'20131121.repocredentials.2.mig.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('20131121.repocredentials.2.mig.php'),
),
); );
} }
} }