11 Commits

Author SHA1 Message Date
Patrick Georgi
cdb8dbafe2 Write-protect git repository over HTTP!
I totally misunderstood the access control mechanism (but it's logical
that it behaves the way it does), and so on git projects with "open"
source access, repos were write-for-all.

This should fix it by enforcing member-or-owner auth for writes.

Signed-off-by: Patrick Georgi <patrick@georgi-clan.de>
2011-09-03 19:30:34 +02:00
Patrick Georgi
33b22f95ab Use idf_exec_prefix when git tools are called 2011-08-25 23:36:27 +02:00
Patrick Georgi
39f77886db Make HTTP auth cover all popular FastCGI workarounds 2011-08-25 15:28:02 +02:00
Patrick Georgi
f7470e4a7a Use REDIRECT_* for FastCGI/PHP Authorization handling 2011-08-25 14:50:21 +02:00
Patrick Georgi
c10c002ee3 Document HTTP repository access for git 2011-08-25 14:45:50 +02:00
Patrick Georgi
a47ec0df0a If necessary, create git repository on first http access 2011-08-21 23:54:42 +02:00
Patrick Georgi
be95050a4b Use new semantics for adding an stdin stream to PassThru 2011-08-21 08:39:41 +02:00
Patrick Georgi
5043c4e845 Stylistic fixes 2011-08-21 07:53:21 +02:00
Patrick Georgi
dacbf0707b Move http repository access to /r/$project
It's a shorter URL and also helps git derive the right name
for the clone.
2011-08-21 07:44:19 +02:00
Patrick Georgi
34c9d04a35 Provide http access to git repositories
/p/$project/source/repo for git repos now exposes both
"dumb" and "smart" http protocol access.
2011-08-20 20:07:26 +02:00
Patrick Georgi
aa2868eb17 Add basic framework for web based repository access
/p/$project/source/repo/ is assigned to a method that
takes care of providing repository access.
For now, this results in an exception on all SCMs.
2011-08-19 22:05:15 +02:00
7 changed files with 184 additions and 1 deletions

View File

@@ -13,6 +13,7 @@ or newer to properly run this version of Indefero!
- Display monotone file and directory attributes in the tree and file view
(needs a monotone with an interface version of 13.1 or newer)
- The context area is now kept in view when a page scrolls down several pages
- git repositories are now available under /r/projectname/
## Bugfixes

57
doc/httprepos-git.mdtext Normal file
View File

@@ -0,0 +1,57 @@
# Accessing git repositories over HTTP
Starting with indefero 1.2, git repositories are provided via http,
featuring read-only and read-write access with full integration with
indefero's access control mechanisms.
## Access git repositories
The repositories are available under http://YOURHOST/BASEPATH/r/PROJECT.
For authentication, use the "extra password" which you can find on your
profile page.
## Setup
The main thing to setup is git_repositories and git_remote_url in
src/IDF/conf/idf.php
git_remote_url should match http://YOURHOST/BASEPATH/r/%s, while
git_repositories points to the local directory supposed to contain the
repositories like /PATH/TO/REPOSITORIES/%s.git.
## Setup requirements when using PHP over FastCGI
There are a couple of things to setup when using PHP over FastCGI, as compared
to mod_php or similar integrated mechanisms:
- You will need to setup a RewriteRule for mod_rewrite (in case of Apache,
analogous mechanisms might need to be setup for other http daemons), which
passes through the Authorization HTTP Header of a request.
In case of mod_rewrite, the necessary line is
one of (depending on server configuration):
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]
- The FastCGI adaptor must allow large requests to be handled by PHP,
otherwise push might fail.
For mod_fcgid, this is the FcgidMaxRequestLen option, or MaxRequestLen in
older versions. Set this option to some large enough value - there is no
issue with RAM use, as another option defines the limit after which the
request is backed on disk, which can remain small.
## When migrating from syncgit
HTTP access can be used in parallel to syncgit.
If you want to disable syncgit, just undo the changes detailled
in doc/syncgit.mdtext:
- In src/IDF/conf/idf.php keep git_repositories
- In src/IDF/conf/idf.php adapt git_remote_url to http://host/basepath/r/%s
- In src/IDF/conf/idf.php remove idf_plugin_syncgit*
- Remove the cronjob that called gitcron.php
- Disable the git daemon (eg. /etc/event.d/local-git-daemon)
- You can remove the git user and group, if you also adapt the git repositories
to be owned by the www user.

View File

@@ -44,7 +44,7 @@ class IDF_Middleware
function process_request(&$request)
{
$match = array();
if (preg_match('#^/(?:api/p|p)/([\-\w]+)/?#', $request->query, $match)) {
if (preg_match('#^/(?:api/p|p|r)/([\-\w]+)/?#', $request->query, $match)) {
try {
$request->project = IDF_Project::getOr404($match[1]);
} catch (Pluf_HTTP_Error404 $e) {

View File

@@ -497,5 +497,10 @@ class IDF_Scm
{
return 0;
}
public function repository($request, $match)
{
throw new Exception('This repository does not support web based repository access');
}
}

View File

@@ -924,4 +924,113 @@ class IDF_Scm_Git extends IDF_Scm
}
return false;
}
public function repository($request, $match)
{
// handle a couple of workarounds for authenticating with FastCGI/PHP
if (!empty($_SERVER['HTTP_AUTHORIZATION']))
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
elseif (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 6)));
elseif (!empty($_SERVER['REDIRECT_REMOTE_USER']))
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['REDIRECT_REMOTE_USER'], 6)));
// authenticate: authenticate connection through "extra" password
if (!empty($_SERVER['PHP_AUTH_USER'])) {
$sql = new Pluf_SQL('login=%s', array($_SERVER['PHP_AUTH_USER']));
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
if (count($users) == 1 && $users[0]->active) {
$user = $users[0];
$realkey = substr(sha1($user->password.Pluf::f('secret_key')), 0, 8);
if ($_SERVER['PHP_AUTH_PW'] == $realkey) {
$request->user = $user;
}
}
}
if (IDF_Precondition::accessSource($request) !== true) {
$response = new Pluf_HTTP_Response("");
$response->status_code = 401;
$response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
return $response;
}
$path = $match[2];
if (!file_exists($this->repo)) {
mkdir($this->repo, 0750, true);
$out = array();
$res = 0;
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
Pluf::f('git_path', 'git').' --git-dir=%s init', escapeshellarg($this->repo)),
$out, $res);
if ($res != 0) {
Pluf_Log::error(array('IDF_Scm_Git::repository', $res, $this->repo));
throw new Exception(sprintf('Init repository error, exit status %d.', $res));
}
}
// update files before delivering them
if (($path == 'objects/info/pack') || ($path == 'info/refs')) {
$cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' update-server-info -f',
escapeshellarg($this->repo));
self::shell_exec('IDF_Scm_Git::repository', $cmd);
}
// smart HTTP discovery
if ($path == 'info/refs' && !empty($request->GET['service'])){
$service = $request->GET['service'];
switch ($service) {
case 'git-receive-pack':
if (IDF_Precondition::projectMemberOrOwner($request) !== true) {
$response = new Pluf_HTTP_Response("");
$response->status_code = 401;
$response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
return $response;
}
case 'git-upload-pack':
$content = sprintf('%04x',strlen($service)+15).
'# service='.$service."\n0000";
$content .= self::shell_exec('IDF_Scm_Git::repository',
Pluf::f('idf_exec_cmd_prefix', '').
$service.' --stateless-rpc --advertise-refs '.
$this->repo);
$response = new Pluf_HTTP_Response($content,
'application/x-'.$service.'-advertisement');
return $response;
default:
throw new Exception('unknown service: '.$service);
}
}
switch($path) {
// smart HTTP RPC
case 'git-receive-pack':
if (IDF_Precondition::projectMemberOrOwner($request) !== true) {
$response = new Pluf_HTTP_Response("");
$response->status_code = 401;
$response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
return $response;
}
case 'git-upload-pack':
$response = new Pluf_HTTP_Response_CommandPassThru(
Pluf::f('idf_exec_cmd_prefix', '').$path.
' --stateless-rpc '.$this->repo,
'application/x-'.$path.'-result');
$response->setStdin(fopen('php://input', 'rb'));
return $response;
// regular file
default:
// make sure we're inside the repo hierarchy (ie. no break-out)
if (is_file($this->repo.'/'.$path) &&
strpos(realpath($this->repo.'/'.$path), $this->repo.'/') == 0) {
return new Pluf_HTTP_Response_File($this->repo.'/'.$path,
'application/octet-stream');
} else {
return new Pluf_HTTP_Response_NotFound($request);
}
}
}
}

View File

@@ -132,6 +132,12 @@ class IDF_Views_Source
$request);
}
public function repository($request, $match)
{
$scm = IDF_Scm::get($request->project);
return $scm->repository($request, $match);
}
public $treeBase_precond = array('IDF_Precondition::accessSource',
'IDF_Views_Source_Precondition::scmAvailable',
'IDF_Views_Source_Precondition::revisionValid');

View File

@@ -245,6 +245,11 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/source/changesrev/$#',
'model' => 'IDF_Views_Source_Svn',
'method' => 'changelogRev');
$ctl[] = array('regex' => '#^/r/([\-\w]+)/(.*)$#',
'base' => $base,
'model' => 'IDF_Views_Source',
'method' => 'repository');
// ---------- WIKI -----------------------------------------
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/$#',