Skip to content

Commit d846f65

Browse files
author
epriestley
committed
Fix some repository URI handling issues in Git and Mercurial
Summary: See <https://github.com/facebook/phabricator/issues/467>. @dctrwatson also ran into an issue where we were trying to `setPass()` a GitURI. - For Git and Mercurial, properly generate credential URIs where relevant. - Don't try to `setPass()` on Git-style URIs. This isn't perfect but should clean things up a bit. Test Plan: Added unit tests. Lots of `grep`. Reviewers: btrahan Reviewed By: btrahan CC: dctrwatson, aran Differential Revision: https://secure.phabricator.com/D7759
1 parent 1d9bf6f commit d846f65

7 files changed

+145
-113
lines changed
Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,6 @@
11
#!/usr/bin/env php
22
<?php
33

4-
$root = dirname(dirname(dirname(__FILE__)));
5-
require_once $root.'/scripts/__init_script__.php';
6-
7-
if (empty($argv[1])) {
8-
echo "usage: test_connection.php <repository_callsign>\n";
9-
exit(1);
10-
}
11-
12-
echo phutil_console_wrap(
13-
phutil_console_format(
14-
'This script will test that you have configured valid credentials for '.
15-
'access to a repository, so the Phabricator daemons can pull from it. '.
16-
'You should run this as the **same user you will run the daemons as**, '.
17-
'from the **same machine they will run from**. Doing this will help '.
18-
'detect various problems with your configuration, such as SSH issues.'));
19-
20-
list($whoami) = execx('whoami');
21-
$whoami = trim($whoami);
22-
23-
$ok = phutil_console_confirm("Do you want to continue as '{$whoami}'?");
24-
if (!$ok) {
25-
die(1);
26-
}
27-
28-
$callsign = $argv[1];
29-
echo "Loading '{$callsign}' repository...\n";
30-
$repository = id(new PhabricatorRepository())->loadOneWhere(
31-
'callsign = %s',
32-
$argv[1]);
33-
if (!$repository) {
34-
throw new Exception("No such repository exists!");
35-
}
36-
37-
$vcs = $repository->getVersionControlSystem();
38-
39-
PhutilServiceProfiler::installEchoListener();
40-
41-
echo phutil_console_format(
42-
"\n".
43-
"**NOTE:** If you are prompted for an SSH password in the next step, the\n".
44-
"daemon won't work because it doesn't have the password and can't respond\n".
45-
"to an interactive prompt. Instead of typing the password, it will hang\n".
46-
"forever when prompted. There are several ways to resolve this:\n\n".
47-
" - Run the daemon inside an ssh-agent session where you have unlocked\n".
48-
" the key (most secure, but most complicated).\n".
49-
" - Generate a new, passwordless certificate for the daemon to use\n".
50-
" (usually quite easy).\n".
51-
" - Remove the passphrase from the key with `ssh-keygen -p`\n".
52-
" (easy, but questionable).");
53-
54-
phutil_console_confirm('Did you read all that?', $default_no = false);
55-
56-
echo "Trying to connect to the remote...\n";
57-
switch ($vcs) {
58-
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
59-
$err = $repository->passthruRemoteCommand(
60-
'--limit 1 log %s',
61-
$repository->getRemoteURI());
62-
break;
63-
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
64-
// Do an ls-remote on a nonexistent ref, which we expect to just return
65-
// nothing.
66-
$err = $repository->passthruRemoteCommand(
67-
'ls-remote %s %s',
68-
$repository->getRemoteURI(),
69-
'just-testing');
70-
break;
71-
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
72-
// TODO: 'hg id' doesn't support --insecure so we can't tell it not to
73-
// spew. If 'hg id' eventually supports --insecure, consider using it.
74-
echo "(It is safe to ignore any 'certificate with fingerprint ... not ".
75-
"verified' warnings, although you may want to configure Mercurial ".
76-
"to recognize the server's fingerprint/certificate.)\n";
77-
$err = $repository->passthruRemoteCommand(
78-
'id --rev tip %s',
79-
$repository->getRemoteURI());
80-
break;
81-
default:
82-
throw new Exception("Unsupported repository type.");
83-
}
84-
85-
if ($err) {
86-
echo phutil_console_format(
87-
"<bg:red>** FAIL **</bg> Connection failed. The credentials for this ".
88-
"repository appear to be incorrectly configured.\n");
89-
exit(1);
90-
} else {
91-
echo phutil_console_format(
92-
"<bg:green>** OKAY **</bg> Connection successful. The credentials for ".
93-
"this repository appear to be correctly configured.\n");
94-
}
4+
echo "This script is obsolete. Use `bin/repository` to manage repositories.\n";
5+
exit(1);
956

