1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 08:42: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:
epriestley 2013-10-26 10:46:09 -07:00
parent 888b3839e7
commit bb4904553f
7 changed files with 183 additions and 2 deletions

View file

@ -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);
} }

View file

@ -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',

View file

@ -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())

View file

@ -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'));
}
}

View 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");
}
}

View 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;
}
}

View file

@ -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;
}
} }