@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+183 -2
+11 -1
scripts/ssh/ssh-exec.php
··· 61 61 62 62 $workflows = array( 63 63 new ConduitSSHWorkflow(), 64 + 65 + new DiffusionSSHGitUploadPackWorkflow(), 64 66 ); 65 67 66 68 $workflow_names = mpull($workflows, 'getName', 'getName'); ··· 81 83 throw new Exception("Unable to open stdout."); 82 84 } 83 85 86 + $sock_stderr = fopen('php://stderr', 'w'); 87 + if (!$sock_stderr) { 88 + throw new Exception("Unable to open stderr."); 89 + } 90 + 84 91 $socket_channel = new PhutilSocketChannel( 85 92 $sock_stdin, 86 93 $sock_stdout); 94 + $error_channel = new PhutilSocketChannel(null, $sock_stderr); 87 95 $metrics_channel = new PhutilMetricsChannel($socket_channel); 88 96 $workflow->setIOChannel($metrics_channel); 97 + $workflow->setErrorChannel($error_channel); 89 98 90 99 $err = $workflow->execute($original_args); 91 100 92 101 $metrics_channel->flush(); 102 + $error_channel->flush(); 93 103 } catch (Exception $ex) { 94 - echo "phabricator-ssh-exec: ".$ex->getMessage()."\n"; 104 + fwrite(STDERR, "phabricator-ssh-exec: ".$ex->getMessage()."\n"); 95 105 exit(1); 96 106 }
+6
src/__phutil_library_map__.php
··· 526 526 'DiffusionRepositoryPath' => 'applications/diffusion/data/DiffusionRepositoryPath.php', 527 527 'DiffusionRepositoryTag' => 'applications/diffusion/data/DiffusionRepositoryTag.php', 528 528 'DiffusionRequest' => 'applications/diffusion/request/DiffusionRequest.php', 529 + 'DiffusionSSHGitUploadPackWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php', 530 + 'DiffusionSSHGitWorkflow' => 'applications/diffusion/ssh/DiffusionSSHGitWorkflow.php', 531 + 'DiffusionSSHWorkflow' => 'applications/diffusion/ssh/DiffusionSSHWorkflow.php', 529 532 'DiffusionSetupException' => 'applications/diffusion/exception/DiffusionSetupException.php', 530 533 'DiffusionStableCommitNameQuery' => 'applications/diffusion/query/stablecommitname/DiffusionStableCommitNameQuery.php', 531 534 'DiffusionSvnCommitParentsQuery' => 'applications/diffusion/query/parents/DiffusionSvnCommitParentsQuery.php', ··· 2711 2714 0 => 'DiffusionController', 2712 2715 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 2713 2716 ), 2717 + 'DiffusionSSHGitUploadPackWorkflow' => 'DiffusionSSHGitWorkflow', 2718 + 'DiffusionSSHGitWorkflow' => 'DiffusionSSHWorkflow', 2719 + 'DiffusionSSHWorkflow' => 'PhabricatorSSHWorkflow', 2714 2720 'DiffusionSetupException' => 'AphrontUsageException', 2715 2721 'DiffusionStableCommitNameQuery' => 'DiffusionQuery', 2716 2722 'DiffusionSvnCommitParentsQuery' => 'DiffusionCommitParentsQuery',
+25 -1
src/applications/diffusion/controller/DiffusionRepositoryEditHostingController.php
··· 134 134 135 135 if ($request->isFormPost()) { 136 136 $v_http_mode = $request->getStr('http'); 137 - $v_ssh_mode = PhabricatorRepository::SERVE_OFF; 137 + $v_ssh_mode = $request->getStr('ssh'); 138 138 139 139 $xactions = array(); 140 140 $template = id(new PhabricatorRepositoryTransaction()); ··· 176 176 'writes.'); 177 177 } 178 178 179 + $ssh_control = 180 + id(new AphrontFormRadioButtonControl()) 181 + ->setName('ssh') 182 + ->setLabel(pht('SSH')) 183 + ->setValue($v_ssh_mode) 184 + ->addButton( 185 + PhabricatorRepository::SERVE_OFF, 186 + PhabricatorRepository::getProtocolAvailabilityName( 187 + PhabricatorRepository::SERVE_OFF), 188 + pht('Phabricator will not serve this repository.')) 189 + ->addButton( 190 + PhabricatorRepository::SERVE_READONLY, 191 + PhabricatorRepository::getProtocolAvailabilityName( 192 + PhabricatorRepository::SERVE_READONLY), 193 + pht('Phabricator will serve a read-only copy of this repository.')) 194 + ->addButton( 195 + PhabricatorRepository::SERVE_READWRITE, 196 + PhabricatorRepository::getProtocolAvailabilityName( 197 + PhabricatorRepository::SERVE_READWRITE), 198 + $rw_message, 199 + $repository->isHosted() ? null : 'disabled', 200 + $repository->isHosted() ? null : true); 201 + 179 202 $http_control = 180 203 id(new AphrontFormRadioButtonControl()) 181 204 ->setName('http') ··· 205 228 pht( 206 229 'Phabricator can serve repositories over various protocols. You can '. 207 230 'configure server protocols here.')) 231 + ->appendChild($ssh_control) 208 232 ->appendChild($http_control) 209 233 ->appendChild( 210 234 id(new AphrontFormSubmitControl())
+26
src/applications/diffusion/ssh/DiffusionSSHGitUploadPackWorkflow.php
··· 1 + <?php 2 + 3 + final class DiffusionSSHGitUploadPackWorkflow 4 + extends DiffusionSSHGitWorkflow { 5 + 6 + public function didConstruct() { 7 + $this->setName('git-upload-pack'); 8 + $this->setArguments( 9 + array( 10 + array( 11 + 'name' => 'dir', 12 + 'wildcard' => true, 13 + ), 14 + )); 15 + } 16 + 17 + public function isReadOnly() { 18 + return true; 19 + } 20 + 21 + public function getRequestPath() { 22 + $args = $this->getArgs(); 23 + return head($args->getArg('dir')); 24 + } 25 + 26 + }
+10
src/applications/diffusion/ssh/DiffusionSSHGitWorkflow.php
··· 1 + <?php 2 + 3 + abstract class DiffusionSSHGitWorkflow extends DiffusionSSHWorkflow { 4 + 5 + protected function writeError($message) { 6 + // Git assumes we'll add our own newlines. 7 + return parent::writeError($message."\n"); 8 + } 9 + 10 + }
+90
src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
··· 1 + <?php 2 + 3 + abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow { 4 + 5 + private $args; 6 + 7 + public function getArgs() { 8 + return $this->args; 9 + } 10 + 11 + abstract protected function isReadOnly(); 12 + abstract protected function getRequestPath(); 13 + protected function writeError($message) { 14 + $this->getErrorChannel()->write($message); 15 + return $this; 16 + } 17 + 18 + final public function execute(PhutilArgumentParser $args) { 19 + $this->args = $args; 20 + 21 + try { 22 + $repository = $this->loadRepository(); 23 + 24 + throw new Exception("TODO: Implement serve over SSH."); 25 + 26 + } catch (Exception $ex) { 27 + $this->writeError(get_class($ex).': '.$ex->getMessage()); 28 + return 1; 29 + } 30 + 31 + return 0; 32 + } 33 + 34 + private function loadRepository() { 35 + $viewer = $this->getUser(); 36 + $path = $this->getRequestPath(); 37 + 38 + $regex = '@^/?diffusion/(?P<callsign>[A-Z]+)(?:/|$)@'; 39 + $matches = null; 40 + if (!preg_match($regex, $path, $matches)) { 41 + throw new Exception( 42 + pht( 43 + 'Unrecognized repository path "%s". Expected a path like '. 44 + '"%s".', 45 + $path, 46 + "/diffusion/X/")); 47 + } 48 + 49 + $callsign = $matches[1]; 50 + $repository = id(new PhabricatorRepositoryQuery()) 51 + ->setViewer($viewer) 52 + ->withCallsigns(array($callsign)) 53 + ->executeOne(); 54 + 55 + if (!$repository) { 56 + throw new Exception( 57 + pht('No repository "%s" exists!', $callsign)); 58 + } 59 + 60 + $is_push = !$this->isReadOnly(); 61 + 62 + switch ($repository->getServeOverSSH()) { 63 + case PhabricatorRepository::SERVE_READONLY: 64 + if ($is_push) { 65 + throw new Exception( 66 + pht('This repository is read-only over SSH.')); 67 + } 68 + break; 69 + case PhabricatorRepository::SERVE_READWRITE: 70 + if ($is_push) { 71 + $can_push = PhabricatorPolicyFilter::hasCapability( 72 + $viewer, 73 + $repository, 74 + DiffusionCapabilityPush::CAPABILITY); 75 + if (!$can_push) { 76 + throw new Exception( 77 + pht('You do not have permission to push to this repository.')); 78 + } 79 + } 80 + break; 81 + case PhabricatorRepository::SERVE_OFF: 82 + default: 83 + throw new Exception( 84 + pht('This repository is not available over SSH.')); 85 + } 86 + 87 + return $repository; 88 + } 89 + 90 + }
+15
src/infrastructure/ssh/PhabricatorSSHWorkflow.php
··· 4 4 5 5 private $user; 6 6 private $iochannel; 7 + private $errorChannel; 8 + 9 + public function setErrorChannel(PhutilChannel $error_channel) { 10 + $this->errorChannel = $error_channel; 11 + return $this; 12 + } 13 + 14 + public function getErrorChannel() { 15 + return $this->errorChannel; 16 + } 7 17 8 18 public function setUser(PhabricatorUser $user) { 9 19 $this->user = $user; ··· 36 46 } 37 47 } 38 48 return $channel->read(); 49 + } 50 + 51 + public function writeIO($data) { 52 + $this->getIOChannel()->write($data); 53 + return $this; 39 54 } 40 55 41 56 }