src/__phutil_library_map__.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,7 @@
18151815
'PhabricatorRepositoryTransaction' => 'applications/repository/storage/PhabricatorRepositoryTransaction.php',
18161816
'PhabricatorRepositoryTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryTransactionQuery.php',
18171817
'PhabricatorRepositoryType' => 'applications/repository/constants/PhabricatorRepositoryType.php',
1818+
'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php',
18181819
'PhabricatorRepositoryVCSPassword' => 'applications/repository/storage/PhabricatorRepositoryVCSPassword.php',
18191820
'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php',
18201821
'PhabricatorSQLPatchList' => 'infrastructure/storage/patch/PhabricatorSQLPatchList.php',
@@ -4380,6 +4381,7 @@
43804381
'PhabricatorRepositoryTestCase' => 'PhabricatorTestCase',
43814382
'PhabricatorRepositoryTransaction' => 'PhabricatorApplicationTransaction',
43824383
'PhabricatorRepositoryTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
4384+
'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase',
43834385
'PhabricatorRepositoryVCSPassword' => 'PhabricatorRepositoryDAO',
43844386
'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine',
43854387
'PhabricatorSSHLog' => 'Phobject',

src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ protected function executeAllocateResource(DrydockLease $lease) {
6363

6464
$cmd = $host_lease->getInterface('command');
6565
$cmd->execx(
66-
'git clone --origin origin %s %s',
67-
$repository->getRemoteURI(),
66+
'git clone --origin origin %P %s',
67+
$repository->getRemoteURIEnvelope(),
6868
$path);
6969

7070
$this->log(pht('Complete.'));

src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -842,8 +842,8 @@ private function pushToMirrors(PhabricatorRepository $repository) {
842842
}
843843

844844
$future = $proxy->getRemoteCommandFuture(
845-
'push --verbose --mirror -- %s',
846-
$proxy->getRemoteURI());
845+
'push --verbose --mirror -- %P',
846+
$proxy->getRemoteURIEnvelope());
847847

848848
$future
849849
->setCWD($proxy->getLocalPath())

src/applications/repository/engine/PhabricatorRepositoryPullEngine.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,8 @@ private function executeGitCreate() {
188188
$path);
189189
} else {
190190
$repository->execxRemoteCommand(
191-
'clone --bare -- %s %s',
192-
$repository->getRemoteURI(),
191+
'clone --bare -- %P %s',
192+
$repository->getRemoteURIEnvelope(),
193193
$path);
194194
}
195195
}
@@ -337,8 +337,8 @@ private function executeMercurialCreate() {
337337
$path);
338338
} else {
339339
$repository->execxRemoteCommand(
340-
'clone --noupdate -- %s %s',
341-
$repository->getRemoteURI(),
340+
'clone --noupdate -- %P %s',
341+
$repository->getRemoteURIEnvelope(),
342342
$path);
343343
}
344344
}

src/applications/repository/storage/PhabricatorRepository.php

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public function toDictionary() {
8181
'callsign' => $this->getCallsign(),
8282
'vcs' => $this->getVersionControlSystem(),
8383
'uri' => PhabricatorEnv::getProductionURI($this->getURI()),
84-
'remoteURI' => (string)$this->getPublicRemoteURI(),
84+
'remoteURI' => (string)$this->getRemoteURI(),
8585
'tracking' => $this->getDetail('tracking-enabled'),
8686
'description' => $this->getDetail('description'),
8787
);
@@ -440,10 +440,6 @@ private function formatLocalCommand(array $args) {
440440
return $args;
441441
}
442442

443-
public function getSSHLogin() {
444-
return $this->getDetail('ssh-login');
445-
}
446-
447443
public function getURI() {
448444
return '/diffusion/'.$this->getCallsign().'/';
449445
}
@@ -568,6 +564,37 @@ public function getRemoteURI() {
568564
}
569565

570566

