def post_update(cfg, git_dir): export = os.path.join(git_dir, 'gitosis-export') try: shutil.rmtree(export) except OSError as e: if e.errno == errno.ENOENT: pass else: raise repository.export(git_dir=git_dir, path=export) os.rename( os.path.join(export, 'gitosis.conf'), os.path.join(export, '..', 'gitosis.conf'), ) # re-read config to get up-to-date settings cfg.read(os.path.join(export, '..', 'gitosis.conf')) gitweb.set_descriptions(config=cfg, ) generated = util.getGeneratedFilesDir(config=cfg) gitweb.generate_project_list( config=cfg, path=os.path.join(generated, 'projects.list'), ) gitdaemon.set_export_ok(config=cfg, ) authorized_keys = util.getSSHAuthorizedKeysPath(config=cfg) ssh.writeAuthorizedKeys( path=authorized_keys, keydir=os.path.join(export, 'keydir'), )
def test_git_daemon_export_ok_allowed_all(): 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-if-all", "yes") cfg.add_section("group all") cfg.set("group all", "readonly", "foo") cfg.add_section("group boo") cfg.set("group boo", "members", "@all") cfg.set("group boo", "readonly", "quux thud") 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)
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)
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)
def test_git_daemon_export_ok_allowed_all(): 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-if-all', 'yes') cfg.add_section('group all') cfg.set('group all', 'readonly', 'foo') cfg.add_section('group boo') cfg.set('group boo', 'members', '@all') cfg.set('group boo', 'readonly', 'quux thud') 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)
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_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_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_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_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_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_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_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_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_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)
repository.export(git_dir=git_dir, path=export) os.rename( os.path.join(export, 'gitosis.conf'), os.path.join(export, '..', 'gitosis.conf'), ) cfg.read(os.path.join(export, '..', 'gitosis.conf')) gitweb.set_descriptions( config=cfg, ) generated = util.getGeneratedFilesDir(config=cfg) gitweb.write_project_list(cfg, os.path.join(generated, 'projects.list')) gitdaemon.set_export_ok(cfg) authorized_keys = util.getSSHAuthorizedKeysPath(config=cfg) ssh.writeAuthorizedKeys(authorized_keys, os.path.join(export, 'keydir')) class Main(app.App): def create_parser(self): parser = super(Main, self).create_parser() parser.set_usage('%prog [OPTS] HOOK') parser.set_description( 'Perform gitosis actions for a git hook') return parser def handle_args(self, parser, cfg, options, args): try: (hook,) = args except ValueError:
def serve( cfg, user, command, ): if '\n' in command: raise CommandMayNotContainNewlineError() try: verb, args = command.split(None, 1) except ValueError: # all known "git-foo" commands take one argument; improve # if/when needed raise UnknownCommandError() if verb == 'git': try: subverb, args = args.split(None, 1) except ValueError: # all known "git foo" commands take one argument; improve # if/when needed raise UnknownCommandError() verb = '%s %s' % (verb, subverb) if (verb not in COMMANDS_WRITE and verb not in COMMANDS_READONLY): raise UnknownCommandError() match = ALLOW_RE.match(args) if match is None: raise UnsafeArgumentsError() path = match.group('path') # write access is always sufficient newpath = access.haveAccess( config=cfg, user=user, mode='writable', path=path) if newpath is None: # didn't have write access; try once more with the popular # misspelling newpath = access.haveAccess( config=cfg, user=user, mode='writeable', path=path) if newpath is not None: log.warning( 'Repository %r config has typo "writeable", ' +'should be "writable"', path, ) if newpath is None: # didn't have write access newpath = access.haveAccess( config=cfg, user=user, mode='readonly', path=path) if newpath is None: raise ReadAccessDenied() if verb in COMMANDS_WRITE: # didn't have write access and tried to write raise WriteAccessDenied() (topdir, relpath) = newpath assert not relpath.endswith('.git'), \ 'git extension should have been stripped: %r' % relpath repopath = '%s.git' % relpath fullpath = os.path.join(topdir, repopath) if (not os.path.exists(fullpath) and verb in COMMANDS_WRITE): # it doesn't exist on the filesystem, but the configuration # refers to it, we're serving a write request, and the user is # authorized to do that: create the repository on the fly # create leading directories p = topdir for segment in repopath.split(os.sep)[:-1]: p = os.path.join(p, segment) util.mkdir(p, 0755) repository.init(path=fullpath) gitweb.set_descriptions( config=cfg, ) generated = util.getGeneratedFilesDir(config=cfg) gitweb.generate_project_list( config=cfg, path=os.path.join(generated, 'projects.list'), ) gitdaemon.set_export_ok( config=cfg, ) # put the verb back together with the new path newcmd = "%(verb)s '%(path)s'" % dict( verb=verb, path=fullpath, ) return newcmd
os.path.join(export, 'gitosis.conf'), os.path.join(export, '..', 'gitosis.conf'), ) # re-read config to get up-to-date settings cfg.read(os.path.join(export, '..', 'gitosis.conf')) autoinit_repos(config=cfg) gitweb.set_descriptions( config=cfg, ) generated = util.getGeneratedFilesDir(config=cfg) gitweb.generate_project_list( config=cfg, path=os.path.join(generated, 'projects.list'), ) gitdaemon.set_export_ok( config=cfg, ) if htaccess.gen_htaccess_if_enabled(config=cfg): group.generate_group_list( config=cfg, path=os.path.join(generated, 'groups'), ) authorized_keys = util.getSSHAuthorizedKeysPath(config=cfg) ssh.writeAuthorizedKeys( path=authorized_keys, keydir=os.path.join(export, 'keydir'), ) def update_mirrors(cfg, git_dir): mirror.push_mirrors(cfg, git_dir)
os.rename( os.path.join(export, 'gitosis.conf'), os.path.join(export, '..', 'gitosis.conf'), ) # re-read config to get up-to-date settings cfg.read(os.path.join(export, '..', 'gitosis.conf')) gitweb.set_descriptions( config=cfg, ) generated = util.getGeneratedFilesDir(config=cfg) gitweb.generate_project_list( config=cfg, path=os.path.join(generated, 'projects.list'), ) gitdaemon.set_export_ok( config=cfg, ) authorized_keys = util.getSSHAuthorizedKeysPath(config=cfg) ssh.writeAuthorizedKeys( path=authorized_keys, keydir=os.path.join(export, 'keydir'), ) class Main(app.App): def create_parser(self): parser = super(Main, self).create_parser() parser.set_usage('%prog [OPTS] HOOK') parser.set_description( 'Perform gitosis actions for a git hook') return parser
def serve( cfg, user, command, ): if '\n' in command: raise CommandMayNotContainNewlineError() try: verb, args = command.split(None, 1) except ValueError: # all known "git-foo" commands take one argument; improve # if/when needed raise UnknownCommandError() if verb == 'git': try: subverb, args = args.split(None, 1) except ValueError: # all known "git foo" commands take one argument; improve # if/when needed raise UnknownCommandError() verb = '%s %s' % (verb, subverb) if (verb not in COMMANDS_WRITE and verb not in COMMANDS_READONLY): raise UnknownCommandError() match = ALLOW_RE.match(args) if match is None: raise UnsafeArgumentsError() path = match.group('path') # write access is always sufficient newpath = access.haveAccess( config=cfg, user=user, mode='writable', path=path) if newpath is None: # didn't have write access; try once more with the popular # misspelling newpath = access.haveAccess( config=cfg, user=user, mode='writeable', path=path) if newpath is not None: log.warning( 'Repository %r config has typo "writeable", ' +'should be "writable"', path, ) if newpath is None: # didn't have write access newpath = access.haveAccess( config=cfg, user=user, mode='readonly', path=path) if newpath is None: raise ReadAccessDenied() if verb in COMMANDS_WRITE: # didn't have write access and tried to write raise WriteAccessDenied() (topdir, relpath) = newpath assert not relpath.endswith('.git'), \ 'git extension should have been stripped: %r' % relpath repopath = '%s.git' % relpath fullpath = os.path.join(topdir, repopath) if not os.path.exists(fullpath): # it doesn't exist on the filesystem, but the configuration # refers to it, we're serving a write request, and the user is # authorized to do that: create the repository on the fly # create leading directories p = topdir for segment in repopath.split(os.sep)[:-1]: p = os.path.join(p, segment) util.mkdir(p, 0750) repository.init(path=fullpath) gitweb.set_descriptions( config=cfg, ) generated = util.getGeneratedFilesDir(config=cfg) gitweb.generate_project_list( config=cfg, path=os.path.join(generated, 'projects.list'), ) gitdaemon.set_export_ok( config=cfg, ) # put the verb back together with the new path newcmd = "%(verb)s '%(path)s'" % dict( verb=verb, path=fullpath, ) return newcmd
def serve( cfg, user, command, ): if '\n' in command: raise CommandMayNotContainNewlineError() try: verb, args = command.split(None, 1) except ValueError: # all known "git-foo" commands take one argument; improve # if/when needed raise UnknownCommandError() if verb == 'git': try: subverb, args = args.split(None, 1) except ValueError: # all known "git foo" commands take one argument; improve # if/when needed raise UnknownCommandError() verb = '%s %s' % (verb, subverb) elif verb == 'cvs': try: args, server = args.split(None, 1) except: raise UnknownCommandError() if server != 'server': raise UnknownCommandError() path = path_from_args(args) newpath = path_for_write(cfg=cfg, user=user, path=path) if newpath is None: raise WriteAccessDenied() (topdir, repopath) = construct_path(newpath) # Put the repository and base path in the environment repos_dir = util.getRepositoryDir(cfg) fullpath = os.path.join(repos_dir, repopath) os.environ['GIT_CVSSERVER_BASE_PATH'] = repos_dir os.environ['GIT_CVSSERVER_ROOTS'] = fullpath # Put the user's information in the environment try: section = 'user %s' % user name = cfg.get(section, 'name') email = cfg.get(section, 'email') except: log.error('Missing name or email for user "%s"' % user) raise WriteAccessDenied() os.environ['GIT_AUTHOR_NAME'] = name os.environ['GIT_AUTHOR_EMAIL'] = email return 'cvs server' if (verb not in COMMANDS_WRITE and verb not in COMMANDS_READONLY): raise UnknownCommandError() path = path_from_args(args) # write access is always sufficient newpath = path_for_write( cfg=cfg, user=user, path=path) if newpath is None: # didn't have write access newpath = access.haveAccess( config=cfg, user=user, mode='readonly', path=path) if newpath is None: raise ReadAccessDenied() if verb in COMMANDS_WRITE: # didn't have write access and tried to write raise WriteAccessDenied() (topdir, repopath) = construct_path(newpath) fullpath = os.path.join(topdir, repopath) if not os.path.exists(fullpath): # it doesn't exist on the filesystem, but the configuration # refers to it, we're serving a write request, and the user is # authorized to do that: create the repository on the fly auto_init_repo(cfg,topdir,repopath) gitweb.set_descriptions( config=cfg, ) generated = util.getGeneratedFilesDir(config=cfg) gitweb.generate_project_list( config=cfg, path=os.path.join(generated, 'projects.list'), ) gitdaemon.set_export_ok( config=cfg, ) htaccess.gen_htaccess_if_enabled( config=cfg, ) # put the verb back together with the new path newcmd = "%(verb)s '%(path)s'" % dict( verb=verb, path=fullpath, ) return newcmd