aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTommi Virtanen <tv@eagain.net>2007-11-17 16:27:21 +0200
committerTommi Virtanen <tv@eagain.net>2007-11-17 16:27:21 +0200
commit13c89cdb7d493c43659c7f202c6cc3f81ea4c1e8 (patch)
tree25297dba3becc15f13950dfeb82f492547b4dbf9
parentAdd unit test for gitweb.generate_project_list. (diff)
downloadgitosis-dakkar-13c89cdb7d493c43659c7f202c6cc3f81ea4c1e8.tar.gz
gitosis-dakkar-13c89cdb7d493c43659c7f202c6cc3f81ea4c1e8.tar.bz2
gitosis-dakkar-13c89cdb7d493c43659c7f202c6cc3f81ea4c1e8.zip
Manage git-daemon-export-ok flags from gitosis config.
-rw-r--r--TODO.rst3
-rw-r--r--example.conf6
-rw-r--r--gitosis/gitdaemon.py90
-rw-r--r--gitosis/run_hook.py4
-rw-r--r--gitosis/test/test_gitdaemon.py155
5 files changed, 253 insertions, 5 deletions
diff --git a/TODO.rst b/TODO.rst
index ccf86d1..976105a 100644
--- a/TODO.rst
+++ b/TODO.rst
@@ -6,7 +6,8 @@
- gitosis-lint: check that the user account (e.g. ``git``) looks valid
-- git-daemon-export-ok
+- create git-daemon-export-ok, description, projects.list etc when
+ autocreating repositorites?
- guard against *.pub files named -foo.pub or foo;bar.pub
diff --git a/example.conf b/example.conf
index 117be5c..411a647 100644
--- a/example.conf
+++ b/example.conf
@@ -13,8 +13,7 @@ gitweb = no
## Allow git-daemon to publish all known repositories. As with gitweb,
## this can be done globally or per-repository.
-## NOT YET IMPLEMENTED.
-# daemon-ok = no
+daemon = no
## Logging level, one of DEBUG, INFO, WARNING, ERROR, CRITICAL
loglevel = DEBUG
@@ -41,8 +40,7 @@ gitweb = yes
owner = John Doe
## Allow git-daemon to publish this repository.
-## NOT YET IMPLEMENTED.
-# daemon-ok = no
+daemon = yes
[gitweb]
## Where to make gitweb link to as it's "home location".
diff --git a/gitosis/gitdaemon.py b/gitosis/gitdaemon.py
new file mode 100644
index 0000000..78ca9ea
--- /dev/null
+++ b/gitosis/gitdaemon.py
@@ -0,0 +1,90 @@
+import errno
+import logging
+import os
+
+from ConfigParser import NoSectionError, NoOptionError
+
+log = logging.getLogger('gitosis.gitdaemon')
+
+from gitosis import util
+
+def export_ok_path(repopath):
+ p = os.path.join(repopath, 'git-daemon-export-ok')
+ return p
+
+def allow_export(repopath):
+ p = export_ok_path(repopath)
+ file(p, 'a').close()
+
+def deny_export(repopath):
+ p = export_ok_path(repopath)
+ try:
+ os.unlink(p)
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ raise
+
+def _extract_reldir(topdir, dirpath):
+ if topdir == dirpath:
+ return '.'
+ prefix = topdir + '/'
+ assert dirpath.startswith(prefix)
+ reldir = dirpath[len(prefix):]
+ return reldir
+
+def set_export_ok(config):
+ repositories = util.getRepositoryDir(config)
+
+ try:
+ global_enable = config.getboolean('gitosis', 'daemon')
+ except (NoSectionError, NoOptionError):
+ global_enable = False
+ log.debug(
+ 'Global default is %r',
+ {True: 'allow', False: 'deny'}.get(global_enable),
+ )
+
+ def _error(e):
+ if e.errno == errno.ENOENT:
+ pass
+ else:
+ raise e
+
+ for (dirpath, dirnames, filenames) \
+ in os.walk(repositories, onerror=_error):
+ # oh how many times i have wished for os.walk to report
+ # topdir and reldir separately, instead of dirpath
+ reldir = _extract_reldir(
+ topdir=repositories,
+ dirpath=dirpath,
+ )
+
+ log.debug('Walking %r, seeing %r', reldir, dirnames)
+
+ to_recurse = []
+ repos = []
+ for dirname in dirnames:
+ if dirname.endswith('.git'):
+ repos.append(dirname)
+ else:
+ to_recurse.append(dirname)
+ dirnames[:] = to_recurse
+
+ for repo in repos:
+ name, ext = os.path.splitext(repo)
+ if reldir != '.':
+ name = os.path.join(reldir, name)
+ assert ext == '.git'
+ try:
+ enable = config.getboolean('repo %s' % name, 'daemon')
+ except (NoSectionError, NoOptionError):
+ enable = global_enable
+
+ if enable:
+ log.debug('Allow %r', name)
+ allow_export(os.path.join(dirpath, repo))
+ else:
+ log.debug('Deny %r', name)
+ deny_export(os.path.join(dirpath, repo))
diff --git a/gitosis/run_hook.py b/gitosis/run_hook.py
index d5dd4ab..a5ed9d0 100644
--- a/gitosis/run_hook.py
+++ b/gitosis/run_hook.py
@@ -11,6 +11,7 @@ import shutil
from gitosis import repository
from gitosis import ssh
from gitosis import gitweb
+from gitosis import gitdaemon
from gitosis import app
def post_update(cfg, git_dir):
@@ -31,6 +32,9 @@ def post_update(cfg, git_dir):
config=cfg,
path=os.path.join(git_dir, 'projects.list'),
)
+ gitdaemon.set_export_ok(
+ config=cfg,
+ )
ssh.writeAuthorizedKeys(
path=os.path.expanduser('~/.ssh/authorized_keys'),
keydir=os.path.join(export, 'keydir'),
diff --git a/gitosis/test/test_gitdaemon.py b/gitosis/test/test_gitdaemon.py
new file mode 100644
index 0000000..94475ac
--- /dev/null
+++ b/gitosis/test/test_gitdaemon.py
@@ -0,0 +1,155 @@
+from nose.tools import eq_ as eq
+
+import os
+from ConfigParser import RawConfigParser
+
+from gitosis import gitdaemon
+from gitosis.test.util import maketemp, writeFile
+
+def exported(path):
+ assert os.path.isdir(path)
+ p = gitdaemon.export_ok_path(path)
+ return os.path.exists(p)
+
+def test_git_daemon_export_ok_repo_missing():
+ # configured but not created yet; before first push
+ tmp = maketemp()
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.add_section('repo foo')
+ cfg.set('repo foo', 'daemon', 'yes')
+ gitdaemon.set_export_ok(config=cfg)
+ assert not os.path.exists(os.path.join(tmp, 'foo'))
+ assert not os.path.exists(os.path.join(tmp, 'foo.git'))
+
+def test_git_daemon_export_ok_repo_missing_parent():
+ # configured but not created yet; before first push
+ tmp = maketemp()
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.add_section('repo foo/bar')
+ cfg.set('repo foo/bar', 'daemon', 'yes')
+ gitdaemon.set_export_ok(config=cfg)
+ assert not os.path.exists(os.path.join(tmp, 'foo'))
+
+def test_git_daemon_export_ok_allowed():
+ tmp = maketemp()
+ path = os.path.join(tmp, 'foo.git')
+ os.mkdir(path)
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.add_section('repo foo')
+ cfg.set('repo foo', 'daemon', 'yes')
+ gitdaemon.set_export_ok(config=cfg)
+ eq(exported(path), True)
+
+def test_git_daemon_export_ok_allowed_already():
+ tmp = maketemp()
+ path = os.path.join(tmp, 'foo.git')
+ os.mkdir(path)
+ writeFile(gitdaemon.export_ok_path(path), '')
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.add_section('repo foo')
+ cfg.set('repo foo', 'daemon', 'yes')
+ gitdaemon.set_export_ok(config=cfg)
+ eq(exported(path), True)
+
+def test_git_daemon_export_ok_denied():
+ tmp = maketemp()
+ path = os.path.join(tmp, 'foo.git')
+ os.mkdir(path)
+ writeFile(gitdaemon.export_ok_path(path), '')
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.add_section('repo foo')
+ cfg.set('repo foo', 'daemon', 'no')
+ gitdaemon.set_export_ok(config=cfg)
+ eq(exported(path), False)
+
+def test_git_daemon_export_ok_denied_already():
+ tmp = maketemp()
+ path = os.path.join(tmp, 'foo.git')
+ os.mkdir(path)
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.add_section('repo foo')
+ cfg.set('repo foo', 'daemon', 'no')
+ gitdaemon.set_export_ok(config=cfg)
+ eq(exported(path), False)
+
+def test_git_daemon_export_ok_subdirs():
+ tmp = maketemp()
+ foo = os.path.join(tmp, 'foo')
+ os.mkdir(foo)
+ path = os.path.join(foo, 'bar.git')
+ os.mkdir(path)
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.add_section('repo foo/bar')
+ cfg.set('repo foo/bar', 'daemon', 'yes')
+ gitdaemon.set_export_ok(config=cfg)
+ eq(exported(path), True)
+
+def test_git_daemon_export_ok_denied_default():
+ tmp = maketemp()
+ path = os.path.join(tmp, 'foo.git')
+ os.mkdir(path)
+ writeFile(gitdaemon.export_ok_path(path), '')
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.add_section('repo foo')
+ gitdaemon.set_export_ok(config=cfg)
+ eq(exported(path), False)
+
+def test_git_daemon_export_ok_denied_even_not_configured():
+ # repositories not mentioned in config also get touched; this is
+ # to avoid security trouble, otherwise we might expose (or
+ # continue to expose) old repositories removed from config
+ tmp = maketemp()
+ path = os.path.join(tmp, 'foo.git')
+ os.mkdir(path)
+ writeFile(gitdaemon.export_ok_path(path), '')
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ gitdaemon.set_export_ok(config=cfg)
+ eq(exported(path), False)
+
+def test_git_daemon_export_ok_allowed_global():
+ tmp = maketemp()
+
+ for repo in [
+ 'foo.git',
+ 'quux.git',
+ 'thud.git',
+ ]:
+ path = os.path.join(tmp, repo)
+ os.mkdir(path)
+
+ # try to provoke an invalid allow
+ writeFile(gitdaemon.export_ok_path(os.path.join(tmp, 'thud.git')), '')
+
+ cfg = RawConfigParser()
+ cfg.add_section('gitosis')
+ cfg.set('gitosis', 'repositories', tmp)
+ cfg.set('gitosis', 'daemon', 'yes')
+ cfg.add_section('repo foo')
+ cfg.add_section('repo quux')
+ # same as default, no effect
+ cfg.set('repo quux', 'daemon', 'yes')
+ cfg.add_section('repo thud')
+ # this is still hidden
+ cfg.set('repo thud', 'daemon', 'no')
+ gitdaemon.set_export_ok(config=cfg)
+ eq(exported(os.path.join(tmp, 'foo.git')), True)
+ eq(exported(os.path.join(tmp, 'quux.git')), True)
+ eq(exported(os.path.join(tmp, 'thud.git')), False)