567+
/**
568+
* Get the remote URI for this repository, including credentials if they're
569+
* used by this repository.
570+
*
571+
* @return PhutilOpaqueEnvelope URI, possibly including credentials.
572+
* @task uri
573+
*/
574+
public function getRemoteURIEnvelope() {
575+
$uri = $this->getRemoteURIObject();
576+
577+
$remote_protocol = $this->getRemoteProtocol();
578+
if ($remote_protocol == 'http' || $remote_protocol == 'https') {
579+
// For SVN, we use `--username` and `--password` flags separately, so
580+
// don't add any credentials here.
581+
if (!$this->isSVN()) {
582+
$credential_phid = $this->getCredentialPHID();
583+
if ($credential_phid) {
584+
$key = PassphrasePasswordKey::loadFromPHID(
585+
$credential_phid,
586+
PhabricatorUser::getOmnipotentUser());
587+
588+
$uri->setUser($key->getUsernameEnvelope()->openEnvelope());
589+
$uri->setPass($key->getPasswordEnvelope()->openEnvelope());
590+
}
591+
}
592+
}
593+
594+
return new PhutilOpaqueEnvelope((string)$uri);
595+
}
596+
597+
571598
/**
572599
* Get the remote URI for this repository, without authentication information.
573600
*
@@ -584,7 +611,12 @@ public function getPublicRemoteURI() {
584611
// password.
585612
if (!$this->shouldUseSSH()) {
586613
$uri->setUser(null);
587-
$uri->setPass(null);
614+
615+
// This might be a Git URI or a normal URI. If it's Git, there's no
616+
// password support.
617+
if ($uri instanceof PhutilURI) {
618+
$uri->setPass(null);
619+
}
588620
}
589621

590622
return (string)$uri;
@@ -629,19 +661,11 @@ public function getRemoteURIObject() {
629661

630662
$uri = new PhutilURI($raw_uri);
631663
if ($uri->getProtocol()) {
632-
if ($this->isSSHProtocol($uri->getProtocol())) {
633-
if ($this->getSSHLogin()) {
634-
$uri->setUser($this->getSSHLogin());
635-
}
636-
}
637664
return $uri;
638665
}
639666

640667
$uri = new PhutilGitURI($raw_uri);
641668
if ($uri->getDomain()) {
642-
if ($this->getSSHLogin()) {
643-
$uri->setUser($this->getSSHLogin());
644-
}
645669
return $uri;
646670
}
647671

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
final class PhabricatorRepositoryURITestCase
4+
extends PhabricatorTestCase {
5+
6+
protected function getPhabricatorTestCaseConfiguration() {
7+
return array(
8+
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
9+
);
10+
}
11+
12+
public function testURIGeneration() {
13+
$svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
14+
$git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
15+
$hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
16+
17+
$user = $this->generateNewTestUser();
18+
19+
$http_secret = id(new PassphraseSecret())->setSecretData('quack')->save();
20+
21+
$http_credential = PassphraseCredential::initializeNewCredential($user)
22+
->setCredentialType(PassphraseCredentialTypePassword::CREDENTIAL_TYPE)
23+
->setProvidesType(PassphraseCredentialTypePassword::PROVIDES_TYPE)
24+
->setUsername('duck')
25+
->setSecretID($http_secret->getID())
26+
->save();
27+
28+
$repo = PhabricatorRepository::initializeNewRepository($user)
29+
->setVersionControlSystem($svn)
30+
->setName('Test Repo')
31+
->setCallsign('TESTREPO')
32+
->setCredentialPHID($http_credential->getPHID())
33+
->save();
34+
35+
// Test HTTP URIs.
36+
37+
$repo->setDetail('remote-uri', 'http://example.com/');
38+
$repo->setVersionControlSystem($svn);
39+
40+
$this->assertEqual('http://example.com/', $repo->getRemoteURI());
41+
$this->assertEqual('http://example.com/', $repo->getPublicRemoteURI());
42+
$this->assertEqual('http://example.com/',
43+
$repo->getRemoteURIEnvelope()->openEnvelope());
44+
45+
$repo->setVersionControlSystem($git);
46+
47+
$this->assertEqual('http://example.com/', $repo->getRemoteURI());
48+
$this->assertEqual('http://example.com/', $repo->getPublicRemoteURI());
49+
$this->assertEqual('http://duck:quack@example.com/',
50+
$repo->getRemoteURIEnvelope()->openEnvelope());
51+
52+
$repo->setVersionControlSystem($hg);
53+
54+
$this->assertEqual('http://example.com/', $repo->getRemoteURI());
55+
$this->assertEqual('http://example.com/', $repo->getPublicRemoteURI());
56+
$this->assertEqual('http://duck:quack@example.com/',
57+
$repo->getRemoteURIEnvelope()->openEnvelope());
58+
59+
// Test SSH URIs.
60+
61+
$repo->setDetail('remote-uri', 'ssh://example.com/');
62+
$repo->setVersionControlSystem($svn);
63+
64+
$this->assertEqual('ssh://example.com/', $repo->getRemoteURI());
65+
$this->assertEqual('ssh://example.com/', $repo->getPublicRemoteURI());
66+
$this->assertEqual('ssh://example.com/',
67+
$repo->getRemoteURIEnvelope()->openEnvelope());
68+
69+
$repo->setVersionControlSystem($git);
70+
71+
$this->assertEqual('ssh://example.com/', $repo->getRemoteURI());
72+
$this->assertEqual('ssh://example.com/', $repo->getPublicRemoteURI());
73+
$this->assertEqual('ssh://example.com/',
74+
$repo->getRemoteURIEnvelope()->openEnvelope());
75+
76+
$repo->setVersionControlSystem($hg);
77+
78+
$this->assertEqual('ssh://example.com/', $repo->getRemoteURI());
79+
$this->assertEqual('ssh://example.com/', $repo->getPublicRemoteURI());
80+
$this->assertEqual('ssh://example.com/',
81+
$repo->getRemoteURIEnvelope()->openEnvelope());
82+
83+
// Test Git URIs.
84+
85+
$repo->setDetail('remote-uri', 'git@example.com:path.git');
86+
$repo->setVersionControlSystem($git);
87+
88+
$this->assertEqual('git@example.com:path.git', $repo->getRemoteURI());
89+
$this->assertEqual('git@example.com:path.git', $repo->getPublicRemoteURI());
90+
$this->assertEqual('git@example.com:path.git',
91+
$repo->getRemoteURIEnvelope()->openEnvelope());
92+
93+
}
94+
95+
}

0 commit comments

Comments
 (0)