mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 20:40:56 +01:00
Implement writes over HTTP for Git.
Summary: Depends on D7642. This updates the authentication logic so that HTTP writes can be made to Git repositories hosted by Phabricator. Test Plan: Set the policy to allow me to push and I was able to. Changed the policy to disallow push and I was no longer able to push. Reviewers: #blessed_reviewers, hach-que Reviewed By: hach-que CC: Korvin, epriestley, aran Maniphest Tasks: T2230 Differential Revision: https://secure.phabricator.com/D7468
This commit is contained in:
parent
0278b15ceb
commit
43fd567ef4
1 changed files with 119 additions and 19 deletions
|
@ -35,6 +35,9 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
} else if ($content_type == 'application/x-git-upload-pack-request') {
|
||||
// We get this for `git-upload-pack`.
|
||||
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
|
||||
} else if ($content_type == 'application/x-git-receive-pack-request') {
|
||||
// We get this for `git-receive-pack`.
|
||||
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
|
||||
} else if ($request->getExists('cmd')) {
|
||||
// Mercurial also sends an Accept header like
|
||||
// "application/mercurial-0.1", and a User-Agent like
|
||||
|
@ -61,16 +64,37 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
|
||||
private function processVCSRequest($callsign) {
|
||||
|
||||
// TODO: Authenticate user.
|
||||
// If authentication credentials have been provided, try to find a user
|
||||
// that actually matches those credentials.
|
||||
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
|
||||
$username = $_SERVER['PHP_AUTH_USER'];
|
||||
$password = new PhutilOpaqueEnvelope($_SERVER['PHP_AUTH_PW']);
|
||||
|
||||
$viewer = new PhabricatorUser();
|
||||
|
||||
$allow_public = PhabricatorEnv::getEnvConfig('policy.allow-public');
|
||||
if (!$allow_public) {
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
$viewer = $this->authenticateHTTPRepositoryUser($username, $password);
|
||||
if (!$viewer) {
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht('You must log in to access repositories.'));
|
||||
pht('Invalid credentials.'));
|
||||
}
|
||||
} else {
|
||||
// User hasn't provided credentials, which means we count them as
|
||||
// being "not logged in".
|
||||
$viewer = new PhabricatorUser();
|
||||
}
|
||||
|
||||
$allow_public = PhabricatorEnv::getEnvConfig('policy.allow-public');
|
||||
$allow_auth = PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
|
||||
if (!$allow_public) {
|
||||
if (!$viewer->isLoggedIn()) {
|
||||
if ($allow_auth) {
|
||||
return new PhabricatorVCSResponse(
|
||||
401,
|
||||
pht('You must log in to access repositories.'));
|
||||
} else {
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht('Public and authenticated HTTP access are both forbidden.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,9 +114,17 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
403,
|
||||
pht('You do not have permission to access this repository.'));
|
||||
} else {
|
||||
return new PhabricatorVCSResponse(
|
||||
401,
|
||||
pht('You must log in to access this repository.'));
|
||||
if ($allow_auth) {
|
||||
return new PhabricatorVCSResponse(
|
||||
401,
|
||||
pht('You must log in to access this repository.'));
|
||||
} else {
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht(
|
||||
'This repository requires authentication, which is forbidden '.
|
||||
'over HTTP.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,9 +156,17 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
403,
|
||||
pht('You do not have permission to push to this repository.'));
|
||||
} else {
|
||||
return new PhabricatorVCSResponse(
|
||||
401,
|
||||
pht('You must log in to push to this repository.'));
|
||||
if ($allow_auth) {
|
||||
return new PhabricatorVCSResponse(
|
||||
401,
|
||||
pht('You must log in to push to this repository.'));
|
||||
} else {
|
||||
return new PhabricatorVCSResponse(
|
||||
403,
|
||||
pht(
|
||||
'Pushing to this repository requires authentication, '.
|
||||
'which is forbidden over HTTP.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -422,7 +462,16 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
// Rebuild the query string to strip `__magic__` parameters and prevent
|
||||
// issues where we might interpret inputs like "service=read&service=write"
|
||||
// differently than the server does and pass it an unsafe command.
|
||||
$query_data = $request->getPassthroughRequestParameters();
|
||||
|
||||
// NOTE: This does not use getPassthroughRequestParameters() because
|
||||
// that code is HTTP-method agnostic and will encode POST data.
|
||||
|
||||
$query_data = $_GET;
|
||||
foreach ($query_data as $key => $value) {
|
||||
if (!strncmp($key, '__', 2)) {
|
||||
unset($query_data[$key]);
|
||||
}
|
||||
}
|
||||
$query_string = http_build_query($query_data, '', '&');
|
||||
|
||||
// We're about to wipe out PATH with the rest of the environment, so
|
||||
|
@ -435,18 +484,31 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
$env = array(
|
||||
'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
|
||||
'QUERY_STRING' => $query_string,
|
||||
'CONTENT_TYPE' => $_SERVER['CONTENT_TYPE'],
|
||||
'REMOTE_USER' => '',
|
||||
'CONTENT_TYPE' => $request->getHTTPHeader('Content-Type'),
|
||||
'HTTP_CONTENT_ENCODING' => $request->getHTTPHeader('Content-Encoding'),
|
||||
'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'],
|
||||
'GIT_PROJECT_ROOT' => $repository_root,
|
||||
'GIT_HTTP_EXPORT_ALL' => '1',
|
||||
'PATH_INFO' => $request_path,
|
||||
|
||||
// TODO: Set these correctly.
|
||||
'REMOTE_USER' => '',
|
||||
// GIT_COMMITTER_NAME
|
||||
// GIT_COMMITTER_EMAIL
|
||||
);
|
||||
|
||||
list($stdout) = id(new ExecFuture('%s', $bin))
|
||||
$input = PhabricatorStartup::getRawInput();
|
||||
|
||||
list($err, $stdout, $stderr) = id(new ExecFuture('%s', $bin))
|
||||
->setEnv($env, true)
|
||||
->write(PhabricatorStartup::getRawInput())
|
||||
->resolvex();
|
||||
->write($input)
|
||||
->resolve();
|
||||
|
||||
if ($err) {
|
||||
return new PhabricatorVCSResponse(
|
||||
500,
|
||||
pht('Error %d: %s', $err, $stderr));
|
||||
}
|
||||
|
||||
return id(new DiffusionGitResponse())->setGitData($stdout);
|
||||
}
|
||||
|
@ -463,5 +525,43 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
->setTitle($title)
|
||||
->appendChild($body);
|
||||
}
|
||||
|
||||
private function authenticateHTTPRepositoryUser(
|
||||
$username,
|
||||
PhutilOpaqueEnvelope $password) {
|
||||
|
||||
if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
|
||||
// No HTTP auth permitted.
|
||||
return null;
|
||||
}
|
||||
|
||||
$user = id(new PhabricatorPeopleQuery())
|
||||
->setViewer(new PhabricatorUser())
|
||||
->withUsernames(array($username))
|
||||
->executeOne();
|
||||
if (!$user) {
|
||||
// Username doesn't match anything.
|
||||
return null;
|
||||
}
|
||||
|
||||
$password_entry = id(new PhabricatorRepositoryVCSPassword())
|
||||
->loadOneWhere('userPHID = %s', $user->getPHID());
|
||||
if (!$password_entry) {
|
||||
// User doesn't have a password set.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$password_entry->comparePassword($password, $user)) {
|
||||
// Password doesn't match.
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($user->getIsDisabled()) {
|
||||
// User is disabled.
|
||||
return null;
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue