1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 02:02:41 +01:00
phorge-phorge/src/applications/diffusion/ssh/DiffusionSSHWorkflow.php

140 lines
3.9 KiB
PHP
Raw Normal View History

<?php
abstract class DiffusionSSHWorkflow extends PhabricatorSSHWorkflow {
private $args;
private $repository;
private $hasWriteAccess;
public function getRepository() {
if (!$this->repository) {
throw new Exception("Call loadRepository() before getRepository()!");
}
return $this->repository;
}
public function getArgs() {
return $this->args;
}
public function getEnvironment() {
$env = array(
DiffusionCommitHookEngine::ENV_USER => $this->getUser()->getUsername(),
DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL => 'ssh',
);
$ssh_client = getenv('SSH_CLIENT');
if ($ssh_client) {
// This has the format "<ip> <remote-port> <local-port>". Grab the IP.
$remote_address = head(explode(' ', $ssh_client));
$env[DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS] = $remote_address;
}
return $env;
}
abstract protected function executeRepositoryOperations();
protected function writeError($message) {
$this->getErrorChannel()->write($message);
return $this;
}
final public function execute(PhutilArgumentParser $args) {
$this->args = $args;
try {
return $this->executeRepositoryOperations();
} catch (Exception $ex) {
$this->writeError(get_class($ex).': '.$ex->getMessage());
return 1;
}
}
protected function loadRepository($path) {
$viewer = $this->getUser();
$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));
}
switch ($repository->getServeOverSSH()) {
case PhabricatorRepository::SERVE_READONLY:
case PhabricatorRepository::SERVE_READWRITE:
// If we have read or read/write access, proceed for now. We will
// check write access when the user actually issues a write command.
break;
case PhabricatorRepository::SERVE_OFF:
default:
throw new Exception(
pht('This repository is not available over SSH.'));
}
$this->repository = $repository;
return $repository;
}
protected function requireWriteAccess($protocol_command = null) {
if ($this->hasWriteAccess === true) {
return;
}
$repository = $this->getRepository();
$viewer = $this->getUser();
switch ($repository->getServeOverSSH()) {
case PhabricatorRepository::SERVE_READONLY:
if ($protocol_command !== null) {
throw new Exception(
pht(
'This repository is read-only over SSH (tried to execute '.
'protocol command "%s").',
$protocol_command));
} else {
throw new Exception(
pht('This repository is read-only over SSH.'));
}
break;
case PhabricatorRepository::SERVE_READWRITE:
$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:
// This shouldn't be reachable because we don't get this far if the
// repository isn't enabled, but kick them out anyway.
throw new Exception(
pht('This repository is not available over SSH.'));
}
$this->hasWriteAccess = true;
return $this->hasWriteAccess;
}
}