mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-29 18:22:41 +01:00
Route some VCS connections over SSH
Summary: - Add web UI for configuring SSH hosting. - Route git reads (`git-upload-pack` over SSH). Test Plan: >>> orbital ~ $ git clone ssh://127.0.0.1/ Cloning into '127.0.0.1'... Exception: Unrecognized repository path "/". Expected a path like "/diffusion/X/". fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. >>> orbital ~ $ git clone ssh://127.0.0.1/diffusion/X/ Cloning into 'X'... Exception: No repository "X" exists! fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. >>> orbital ~ $ git clone ssh://127.0.0.1/diffusion/MT/ Cloning into 'MT'... Exception: This repository is not available over SSH. fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. >>> orbital ~ $ git clone ssh://127.0.0.1/diffusion/P/ Cloning into 'P'... Exception: TODO: Implement serve over SSH. fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. Reviewers: btrahan Reviewed By: btrahan CC: hach-que, aran Maniphest Tasks: T2230 Differential Revision: https://secure.phabricator.com/D7421
This commit is contained in:
parent
888b3839e7
commit
bb4904553f
7 changed files with 183 additions and 2 deletions
|
@ -61,6 +61,8 @@ try {
|
||||||
|
|
||||||
$workflows = array(
|
$workflows = array(
|
||||||
new ConduitSSHWorkflow(),
|
new ConduitSSHWorkflow(),
|
||||||
|
|
||||||
|
new DiffusionSSHGitUploadPackWorkflow(),
|
||||||
);
|
);
|
||||||
|
|
||||||
$workflow_names = mpull($workflows, 'getName', 'getName');
|
$workflow_names = mpull($workflows, 'getName', 'getName');
|
||||||
|
@ -81,16 +83,24 @@ try {
|
||||||
throw new Exception("Unable to open stdout.");
|
throw new Exception("Unable to open stdout.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$sock_stderr = fopen('php://stderr', 'w');
|
||||||
|
if (!$sock_stderr) {
|
||||||
|
throw new Exception("Unable to open stderr.");
|
||||||
|
}
|
||||||
|
|
||||||
$socket_channel = new PhutilSocketChannel(
|
$socket_channel = new PhutilSocketChannel(
|
||||||
$sock_stdin,
|
$sock_stdin,
|
||||||
$sock_stdout);
|
$sock_stdout);
|
||||||
|
$error_channel = new PhutilSocketChannel(null, $sock_stderr);
|
||||||
$metrics_channel = new PhutilMetricsChannel($socket_channel);
|
$metrics_channel = new PhutilMetricsChannel($socket_channel);
|
||||||
$workflow->setIOChannel($metrics_channel);
|
$workflow->setIOChannel($metrics_channel);
|
||||||
|
$workflow->setErrorChannel($error_channel);
|
||||||
|
|
||||||
$err = $workflow->execute($original_args);
|
$err = $workflow->execute($original_args);
|
||||||
|
|
||||||
$metrics_channel->flush();
|
$metrics_channel->flush();
|
||||||
|
$error_channel->flush();
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
echo "phabricator-ssh-exec: ".$ex->getMessage()."\n";
|
fwrite(STDERR, "phabricator-ssh-exec: ".$ex->getMessage()."\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -526,6 +526,9 @@ phutil_register_library_map(array(
|
||||||
'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
|
'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
|
||||||
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
|
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php',
|
||||||
'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php',
|
'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php',
|
||||||
|
'DiffusionSSHGitUploadPackWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php',
|
||||||
|
'DiffusionSSHGitWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitWorkflow.php',
|
||||||
|
'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php',
|
||||||
'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
|
'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php',
|
||||||
'DiffusionStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionStableCommitNameQuery.php',
|
'DiffusionStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionStableCommitNameQuery.php',
|
||||||
'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php',
|
'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php',
|
||||||
|
@ -2711,6 +2714,9 @@ phutil_register_library_map(array(
|
||||||
0 => 'DiffusionController',
|
0 => 'DiffusionController',
|
||||||
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
|
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
|
||||||
),
|
),
|
||||||
|
'DiffusionSSHGitUploadPackWorkflow' => 'DiffusionSSHGitWorkflow',
|
||||||
|
'DiffusionSSHGitWorkflow' => 'DiffusionSSHWorkflow',
|
||||||
|
'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow',
|
||||||
'DiffusionSetupException' => 'AphrontUsageException',
|
'DiffusionSetupException' => 'AphrontUsageException',
|
||||||
'DiffusionStableCommitNameQuery' => 'DiffusionQuery',
|
'DiffusionStableCommitNameQuery' => 'DiffusionQuery',
|
||||||
'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
||||||
|
|
|
@ -134,7 +134,7 @@ final class DiffusionRepositoryEditHostingController
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
$v_http_mode = $request->getStr('http');
|
$v_http_mode = $request->getStr('http');
|
||||||
$v_ssh_mode = PhabricatorRepository::SERVE_OFF;
|
$v_ssh_mode = $request->getStr('ssh');
|
||||||
|
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
$template = id(new PhabricatorRepositoryTransaction());
|
$template = id(new PhabricatorRepositoryTransaction());
|
||||||
|
@ -176,6 +176,29 @@ final class DiffusionRepositoryEditHostingController
|
||||||
'writes.');
|
'writes.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ssh_control =
|
||||||
|
id(new AphrontFormRadioButtonControl())
|
||||||
|
->setName('ssh')
|
||||||
|
->setLabel(pht('SSH'))
|
||||||
|
->setValue($v_ssh_mode)
|
||||||
|
->addButton(
|
||||||
|
PhabricatorRepository::SERVE_OFF,
|
||||||
|
PhabricatorRepository::getProtocolAvailabilityName(
|
||||||
|
PhabricatorRepository::SERVE_OFF),
|
||||||
|
pht('Phabricator will not serve this repository.'))
|
||||||
|
->addButton(
|
||||||
|
PhabricatorRepository::SERVE_READONLY,
|
||||||
|
PhabricatorRepository::getProtocolAvailabilityName(
|
||||||
|
PhabricatorRepository::SERVE_READONLY),
|
||||||
|
pht('Phabricator will serve a read-only copy of this repository.'))
|
||||||
|
->addButton(
|
||||||
|
PhabricatorRepository::SERVE_READWRITE,
|
||||||
|
PhabricatorRepository::getProtocolAvailabilityName(
|
||||||
|
PhabricatorRepository::SERVE_READWRITE),
|
||||||
|
$rw_message,
|
||||||
|
$repository->isHosted() ? null : 'disabled',
|
||||||
|
$repository->isHosted() ? null : true);
|
||||||
|
|
||||||
$http_control =
|
$http_control =
|
||||||
id(new AphrontFormRadioButtonControl())
|
id(new AphrontFormRadioButtonControl())
|
||||||
->setName('http')
|
->setName('http')
|
||||||
|
@ -205,6 +228,7 @@ final class DiffusionRepositoryEditHostingController
|
||||||
pht(
|
pht(
|
||||||
'Phabricator can serve repositories over various protocols. You can '.
|
'Phabricator can serve repositories over various protocols. You can '.
|
||||||
'configure server protocols here.'))
|
'configure server protocols here.'))
|
||||||
|
->appendChild($ssh_control)
|
||||||
->appendChild($http_control)
|
->appendChild($http_control)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DiffusionSSHGitUploadPackWorkflow
|
||||||
|
extends DiffusionSSHGitWorkflow {
|
||||||
|
|
||||||
|
public function didConstruct() {
|
||||||
|
$this->setName('git-upload-pack');
|
||||||
|
$this->setArguments(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'dir',
|
||||||
|
'wildcard' => true,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isReadOnly() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequestPath() {
|
||||||
|
$args = $this->getArgs();
|
||||||
|
return head($args->getArg('dir'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
src/applications/diffusion/ssh/DiffusionSSHGitWorkflow.php
Normal file
10
src/applications/diffusion/ssh/DiffusionSSHGitWorkflow.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class DiffusionSSHGitWorkflow extends DiffusionSSHWorkflow {
|
||||||
|
|
||||||
|
protected function writeError($message) {
|
||||||
|
// Git assumes we'll add our own newlines.
|
||||||
|
return parent::writeError($message."\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
90
src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
Normal file
90
src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
|
||||||
|
|
||||||
|
private $args;
|
||||||
|
|
||||||
|
public function getArgs() {
|
||||||
|
return $this->args;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function isReadOnly();
|
||||||
|
abstract protected function getRequestPath();
|
||||||
|
protected function writeError($message) {
|
||||||
|
$this->getErrorChannel()->write($message);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function execute(PhutilArgumentParser $args) {
|
||||||
|
$this->args = $args;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$repository = $this->loadRepository();
|
||||||
|
|
||||||
|
throw new Exception("TODO: Implement serve over SSH.");
|
||||||
|
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$this->writeError(get_class($ex).': '.$ex->getMessage());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadRepository() {
|
||||||
|
$viewer = $this->getUser();
|
||||||
|
$path = $this->getRequestPath();
|
||||||
|
|
||||||
|
$regex = '@^/?diffusion/(?P<callsign>[A-Z]+)(?:/|$)@';
|
||||||
|
$matches = null;
|
||||||
|
if (!preg_match($regex, $path, $matches)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Unrecognized repository path "%s". Expected a path like '.
|
||||||
|
'"%s".',
|
||||||
|
$path,
|
||||||
|
"/diffusion/X/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$callsign = $matches[1];
|
||||||
|
$repository = id(new PhabricatorRepositoryQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withCallsigns(array($callsign))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
if (!$repository) {
|
||||||
|
throw new Exception(
|
||||||
|
pht('No repository "%s" exists!', $callsign));
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_push = !$this->isReadOnly();
|
||||||
|
|
||||||
|
switch ($repository->getServeOverSSH()) {
|
||||||
|
case PhabricatorRepository::SERVE_READONLY:
|
||||||
|
if ($is_push) {
|
||||||
|
throw new Exception(
|
||||||
|
pht('This repository is read-only over SSH.'));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PhabricatorRepository::SERVE_READWRITE:
|
||||||
|
if ($is_push) {
|
||||||
|
$can_push = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$viewer,
|
||||||
|
$repository,
|
||||||
|
DiffusionCapabilityPush::CAPABILITY);
|
||||||
|
if (!$can_push) {
|
||||||
|
throw new Exception(
|
||||||
|
pht('You do not have permission to push to this repository.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PhabricatorRepository::SERVE_OFF:
|
||||||
|
default:
|
||||||
|
throw new Exception(
|
||||||
|
pht('This repository is not available over SSH.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,16 @@ abstract class PhabricatorSSHWorkflow extends PhutilArgumentWorkflow {
|
||||||
|
|
||||||
private $user;
|
private $user;
|
||||||
private $iochannel;
|
private $iochannel;
|
||||||
|
private $errorChannel;
|
||||||
|
|
||||||
|
public function setErrorChannel(PhutilChannel $error_channel) {
|
||||||
|
$this->errorChannel = $error_channel;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getErrorChannel() {
|
||||||
|
return $this->errorChannel;
|
||||||
|
}
|
||||||
|
|
||||||
public function setUser(PhabricatorUser $user) {
|
public function setUser(PhabricatorUser $user) {
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
|
@ -38,4 +48,9 @@ abstract class PhabricatorSSHWorkflow extends PhutilArgumentWorkflow {
|
||||||
return $channel->read();
|
return $channel->read();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function writeIO($data) {
|
||||||
|
$this->getIOChannel()->write($data);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue