mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52: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(
|
||||
new ConduitSSHWorkflow(),
|
||||
|
||||
new DiffusionSSHGitUploadPackWorkflow(),
|
||||
);
|
||||
|
||||
$workflow_names = mpull($workflows, 'getName', 'getName');
|
||||
|
@ -81,16 +83,24 @@ try {
|
|||
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(
|
||||
$sock_stdin,
|
||||
$sock_stdout);
|
||||
$error_channel = new PhutilSocketChannel(null, $sock_stderr);
|
||||
$metrics_channel = new PhutilMetricsChannel($socket_channel);
|
||||
$workflow->setIOChannel($metrics_channel);
|
||||
$workflow->setErrorChannel($error_channel);
|
||||
|
||||
$err = $workflow->execute($original_args);
|
||||
|
||||
$metrics_channel->flush();
|
||||
$error_channel->flush();
|
||||
} catch (Exception $ex) {
|
||||
echo "phabricator-ssh-exec: ".$ex->getMessage()."\n";
|
||||
fwrite(STDERR, "phabricator-ssh-exec: ".$ex->getMessage()."\n");
|
||||
exit(1);
|
||||
}
|
||||
|
|
|
@ -526,6 +526,9 @@ phutil_register_library_map(array(
|
|||
'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php',
|
||||
'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.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',
|
||||
'DiffusionStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionStableCommitNameQuery.php',
|
||||
'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php',
|
||||
|
@ -2711,6 +2714,9 @@ phutil_register_library_map(array(
|
|||
0 => 'DiffusionController',
|
||||
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
|
||||
),
|
||||
'DiffusionSSHGitUploadPackWorkflow' => 'DiffusionSSHGitWorkflow',
|
||||
'DiffusionSSHGitWorkflow' => 'DiffusionSSHWorkflow',
|
||||
'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow',
|
||||
'DiffusionSetupException' => 'AphrontUsageException',
|
||||
'DiffusionStableCommitNameQuery' => 'DiffusionQuery',
|
||||
'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery',
|
||||
|
|
|
@ -134,7 +134,7 @@ final class DiffusionRepositoryEditHostingController
|
|||
|
||||
if ($request->isFormPost()) {
|
||||
$v_http_mode = $request->getStr('http');
|
||||
$v_ssh_mode = PhabricatorRepository::SERVE_OFF;
|
||||
$v_ssh_mode = $request->getStr('ssh');
|
||||
|
||||
$xactions = array();
|
||||
$template = id(new PhabricatorRepositoryTransaction());
|
||||
|
@ -176,6 +176,29 @@ final class DiffusionRepositoryEditHostingController
|
|||
'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 =
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->setName('http')
|
||||
|
@ -205,6 +228,7 @@ final class DiffusionRepositoryEditHostingController
|
|||
pht(
|
||||
'Phabricator can serve repositories over various protocols. You can '.
|
||||
'configure server protocols here.'))
|
||||
->appendChild($ssh_control)
|
||||
->appendChild($http_control)
|
||||
->appendChild(
|
||||
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 $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) {
|
||||
$this->user = $user;
|
||||
|
@ -38,4 +48,9 @@ abstract class PhabricatorSSHWorkflow extends PhutilArgumentWorkflow {
|
|||
return $channel->read();
|
||||
}
|
||||
|
||||
public function writeIO($data) {
|
||||
$this->getIOChannel()->write($data);
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue