diff --git a/resources/sql/patches/20131121.repocredentials.1.col.sql b/resources/sql/patches/20131121.repocredentials.1.col.sql
new file mode 100644
index 0000000000..9471c7189b
--- /dev/null
+++ b/resources/sql/patches/20131121.repocredentials.1.col.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_repository.repository
+ ADD credentialPHID VARCHAR(64) COLLATE utf8_bin;
diff --git a/resources/sql/patches/20131121.repocredentials.2.mig.php b/resources/sql/patches/20131121.repocredentials.2.mig.php
new file mode 100644
index 0000000000..d9735d6b5b
--- /dev/null
+++ b/resources/sql/patches/20131121.repocredentials.2.mig.php
@@ -0,0 +1,104 @@
+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";
diff --git a/scripts/ssh/ssh-connect.php b/scripts/ssh/ssh-connect.php
index 2f28778565..03e1caa77a 100755
--- a/scripts/ssh/ssh-connect.php
+++ b/scripts/ssh/ssh-connect.php
@@ -12,8 +12,10 @@ if (!$target_name) {
throw new Exception(pht("No 'PHABRICATOR_SSH_TARGET' in environment!"));
}
+$viewer = PhabricatorUser::getOmnipotentUser();
+
$repository = id(new PhabricatorRepositoryQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->setViewer($viewer)
->withCallsigns(array($target_name))
->executeOne();
if (!$repository) {
@@ -28,30 +30,14 @@ $pattern[] = 'ssh';
$pattern[] = '-o';
$pattern[] = 'StrictHostKeyChecking=no';
-$login = $repository->getSSHLogin();
-if (strlen($login)) {
- $pattern[] = '-l';
- $pattern[] = '%P';
- $arguments[] = new PhutilOpaqueEnvelope($login);
-}
+$credential_phid = $repository->getCredentialPHID();
+if ($credential_phid) {
+ $key = PassphraseSSHKey::loadFromPHID($credential_phid, $viewer);
-$ssh_identity = null;
-
-$key = $repository->getDetail('ssh-key');
-$keyfile = $repository->getDetail('ssh-keyfile');
-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[] = '-l %P';
+ $arguments[] = $key->getUsernameEnvelope();
+ $pattern[] = '-i %P';
+ $arguments[] = $key->getKeyfileEnvelope();
}
$pattern[] = '--';
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
index 36ad1feb83..6bb3b0f70c 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php
@@ -94,12 +94,8 @@ final class DiffusionRepositoryCreateController
$type_activate = PhabricatorRepositoryTransaction::TYPE_ACTIVATE;
$type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
$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_credential = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
$xactions = array();
@@ -159,29 +155,9 @@ final class DiffusionRepositoryCreateController
->getValue());
$xactions[] = id(clone $template)
- ->setTransactionType($type_ssh_login)
+ ->setTransactionType($type_credential)
->setNewValue(
- $form->getPage('auth')->getControl('ssh-login')->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());
+ $form->getPage('auth')->getControl('credential')->getValue());
}
id(new PhabricatorRepositoryEditor())
@@ -198,11 +174,7 @@ final class DiffusionRepositoryCreateController
if ($repository) {
$dict = array(
'remoteURI' => $repository->getRemoteURI(),
- 'ssh-login' => $repository->getDetail('ssh-login'),
- 'ssh-key' => $repository->getDetail('ssh-key'),
- 'ssh-keyfile' => $repository->getDetail('ssh-keyfile'),
- 'http-login' => $repository->getDetail('http-login'),
- 'http-pass' => $repository->getDetail('http-pass'),
+ 'credential' => $repository->getCredentialPHID(),
);
}
$form->readFromObject($dict);
@@ -550,105 +522,86 @@ final class DiffusionRepositoryCreateController
->setUser($this->getRequest()->getUser())
->setAdjustFormPageCallback(array($this, 'adjustAuthPage'))
->addControl(
- id(new AphrontFormTextControl())
- ->setName('ssh-login')
- ->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, or...')))
- ->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'));
+ id(new PassphraseCredentialControl())
+ ->setName('credential'));
}
public function adjustAuthPage($page) {
$form = $page->getForm();
- $remote_uri = $form->getPage('remote-uri')
- ->getControl('remoteURI')
- ->getValue();
-
if ($this->getRepository()) {
$vcs = $this->getRepository()->getVersionControlSystem();
} else {
$vcs = $form->getPage('vcs')->getControl('vcs')->getValue();
}
+ $remote_uri = $form->getPage('remote-uri')
+ ->getControl('remoteURI')
+ ->getValue();
$proto = $this->getRemoteURIProtocol($remote_uri);
$remote_user = $this->getRemoteURIUser($remote_uri);
- $page->getControl('ssh-login')->setHidden(true);
- $page->getControl('ssh-key')->setHidden(true);
- $page->getControl('ssh-keyfile')->setHidden(true);
- $page->getControl('http-login')->setHidden(true);
- $page->getControl('http-pass')->setHidden(true);
+ $c_credential = $page->getControl('credential');
+ $c_credential->setDefaultUsername($remote_user);
if ($this->isSSHProtocol($proto)) {
- $page->getControl('ssh-login')->setHidden(false);
- $page->getControl('ssh-key')->setHidden(false);
- $page->getControl('ssh-keyfile')->setHidden(false);
-
- $c_login = $page->getControl('ssh-login');
- if (!strlen($c_login->getValue())) {
- $c_login->setValue($remote_user);
- }
+ $c_credential->setLabel(pht('SSH Key'));
+ $c_credential->setCredentialType(
+ PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE);
+ $provides_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE;
$page->addRemarkupInstructions(
pht(
- 'Enter the username and private key to use to connect to the '.
- 'the repository hosted at:'.
+ 'Choose or add the SSH credentials to use to connect to the the '.
+ 'repository hosted at:'.
"\n\n".
" lang=text\n".
- " %s".
- "\n\n".
- 'You can either copy/paste the entire private key, or put it '.
- 'somewhere on disk and provide the path to it.',
+ " %s",
$remote_uri),
- 'ssh-login');
-
+ 'credential');
} else if ($this->isUsernamePasswordProtocol($proto)) {
- $page->getControl('http-login')->setHidden(false);
- $page->getControl('http-pass')->setHidden(false);
+ $c_credential->setLabel(pht('Password'));
+ $c_credential->setAllowNull(true);
+ $c_credential->setCredentialType(
+ PassphraseCredentialTypePassword::CREDENTIAL_TYPE);
+ $provides_type = PassphraseCredentialTypePassword::PROVIDES_TYPE;
$page->addRemarkupInstructions(
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:'.
"\n\n".
" lang=text\n".
" %s".
"\n\n".
"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),
- 'http-login');
+ 'credential');
} else if ($proto == 'file') {
+ $c_credential->setHidden(true);
$page->addRemarkupInstructions(
pht(
- 'You do not need to configure any authentication options for '.
- 'repositories accessed over the `file://` protocol. Continue '.
- 'to the next step.'),
- 'ssh-login');
+ 'You do not need to configure any credentials for repositories '.
+ 'accessed over the `file://` protocol. Continue to the next step.'),
+ 'credential');
} else {
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) {
@@ -656,49 +609,46 @@ final class DiffusionRepositoryCreateController
$remote_uri = $form->getPage('remote')->getControl('remoteURI')->getValue();
$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)) {
- $c_user = $page->getControl('ssh-login');
- $c_key = $page->getControl('ssh-key');
- $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'));
+ if (!$credential) {
+ $c_credential->setError(pht('Required'));
$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)) {
- $c_key->setError(pht('No Key'));
- $c_file->setError(pht('No Key'));
+ $ssh_type = PassphraseCredentialTypeSSHPrivateKey::PROVIDES_TYPE;
+ if ($credential->getProvidesType() !== $ssh_type) {
+ $c_credential->setError(pht('Invalid'));
$page->addPageError(
pht(
- 'You must provide either a private key or the path to a private '.
- 'key to connect over SSH.'));
- } 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?)'));
+ 'You must choose an SSH credential, not some other type '.
+ 'of credential.'));
}
- return $c_user->isValid() &&
- $c_key->isValid() &&
- $c_file->isValid();
} 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 {
return true;
}
diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
index 5ba4423481..999ae7845f 100644
--- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
+++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php
@@ -481,6 +481,14 @@ final class DiffusionRepositoryEditMainController
pht('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;
}
diff --git a/src/applications/passphrase/application/PhabricatorApplicationPassphrase.php b/src/applications/passphrase/application/PhabricatorApplicationPassphrase.php
index 493372262b..f2951353ed 100644
--- a/src/applications/passphrase/application/PhabricatorApplicationPassphrase.php
+++ b/src/applications/passphrase/application/PhabricatorApplicationPassphrase.php
@@ -26,8 +26,8 @@ final class PhabricatorApplicationPassphrase extends PhabricatorApplication {
return self::GROUP_UTILITIES;
}
- public function isBeta() {
- return true;
+ public function canUninstall() {
+ return false;
}
public function getRoutes() {
diff --git a/src/applications/passphrase/keys/PassphraseAbstractKey.php b/src/applications/passphrase/keys/PassphraseAbstractKey.php
index 76cba1b39e..cf532d3fac 100644
--- a/src/applications/passphrase/keys/PassphraseAbstractKey.php
+++ b/src/applications/passphrase/keys/PassphraseAbstractKey.php
@@ -32,16 +32,24 @@ abstract class PassphraseAbstractKey extends Phobject {
PassphraseCredential $credential,
$provides_type) {
- $type = $credential->getCredentialType();
- if ($type->getProvides() !== $provides_type) {
+ $type = $credential->getCredentialTypeImplementation();
+
+ 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(
pht(
'Credential "%s" must provide "%s", but provides "%s"!',
'K'.$credential->getID(),
$provides_type,
- $type->getProvides()));
+ $type->getProvidesType()));
}
-
}
protected function loadAndValidateFromPHID(
diff --git a/src/applications/passphrase/storage/PassphraseCredential.php b/src/applications/passphrase/storage/PassphraseCredential.php
index 880bf5f170..75c68766c0 100644
--- a/src/applications/passphrase/storage/PassphraseCredential.php
+++ b/src/applications/passphrase/storage/PassphraseCredential.php
@@ -45,6 +45,11 @@ final class PassphraseCredential extends PassphraseDAO
return $this->assertAttached($this->secret);
}
+ public function getCredentialTypeImplementation() {
+ $type = $this->getCredentialType();
+ return PassphraseCredentialType::getTypeByConstant($type);
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
diff --git a/src/applications/repository/conduit/ConduitAPI_repository_create_Method.php b/src/applications/repository/conduit/ConduitAPI_repository_create_Method.php
index cb6237bbd1..630e7c0d53 100644
--- a/src/applications/repository/conduit/ConduitAPI_repository_create_Method.php
+++ b/src/applications/repository/conduit/ConduitAPI_repository_create_Method.php
@@ -27,11 +27,7 @@ final class ConduitAPI_repository_create_Method
'encoding' => 'optional string',
'tracking' => 'optional bool',
'uri' => 'optional string',
- 'sshUser' => 'optional string',
- 'sshKey' => 'optional string',
- 'sshKeyFile' => 'optional string',
- 'httpUser' => 'optional string',
- 'httpPassword' => 'optional string',
+ 'credentialPHID' => 'optional string',
'localPath' => 'optional string',
'svnSubpath' => 'optional string',
'branchFilter' => 'optional list',
@@ -100,6 +96,8 @@ final class ConduitAPI_repository_create_Method
}
$repository->setVersionControlSystem($map[$vcs]);
+ $repository->setCredentialPHID($request->getValue('credentialPHID'));
+
$details = array(
'encoding' => $request->getValue('encoding'),
'description' => $request->getValue('description'),
@@ -114,9 +112,6 @@ final class ConduitAPI_repository_create_Method
true),
'pull-frequency' => $request->getValue('pullFrequency'),
'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),
'svn-subpath' => $request->getValue('svnSubpath'),
'disable-autoclose' => !$request->getValue('autocloseEnabled', true),
diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php
index cf28febdf0..e534402391 100644
--- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php
+++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php
@@ -29,6 +29,7 @@ final class PhabricatorRepositoryEditor
$types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
$types[] = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
$types[] = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY;
+ $types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
@@ -67,16 +68,6 @@ final class PhabricatorRepositoryEditor
return (int)!$object->getDetail('disable-autoclose');
case PhabricatorRepositoryTransaction::TYPE_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:
return $object->getDetail('local-path');
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
@@ -87,6 +78,8 @@ final class PhabricatorRepositoryEditor
return $object->getServeOverSSH();
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
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_SSH:
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
+ case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
return $xaction->getNewValue();
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
@@ -168,21 +162,6 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
$object->setDetail('remote-uri', $xaction->getNewValue());
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:
$object->setDetail('local-path', $xaction->getNewValue());
break;
@@ -194,6 +173,8 @@ final class PhabricatorRepositoryEditor
return $object->setServeOverSSH($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
return $object->setPushPolicy($xaction->getNewValue());
+ case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
+ return $object->setCredentialPHID($xaction->getNewValue());
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
// 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
@@ -221,7 +202,32 @@ final class PhabricatorRepositoryEditor
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
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(
@@ -278,6 +284,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH:
case PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY:
+ case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL:
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php
index c77a6a62c7..af00bb58e8 100644
--- a/src/applications/repository/storage/PhabricatorRepository.php
+++ b/src/applications/repository/storage/PhabricatorRepository.php
@@ -39,8 +39,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
protected $versionControlSystem;
protected $details = array();
-
- private $sshKeyfile;
+ protected $credentialPHID;
private $commitCount = self::ATTACHABLE;
private $mostRecentCommit = self::ATTACHABLE;
@@ -366,31 +365,29 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
switch ($this->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
- if ($this->shouldUseHTTP()) {
- $pattern =
- "svn ".
- "--non-interactive ".
- "--no-auth-cache ".
- "--trust-server-cert ".
- "--username %P ".
- "--password %P ".
- $pattern;
- array_unshift(
- $args,
- new PhutilOpaqueEnvelope($this->getDetail('http-login')),
- new PhutilOpaqueEnvelope($this->getDetail('http-pass')));
- } else if ($this->shouldUseSVNProtocol()) {
- $pattern =
- "svn ".
- "--non-interactive ".
- "--no-auth-cache ".
- "--username %P ".
- "--password %P ".
- $pattern;
- array_unshift(
- $args,
- new PhutilOpaqueEnvelope($this->getDetail('http-login')),
- new PhutilOpaqueEnvelope($this->getDetail('http-pass')));
+ if ($this->shouldUseHTTP() || $this->shouldUseSVNProtocol()) {
+ $flags = array();
+ $flag_args = array();
+ $flags[] = '--non-interactive';
+ $flags[] = '--no-auth-cache';
+ if ($this->shouldUseHTTP()) {
+ $flags[] = '--trust-server-cert';
+ }
+
+ $credential_phid = $this->getCredentialPHID();
+ if ($credential_phid) {
+ $key = PassphrasePasswordKey::loadFromPHID(
+ $credential_phid,
+ PhabricatorUser::getOmnipotentUser());
+ $flags[] = '--username %P';
+ $flags[] = '--password %P';
+ $flag_args[] = $key->getUsernameEnvelope();
+ $flag_args[] = $key->getPasswordEnvelope();
+ }
+
+ $flags = implode(' ', $flags);
+ $pattern = "svn {$flags} {$pattern}";
+ $args = array_mergev(array($flag_args, $args));
} else {
$pattern = "svn --non-interactive {$pattern}";
}
@@ -687,11 +684,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
}
$protocol = $this->getRemoteProtocol();
- if ($protocol == 'http' || $protocol == 'https') {
- return (bool)$this->getDetail('http-login');
- } else {
- return false;
- }
+ return ($protocol == 'http' || $protocol == 'https');
}
@@ -708,11 +701,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
}
$protocol = $this->getRemoteProtocol();
- if ($protocol == 'svn') {
- return (bool)$this->getDetail('http-login');
- } else {
- return false;
- }
+ return ($protocol == 'svn');
}
diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php
index e40c139cf4..84c74276ab 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php
@@ -16,16 +16,19 @@ final class PhabricatorRepositoryTransaction
const TYPE_NOTIFY = 'repo:notify';
const TYPE_AUTOCLOSE = 'repo:autoclose';
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_HOSTING = 'repo:hosting';
const TYPE_PROTOCOL_HTTP = 'repo:serve-http';
const TYPE_PROTOCOL_SSH = 'repo:serve-ssh';
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() {
return 'repository';
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
index 253633fddc..95ffc58695 100644
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -1776,6 +1776,14 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => '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'),
+ ),
);
}
}