1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-04 20:52:43 +01:00
phorge-phorge/src/applications/diffusion/controller/DiffusionServeController.php

1042 lines
31 KiB
PHP
Raw Normal View History

<?php
final class DiffusionServeController extends DiffusionController {
private $serviceViewer;
private $serviceRepository;
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
private $isGitLFSRequest;
private $gitLFSToken;
public function setServiceViewer(PhabricatorUser $viewer) {
$this->getRequest()->setUser($viewer);
$this->serviceViewer = $viewer;
return $this;
}
public function getServiceViewer() {
return $this->serviceViewer;
}
public function setServiceRepository(PhabricatorRepository $repository) {
$this->serviceRepository = $repository;
return $this;
}
public function getServiceRepository() {
return $this->serviceRepository;
}
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
public function getIsGitLFSRequest() {
return $this->isGitLFSRequest;
}
public function getGitLFSToken() {
return $this->gitLFSToken;
}
public function isVCSRequest(AphrontRequest $request) {
$identifier = $this->getRepositoryIdentifierFromRequest($request);
if ($identifier === null) {
return null;
}
$content_type = $request->getHTTPHeader('Content-Type');
$user_agent = idx($_SERVER, 'HTTP_USER_AGENT');
$request_type = $request->getHTTPHeader('X-Phabricator-Request-Type');
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
// This may have a "charset" suffix, so only match the prefix.
$lfs_pattern = '(^application/vnd\\.git-lfs\\+json(;|\z))';
$vcs = null;
if ($request->getExists('service')) {
$service = $request->getStr('service');
// We get this initially for `info/refs`.
// Git also gives us a User-Agent like "git/1.8.2.3".
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
} else if (strncmp($user_agent, 'git/', 4) === 0) {
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
} 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;
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
} else if (preg_match($lfs_pattern, $content_type)) {
// This is a Git LFS HTTP API request.
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$this->isGitLFSRequest = true;
} else if ($request_type == 'git-lfs') {
// This is a Git LFS object content request.
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
$this->isGitLFSRequest = true;
} else if ($request->getExists('cmd')) {
// Mercurial also sends an Accept header like
// "application/mercurial-0.1", and a User-Agent like
// "mercurial/proto-1.0".
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
} else {
// Subversion also sends an initial OPTIONS request (vs GET/POST), and
// has a User-Agent like "SVN/1.8.3 (x86_64-apple-darwin11.4.2)
// serf/1.3.2".
$dav = $request->getHTTPHeader('DAV');
$dav = new PhutilURI($dav);
if ($dav->getDomain() === 'subversion.tigris.org') {
$vcs = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
}
}
return $vcs;
}
public function handleRequest(AphrontRequest $request) {
$service_exception = null;
$response = null;
try {
$response = $this->serveRequest($request);
} catch (Exception $ex) {
$service_exception = $ex;
}
try {
$remote_addr = $request->getRemoteAddress();
$pull_event = id(new PhabricatorRepositoryPullEvent())
->setEpoch(PhabricatorTime::getNow())
->setRemoteAddress($remote_addr)
->setRemoteProtocol('http');
if ($response) {
$pull_event
->setResultType('wild')
->setResultCode($response->getHTTPResponseCode());
if ($response instanceof PhabricatorVCSResponse) {
$pull_event->setProperties(
array(
'response.message' => $response->getMessage(),
));
}
} else {
$pull_event
->setResultType('exception')
->setResultCode(500)
->setProperties(
array(
'exception.class' => get_class($ex),
'exception.message' => $ex->getMessage(),
));
}
$viewer = $this->getServiceViewer();
if ($viewer) {
$pull_event->setPullerPHID($viewer->getPHID());
}
$repository = $this->getServiceRepository();
if ($repository) {
$pull_event->setRepositoryPHID($repository->getPHID());
}
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$pull_event->save();
unset($unguarded);
} catch (Exception $ex) {
if ($service_exception) {
throw $service_exception;
}
throw $ex;
}
if ($service_exception) {
throw $service_exception;
}
return $response;
}
private function serveRequest(AphrontRequest $request) {
$identifier = $this->getRepositoryIdentifierFromRequest($request);
// 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']);
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
// Try Git LFS auth first since we can usually reject it without doing
// any queries, since the username won't match the one we expect or the
// request won't be LFS.
$viewer = $this->authenticateGitLFSUser($username, $password);
// If that failed, try normal auth. Note that we can use normal auth on
// LFS requests, so this isn't strictly an alternative to LFS auth.
if (!$viewer) {
$viewer = $this->authenticateHTTPRepositoryUser($username, $password);
}
if (!$viewer) {
return new PhabricatorVCSResponse(
403,
pht('Invalid credentials.'));
}
} else {
// User hasn't provided credentials, which means we count them as
// being "not logged in".
$viewer = new PhabricatorUser();
}
$this->setServiceViewer($viewer);
$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.'));
}
}
}
try {
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withIdentifiers(array($identifier))
->executeOne();
if (!$repository) {
return new PhabricatorVCSResponse(
404,
pht('No such repository exists.'));
}
} catch (PhabricatorPolicyException $ex) {
if ($viewer->isLoggedIn()) {
return new PhabricatorVCSResponse(
403,
pht('You do not have permission to access this repository.'));
} else {
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.'));
}
}
}
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
$response = $this->validateGitLFSRequest($repository, $viewer);
if ($response) {
return $response;
}
$this->setServiceRepository($repository);
if (!$repository->isTracked()) {
return new PhabricatorVCSResponse(
403,
pht('This repository is inactive.'));
}
$is_push = !$this->isReadOnlyRequest($repository);
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
if ($this->getIsGitLFSRequest() && $this->getGitLFSToken()) {
// We allow git LFS requests over HTTP even if the repository does not
// otherwise support HTTP reads or writes, as long as the user is using a
// token from SSH. If they're using HTTP username + password auth, they
// have to obey the normal HTTP rules.
} else {
switch ($repository->getServeOverHTTP()) {
case PhabricatorRepository::SERVE_READONLY:
if ($is_push) {
return new PhabricatorVCSResponse(
403,
pht('This repository is read-only over HTTP.'));
}
break;
case PhabricatorRepository::SERVE_READWRITE:
// We'll check for push capability below.
break;
case PhabricatorRepository::SERVE_OFF:
default:
return new PhabricatorVCSResponse(
403,
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
pht('This repository is not available over HTTP.'));
}
}
if ($is_push) {
$can_push = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
DiffusionPushCapability::CAPABILITY);
if (!$can_push) {
if ($viewer->isLoggedIn()) {
return new PhabricatorVCSResponse(
403,
pht(
'You do not have permission to push to this '.
'repository.'));
} else {
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.'));
}
}
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
}
}
$vcs_type = $repository->getVersionControlSystem();
$req_type = $this->isVCSRequest($request);
if ($vcs_type != $req_type) {
switch ($req_type) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$result = new PhabricatorVCSResponse(
500,
pht(
'This repository ("%s") is not a Git repository.',
$repository->getDisplayName()));
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$result = new PhabricatorVCSResponse(
500,
pht(
'This repository ("%s") is not a Mercurial repository.',
$repository->getDisplayName()));
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$result = new PhabricatorVCSResponse(
500,
pht(
'This repository ("%s") is not a Subversion repository.',
$repository->getDisplayName()));
break;
default:
$result = new PhabricatorVCSResponse(
500,
pht('Unknown request type.'));
break;
}
} else {
switch ($vcs_type) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$result = $this->serveVCSRequest($repository, $viewer);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$result = new PhabricatorVCSResponse(
500,
pht(
'Phabricator does not support HTTP access to Subversion '.
'repositories.'));
break;
default:
$result = new PhabricatorVCSResponse(
500,
pht('Unknown version control system.'));
break;
}
}
$code = $result->getHTTPResponseCode();
if ($is_push && ($code == 200)) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$repository->writeStatusMessage(
PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE,
PhabricatorRepositoryStatusMessage::CODE_OKAY);
unset($unguarded);
}
return $result;
}
private function serveVCSRequest(
PhabricatorRepository $repository,
PhabricatorUser $viewer) {
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
// We can serve Git LFS requests first, since we don't need to proxy them.
// It's also important that LFS requests never fall through to standard
// service pathways, because that would let you use LFS tokens to read
// normal repository data.
if ($this->getIsGitLFSRequest()) {
return $this->serveGitLFSRequest($repository, $viewer);
}
// If this repository is hosted on a service, we need to proxy the request
// to a host which can serve it.
$is_cluster_request = $this->getRequest()->isProxiedClusterRequest();
$uri = $repository->getAlmanacServiceURI(
$viewer,
$is_cluster_request,
array(
'http',
'https',
));
if ($uri) {
$future = $this->getRequest()->newClusterProxyFuture($uri);
return id(new AphrontHTTPProxyResponse())
->setHTTPFuture($future);
}
// Otherwise, we're going to handle the request locally.
$vcs_type = $repository->getVersionControlSystem();
switch ($vcs_type) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$result = $this->serveGitRequest($repository, $viewer);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$result = $this->serveMercurialRequest($repository, $viewer);
break;
}
return $result;
}
private function isReadOnlyRequest(
PhabricatorRepository $repository) {
$request = $this->getRequest();
$method = $_SERVER['REQUEST_METHOD'];
// TODO: This implementation is safe by default, but very incomplete.
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
// TODO: This doesn't get the right result for Git LFS yet.
switch ($repository->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
$service = $request->getStr('service');
$path = $this->getRequestDirectoryPath($repository);
// NOTE: Service names are the reverse of what you might expect, as they
// are from the point of view of the server. The main read service is
// "git-upload-pack", and the main write service is "git-receive-pack".
if ($method == 'GET' &&
$path == '/info/refs' &&
$service == 'git-upload-pack') {
return true;
}
if ($path == '/git-upload-pack') {
return true;
}
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$cmd = $request->getStr('cmd');
if ($cmd == 'batch') {
$cmds = idx($this->getMercurialArguments(), 'cmds');
return DiffusionMercurialWireProtocol::isReadOnlyBatchCommand($cmds);
}
return DiffusionMercurialWireProtocol::isReadOnlyCommand($cmd);
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
break;
}
return false;
}
/**
* @phutil-external-symbol class PhabricatorStartup
*/
private function serveGitRequest(
PhabricatorRepository $repository,
PhabricatorUser $viewer) {
$request = $this->getRequest();
$request_path = $this->getRequestDirectoryPath($repository);
$repository_root = $repository->getLocalPath();
// 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.
// 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
// resolve the binary first.
$bin = Filesystem::resolveBinary('git-http-backend');
if (!$bin) {
throw new Exception(
pht(
'Unable to find `%s` in %s!',
'git-http-backend',
'$PATH'));
}
Fix handling of gzip in VCS responses Summary: Fixes T10264. I'm reasonably confident that this is the chain of events here: First, prior to 8269fd6e, we would ignore "Content-Encoding" when reading inbound bodies. So if a request was gzipped, we would read a gzipped body, then give `git-http-backend` a gzipped body with "Content-Encoding: gzip". Everything matched normally, so that was fine, except in the cluster. In the cluster, we'd accept "gzip + compressed body" and proxy it, but not tell cURL that it was already compressed. cURL would think it was raw data, so it would arrive on the repository host with a compressed body but no "Content-Encoding: gzip". Then we'd hand it to git in the same form. This caused the issue in 8269fd6e: handing it compressed data, but no "this is compressed" header. To fix this, I made us decompress the encoding when we read the body, so the cluster now proxies raw data instead of proxying gzipped data. This fixed the issue in the cluster, but created a new issue on non-cluster hosts. The new issue is that we accept "gzip + compressed body" and decompress the body, but then pass the //original// header to `git-http-backend`. So now we have the opposite problem from what we originally had: a "gzip" header, but a raw body. To fix //this//, we could do two things: - Revert 8269fd6e, then change the proxy request to preserve "Content-Encoding" instead. - Stop telling `git-http-backend` that we're handing it compressed data when we're handing it raw data. I did the latter here because it's an easier change to make and test, we'll need to interact with the raw data later anyway, to implement repository virtualization in connection with T8238. Test Plan: See T10264 for users confirming this fix. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10264 Differential Revision: https://secure.phabricator.com/D15258
2016-02-12 13:14:41 +01:00
// NOTE: We do not set HTTP_CONTENT_ENCODING here, because we already
// decompressed the request when we read the request body, so the body is
// just plain data with no encoding.
$env = array(
'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
'QUERY_STRING' => $query_string,
'CONTENT_TYPE' => $request->getHTTPHeader('Content-Type'),
'REMOTE_ADDR' => $_SERVER['REMOTE_ADDR'],
'GIT_PROJECT_ROOT' => $repository_root,
'GIT_HTTP_EXPORT_ALL' => '1',
'PATH_INFO' => $request_path,
'REMOTE_USER' => $viewer->getUsername(),
// TODO: Set these correctly.
// GIT_COMMITTER_NAME
// GIT_COMMITTER_EMAIL
) + $this->getCommonEnvironment($viewer);
$input = PhabricatorStartup::getRawInput();
Add "phd.user" with `sudo` hooks for SSH/HTTP writes Summary: Ref T2230. When fully set up, we have up to three users who all need to write into the repositories: - The webserver needs to write for HTTP receives. - The SSH user needs to write for SSH receives. - The daemons need to write for "git fetch", "git clone", etc. These three users don't need to be different, but in practice they are often not likely to all be the same user. If for no other reason, making them all the same user requires you to "git clone httpd@host.com", and installs are likely to prefer "git clone git@host.com". Using three different users also allows better privilege separation. Particularly, the daemon user can be the //only// user with write access to the repositories. The webserver and SSH user can accomplish their writes through `sudo`, with a whitelisted set of commands. This means that even if you compromise the `ssh` user, you need to find a way to escallate from there to the daemon user in order to, e.g., write arbitrary stuff into the repository or bypass commit hooks. This lays some of the groundwork for a highly-separated configuration where the SSH and HTTP users have the fewest privileges possible and use `sudo` to interact with repositories. Some future work which might make sense: - Make `bin/phd` respect this (require start as the right user, or as root and drop privileges, if this configuration is set). - Execute all `git/hg/svn` commands via sudo? Users aren't expected to configure this yet so I haven't written any documentation. Test Plan: Added an SSH user ("dweller") and gave it sudo by adding this to `/etc/sudoers`: dweller ALL=(epriestley) SETENV: NOPASSWD: /usr/bin/git-upload-pack, /usr/bin/git-receive-pack Then I ran git pushes and pulls over SSH via "dweller@localhost". They successfully interacted with the repository on disk as the "epriestley" user. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2230 Differential Revision: https://secure.phabricator.com/D7589
2013-11-18 17:58:35 +01:00
$command = csprintf('%s', $bin);
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
list($err, $stdout, $stderr) = id(new ExecFuture('%C', $command))
->setEnv($env, true)
->write($input)
->resolve();
if ($err) {
if ($this->isValidGitShallowCloneResponse($stdout, $stderr)) {
// Ignore the error if the response passes this special check for
// validity.
$err = 0;
}
}
if ($err) {
return new PhabricatorVCSResponse(
500,
pht(
'Error %d: %s',
$err,
phutil_utf8ize($stderr)));
}
return id(new DiffusionGitResponse())->setGitData($stdout);
}
private function getRequestDirectoryPath(PhabricatorRepository $repository) {
$request = $this->getRequest();
$request_path = $request->getRequestURI()->getPath();
$info = PhabricatorRepository::parseRepositoryServicePath($request_path);
$base_path = $info['path'];
// For Git repositories, strip an optional directory component if it
// isn't the name of a known Git resource. This allows users to clone
// repositories as "/diffusion/X/anything.git", for example.
if ($repository->isGit()) {
$known = array(
'info',
'git-upload-pack',
'git-receive-pack',
);
foreach ($known as $key => $path) {
$known[$key] = preg_quote($path, '@');
}
$known = implode('|', $known);
if (preg_match('@^/([^/]+)/('.$known.')(/|$)@', $base_path)) {
$base_path = preg_replace('@^/([^/]+)@', '', $base_path);
}
}
return $base_path;
}
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
private function authenticateGitLFSUser(
$username,
PhutilOpaqueEnvelope $password) {
// Never accept these credentials for requests which aren't LFS requests.
if (!$this->getIsGitLFSRequest()) {
return null;
}
// If we have the wrong username, don't bother checking if the token
// is right.
if ($username !== DiffusionGitLFSTemporaryTokenType::HTTP_USERNAME) {
return null;
}
$lfs_pass = $password->openEnvelope();
$lfs_hash = PhabricatorHash::digest($lfs_pass);
$token = id(new PhabricatorAuthTemporaryTokenQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withTokenTypes(array(DiffusionGitLFSTemporaryTokenType::TOKENTYPE))
->withTokenCodes(array($lfs_hash))
->withExpired(false)
->executeOne();
if (!$token) {
return null;
}
$user = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($token->getUserPHID()))
->executeOne();
if (!$user) {
return null;
}
if (!$user->isUserActivated()) {
return null;
}
$this->gitLFSToken = $token;
return $user;
}
private function authenticateHTTPRepositoryUser(
$username,
PhutilOpaqueEnvelope $password) {
if (!PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth')) {
// No HTTP auth permitted.
return null;
}
if (!strlen($username)) {
// No username.
return null;
}
if (!strlen($password->openEnvelope())) {
// No password.
return null;
}
$user = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUsernames(array($username))
->executeOne();
if (!$user) {
// Username doesn't match anything.
return null;
}
Improve handling of email verification and "activated" accounts Summary: Small step forward which improves existing stuff or lays groudwork for future stuff: - Currently, to check for email verification, we have to single-query the email address on every page. Instead, denoramlize it into the user object. - Migrate all the existing users. - When the user verifies an email, mark them as `isEmailVerified` if the email is their primary email. - Just make the checks look at the `isEmailVerified` field. - Add a new check, `isUserActivated()`, to cover email-verified plus disabled. Currently, a non-verified-but-not-disabled user could theoretically use Conduit over SSH, if anyone deployed it. Tighten that up. - Add an `isApproved` flag, which is always true for now. In a future diff, I want to add a default-on admin approval queue for new accounts, to prevent configuration mistakes. The way it will work is: - When the queue is enabled, registering users are created with `isApproved = false`. - Admins are sent an email, "[Phabricator] New User Approval (alincoln)", telling them that a new user is waiting for approval. - They go to the web UI and approve the user. - Manually-created accounts are auto-approved. - The email will have instructions for disabling the queue. I think this queue will be helpful for new installs and give them peace of mind, and when you go to disable it we have a better opportunity to warn you about exactly what that means. Generally, I want to improve the default safety of registration, since if you just blindly coast through the path of least resistance right now your install ends up pretty open, and realistically few installs are on VPNs. Test Plan: - Ran migration, verified `isEmailVerified` populated correctly. - Created a new user, checked DB for verified (not verified). - Verified, checked DB (now verified). - Used Conduit, People, Diffusion. Reviewers: btrahan Reviewed By: btrahan CC: chad, aran Differential Revision: https://secure.phabricator.com/D7572
2013-11-12 23:37:04 +01:00
if (!$user->isUserActivated()) {
// User is not activated.
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 the user's password is stored using a less-than-optimal hash, upgrade
// them to the strongest available hash.
$hash_envelope = new PhutilOpaqueEnvelope(
$password_entry->getPasswordHash());
if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
$password_entry->setPassword($password, $user);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$password_entry->save();
unset($unguarded);
}
return $user;
}
private function serveMercurialRequest(
PhabricatorRepository $repository,
PhabricatorUser $viewer) {
$request = $this->getRequest();
$bin = Filesystem::resolveBinary('hg');
if (!$bin) {
throw new Exception(
pht(
'Unable to find `%s` in %s!',
'hg',
'$PATH'));
}
$env = $this->getCommonEnvironment($viewer);
$input = PhabricatorStartup::getRawInput();
$cmd = $request->getStr('cmd');
$args = $this->getMercurialArguments();
$args = $this->formatMercurialArguments($cmd, $args);
if (strlen($input)) {
$input = strlen($input)."\n".$input."0\n";
}
Add "phd.user" with `sudo` hooks for SSH/HTTP writes Summary: Ref T2230. When fully set up, we have up to three users who all need to write into the repositories: - The webserver needs to write for HTTP receives. - The SSH user needs to write for SSH receives. - The daemons need to write for "git fetch", "git clone", etc. These three users don't need to be different, but in practice they are often not likely to all be the same user. If for no other reason, making them all the same user requires you to "git clone httpd@host.com", and installs are likely to prefer "git clone git@host.com". Using three different users also allows better privilege separation. Particularly, the daemon user can be the //only// user with write access to the repositories. The webserver and SSH user can accomplish their writes through `sudo`, with a whitelisted set of commands. This means that even if you compromise the `ssh` user, you need to find a way to escallate from there to the daemon user in order to, e.g., write arbitrary stuff into the repository or bypass commit hooks. This lays some of the groundwork for a highly-separated configuration where the SSH and HTTP users have the fewest privileges possible and use `sudo` to interact with repositories. Some future work which might make sense: - Make `bin/phd` respect this (require start as the right user, or as root and drop privileges, if this configuration is set). - Execute all `git/hg/svn` commands via sudo? Users aren't expected to configure this yet so I haven't written any documentation. Test Plan: Added an SSH user ("dweller") and gave it sudo by adding this to `/etc/sudoers`: dweller ALL=(epriestley) SETENV: NOPASSWD: /usr/bin/git-upload-pack, /usr/bin/git-receive-pack Then I ran git pushes and pulls over SSH via "dweller@localhost". They successfully interacted with the repository on disk as the "epriestley" user. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2230 Differential Revision: https://secure.phabricator.com/D7589
2013-11-18 17:58:35 +01:00
$command = csprintf('%s serve --stdio', $bin);
$command = PhabricatorDaemon::sudoCommandAsDaemonUser($command);
list($err, $stdout, $stderr) = id(new ExecFuture('%C', $command))
->setEnv($env, true)
->setCWD($repository->getLocalPath())
->write("{$cmd}\n{$args}{$input}")
->resolve();
if ($err) {
return new PhabricatorVCSResponse(
500,
pht('Error %d: %s', $err, $stderr));
}
if ($cmd == 'getbundle' ||
$cmd == 'changegroup' ||
$cmd == 'changegroupsubset') {
// We're not completely sure that "changegroup" and "changegroupsubset"
// actually work, they're for very old Mercurial.
$body = gzcompress($stdout);
} else if ($cmd == 'unbundle') {
// This includes diagnostic information and anything echoed by commit
// hooks. We ignore `stdout` since it just has protocol garbage, and
// substitute `stderr`.
$body = strlen($stderr)."\n".$stderr;
} else {
list($length, $body) = explode("\n", $stdout, 2);
if ($cmd == 'capabilities') {
$body = DiffusionMercurialWireProtocol::filterBundle2Capability($body);
}
}
return id(new DiffusionMercurialResponse())->setContent($body);
}
private function getMercurialArguments() {
// Mercurial sends arguments in HTTP headers. "Why?", you might wonder,
// "Why would you do this?".
$args_raw = array();
for ($ii = 1;; $ii++) {
$header = 'HTTP_X_HGARG_'.$ii;
if (!array_key_exists($header, $_SERVER)) {
break;
}
$args_raw[] = $_SERVER[$header];
}
$args_raw = implode('', $args_raw);
return id(new PhutilQueryStringParser())
->parseQueryString($args_raw);
}
private function formatMercurialArguments($command, array $arguments) {
$spec = DiffusionMercurialWireProtocol::getCommandArgs($command);
$out = array();
// Mercurial takes normal arguments like this:
//
// name <length(value)>
// value
$has_star = false;
foreach ($spec as $arg_key) {
if ($arg_key == '*') {
$has_star = true;
continue;
}
if (isset($arguments[$arg_key])) {
$value = $arguments[$arg_key];
$size = strlen($value);
$out[] = "{$arg_key} {$size}\n{$value}";
unset($arguments[$arg_key]);
}
}
if ($has_star) {
// Mercurial takes arguments for variable argument lists roughly like
// this:
//
// * <count(args)>
// argname1 <length(argvalue1)>
// argvalue1
// argname2 <length(argvalue2)>
// argvalue2
$count = count($arguments);
$out[] = "* {$count}\n";
foreach ($arguments as $key => $value) {
if (in_array($key, $spec)) {
// We already added this argument above, so skip it.
continue;
}
$size = strlen($value);
$out[] = "{$key} {$size}\n{$value}";
}
}
return implode('', $out);
}
private function isValidGitShallowCloneResponse($stdout, $stderr) {
// If you execute `git clone --depth N ...`, git sends a request which
// `git-http-backend` responds to by emitting valid output and then exiting
// with a failure code and an error message. If we ignore this error,
// everything works.
// This is a pretty funky fix: it would be nice to more precisely detect
// that a request is a `--depth N` clone request, but we don't have any code
// to decode protocol frames yet. Instead, look for reasonable evidence
// in the error and output that we're looking at a `--depth` clone.
// For evidence this isn't completely crazy, see:
// https://github.com/schacon/grack/pull/7
$stdout_regexp = '(^Content-Type: application/x-git-upload-pack-result)m';
$stderr_regexp = '(The remote end hung up unexpectedly)';
$has_pack = preg_match($stdout_regexp, $stdout);
$is_hangup = preg_match($stderr_regexp, $stderr);
return $has_pack && $is_hangup;
}
Add "phd.user" with `sudo` hooks for SSH/HTTP writes Summary: Ref T2230. When fully set up, we have up to three users who all need to write into the repositories: - The webserver needs to write for HTTP receives. - The SSH user needs to write for SSH receives. - The daemons need to write for "git fetch", "git clone", etc. These three users don't need to be different, but in practice they are often not likely to all be the same user. If for no other reason, making them all the same user requires you to "git clone httpd@host.com", and installs are likely to prefer "git clone git@host.com". Using three different users also allows better privilege separation. Particularly, the daemon user can be the //only// user with write access to the repositories. The webserver and SSH user can accomplish their writes through `sudo`, with a whitelisted set of commands. This means that even if you compromise the `ssh` user, you need to find a way to escallate from there to the daemon user in order to, e.g., write arbitrary stuff into the repository or bypass commit hooks. This lays some of the groundwork for a highly-separated configuration where the SSH and HTTP users have the fewest privileges possible and use `sudo` to interact with repositories. Some future work which might make sense: - Make `bin/phd` respect this (require start as the right user, or as root and drop privileges, if this configuration is set). - Execute all `git/hg/svn` commands via sudo? Users aren't expected to configure this yet so I haven't written any documentation. Test Plan: Added an SSH user ("dweller") and gave it sudo by adding this to `/etc/sudoers`: dweller ALL=(epriestley) SETENV: NOPASSWD: /usr/bin/git-upload-pack, /usr/bin/git-receive-pack Then I ran git pushes and pulls over SSH via "dweller@localhost". They successfully interacted with the repository on disk as the "epriestley" user. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2230 Differential Revision: https://secure.phabricator.com/D7589
2013-11-18 17:58:35 +01:00
private function getCommonEnvironment(PhabricatorUser $viewer) {
$remote_address = $this->getRequest()->getRemoteAddress();
return array(
DiffusionCommitHookEngine::ENV_USER => $viewer->getUsername(),
DiffusionCommitHookEngine::ENV_REMOTE_ADDRESS => $remote_address,
DiffusionCommitHookEngine::ENV_REMOTE_PROTOCOL => 'http',
);
}
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
private function validateGitLFSRequest(
PhabricatorRepository $repository,
PhabricatorUser $viewer) {
if (!$this->getIsGitLFSRequest()) {
return null;
}
if (!$repository->canUseGitLFS()) {
return new PhabricatorVCSResponse(
403,
pht(
'The requested repository ("%s") does not support Git LFS.',
$repository->getDisplayName()));
}
// If this is using an LFS token, sanity check that we're using it on the
// correct repository. This shouldn't really matter since the user could
// just request a proper token anyway, but it suspicious and should not
// be permitted.
$token = $this->getGitLFSToken();
if ($token) {
$resource = $token->getTokenResource();
if ($resource !== $repository->getPHID()) {
return new PhabricatorVCSResponse(
403,
pht(
'The authentication token provided in the request is bound to '.
'a different repository than the requested repository ("%s").',
$repository->getDisplayName()));
}
}
return null;
}
private function serveGitLFSRequest(
PhabricatorRepository $repository,
PhabricatorUser $viewer) {
if (!$this->getIsGitLFSRequest()) {
throw new Exception(pht('This is not a Git LFS request!'));
}
$path = $this->getGitLFSRequestPath($repository);
if ($path == 'objects/batch') {
return $this->serveGitLFSBatchRequest($repository, $viewer);
} else {
return DiffusionGitLFSResponse::newErrorResponse(
404,
pht(
'Git LFS operation "%s" is not supported by this server.',
$path));
}
}
private function serveGitLFSBatchRequest(
PhabricatorRepository $repository,
PhabricatorUser $viewer) {
$input = PhabricatorStartup::getRawInput();
$input = phutil_json_decode($input);
$operation = idx($input, 'operation');
switch ($operation) {
case 'upload':
$want_upload = true;
break;
case 'download':
$want_upload = false;
break;
default:
return DiffusionGitLFSResponse::newErrorResponse(
404,
pht(
'Git LFS batch operation "%s" is not supported by this server.',
$operation));
}
$objects = idx($input, 'objects', array());
$hashes = array();
foreach ($objects as $object) {
$hashes[] = idx($object, 'oid');
}
if ($hashes) {
$refs = id(new PhabricatorRepositoryGitLFSRefQuery())
->setViewer($viewer)
->withRepositoryPHIDs(array($repository->getPHID()))
->withObjectHashes($hashes)
->execute();
$refs = mpull($refs, null, 'getObjectHash');
} else {
$refs = array();
}
$file_phids = mpull($refs, 'getFilePHID');
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($file_phids))
->execute();
$files = mpull($files, null, 'getPHID');
} else {
$files = array();
}
$authorization = null;
$output = array();
foreach ($objects as $object) {
$oid = idx($object, 'oid');
$size = idx($object, 'size');
$ref = idx($refs, $oid);
// NOTE: If we already have a ref for this object, we only emit a
// "download" action. The client should not upload the file again.
$actions = array();
if ($ref) {
$file = idx($files, $ref->getFilePHID());
if ($file) {
$get_uri = $file->getCDNURIWithToken();
$actions['download'] = array(
'href' => $get_uri,
);
}
} else if ($want_upload) {
if (!$authorization) {
// Here, we could reuse the existing authorization if we have one,
// but it's a little simpler to just generate a new one
// unconditionally.
$authorization = $this->newGitLFSHTTPAuthorization(
$repository,
$viewer,
$operation);
}
$put_uri = $repository->getGitLFSURI("info/lfs/upload/{$oid}");
$actions['upload'] = array(
'href' => $put_uri,
'header' => array(
'Authorization' => $authorization,
'X-Phabricator-Request-Type' => 'git-lfs',
),
);
}
$output[] = array(
'oid' => $oid,
'size' => $size,
'actions' => $actions,
);
}
$output = array(
'objects' => $output,
);
return id(new DiffusionGitLFSResponse())
->setContent($output);
}
private function newGitLFSHTTPAuthorization(
PhabricatorRepository $repository,
PhabricatorUser $viewer,
$operation) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$authorization = DiffusionGitLFSTemporaryTokenType::newHTTPAuthorization(
$repository,
$viewer,
$operation);
unset($unguarded);
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
return $authorization;
Implement a Git LFS server which supports no operations Summary: Ref T7789. This builds on top of `git-lfs-authenticate` to detect LFS requests, read LFS tokens, and route them to a handler which can do useful things. This handler promptly drops them on the floor with an error message. Test Plan: Here's a transcript showing the parts working together so far: - `git-lfs` connects to the server with SSH, and gets told how to connect with HTTP to do uploads. - `git-lfs` uses HTTP, and authenticates with the tokens properly. - But the server tells it to go away, and that it doesn't support anything, so the operation ultimately fails. ``` $ GIT_TRACE=1 git lfs push origin master 12:45:56.153913 git.c:558 trace: exec: 'git-lfs' 'push' 'origin' 'master' 12:45:56.154376 run-command.c:335 trace: run_command: 'git-lfs' 'push' 'origin' 'master' trace git-lfs: Upload refs origin to remote [master] trace git-lfs: run_command: git rev-list --objects master --not --remotes=origin trace git-lfs: run_command: git cat-file --batch-check trace git-lfs: run_command: git cat-file --batch trace git-lfs: run_command: 'git' config -l trace git-lfs: tq: starting 3 transfer workers trace git-lfs: tq: running as batched queue, batch size of 100 trace git-lfs: prepare upload: b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 lfs/dog1.jpg 1/1 trace git-lfs: tq: sending batch of size 1 trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload trace git-lfs: api: batch 1 files trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects/batch trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\/batch\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: api: batch not implemented: 404 trace git-lfs: run_command: 'git' config lfs.batch false trace git-lfs: tq: batch api not implemented, falling back to individual trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: trace git-lfs: tq: retrying 1 failed transfers trace git-lfs: ssh: local@localvault.phacility.com git-lfs-authenticate diffusion/18/poems.git upload b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69 trace git-lfs: api: uploading (b7e0aeb82a03d627c6aa5fc1bbfd454b6789d9d9affc8607d40168fa18cf6c69) trace git-lfs: HTTP: POST http://local.phacility.com/diffusion/POEMS/poems.git/info/lfs/objects trace git-lfs: HTTP: 404 trace git-lfs: HTTP: {"message":"Git LFS operation \"objects\" is not supported by this server."} trace git-lfs: HTTP: Git LFS: (0 of 1 files) 0 B / 87.12 KB Git LFS operation "objects" is not supported by this server. Git LFS operation "objects" is not supported by this server. ``` Reviewers: chad Reviewed By: chad Subscribers: eadler Maniphest Tasks: T7789 Differential Revision: https://secure.phabricator.com/D15485
2016-03-16 20:42:21 +01:00
}
private function getGitLFSRequestPath(PhabricatorRepository $repository) {
$request_path = $this->getRequestDirectoryPath($repository);
$matches = null;
if (preg_match('(^/info/lfs(?:\z|/)(.*))', $request_path, $matches)) {
return $matches[1];
}
return null;
}
}