gitosis/gitosis/init.py
2019-07-31 18:20:02 +02:00

166 lines
4.8 KiB
Python

"""
Initialize a user account for use with gitosis.
"""
import errno
import logging
import os
import sys
import re
from pkg_resources import resource_filename
from cStringIO import StringIO
from ConfigParser import RawConfigParser
from gitosis import repository
from gitosis import run_hook
from gitosis import ssh
from gitosis import util
from gitosis import app
log = logging.getLogger('gitosis.init')
def read_ssh_pubkey(fp=None):
if fp is None:
fp = sys.stdin
line = fp.readline()
return line
class InsecureSSHKeyUsername(Exception):
"""Username contains not allowed characters"""
def __str__(self):
return '%s: %s' % (self.__doc__, ': '.join(self.args))
def ssh_extract_user(pubkey):
if not bool(re.search(r"\s", pubkey)):
_, user = pubkey.rsplit(None, 1)
else:
user = pubkey.strip()
if ssh.isSafeUsername(user):
return user
else:
raise InsecureSSHKeyUsername(repr(user))
def initial_commit(git_dir, cfg, pubkey, user):
log.debug('create initial commit')
log.info('User: ' + user)
if pubkey is None:
keyfile = 'keydir/principals'
content = user
else:
keyfile = 'keydir/%s.pub' % user
content = pubkey
log.debug('keyfile' + keyfile)
log.debug('content' + content)
repository.fast_import(
git_dir=git_dir,
commit_msg='Automatic creation of gitosis repository.',
committer='Gitosis Admin <%s>' % user,
files=[
(keyfile, content),
('gitosis.conf', cfg),
],
)
def symlink_config(git_dir):
dst = os.path.expanduser('~/.gitosis.conf')
tmp = '%s.%d.tmp' % (dst, os.getpid())
try:
os.unlink(tmp)
except OSError, e:
if e.errno == errno.ENOENT:
pass
else:
raise
os.symlink(
os.path.join(git_dir, 'gitosis.conf'),
tmp,
)
os.rename(tmp, dst)
def init_admin_repository(
git_dir,
pubkey,
user,
):
repository.init(
path=git_dir,
template=resource_filename('gitosis.templates', 'admin')
)
repository.init(
path=git_dir,
)
# can't rely on setuptools and all kinds of distro packaging to
# have kept our templates executable, it seems
os.chmod(os.path.join(git_dir, 'hooks', 'post-update'), 0755)
if not repository.has_initial_commit(git_dir):
log.info('Making initial commit...')
# ConfigParser does not guarantee order, so jump through hoops
# to make sure [gitosis] is first
cfg_file = StringIO()
print >>cfg_file, '[gitosis]'
print >>cfg_file
cfg = RawConfigParser()
cfg.add_section('group gitosis-admin')
cfg.set('group gitosis-admin', 'members', user)
cfg.set('group gitosis-admin', 'writable', 'gitosis-admin')
cfg.write(cfg_file)
initial_commit(
git_dir=git_dir,
cfg=cfg_file.getvalue(),
pubkey=pubkey,
user=user,
)
class Main(app.App):
def create_parser(self):
parser = super(Main, self).create_parser()
parser.set_usage('%prog [OPTS]')
parser.set_description(
'Initialize a user account for use with gitosis')
return parser
def read_config(self, *a, **kw):
# ignore errors that result from non-existent config file
try:
super(Main, self).read_config(*a, **kw)
except app.ConfigFileDoesNotExistError:
pass
def handle_args(self, parser, cfg, options, args):
super(Main, self).handle_args(parser, cfg, options, args)
os.umask(0022)
log.info('Reading SSH public key...')
pubkey = read_ssh_pubkey()
user = ssh_extract_user(pubkey)
if not " " in pubkey:
pubkey = None
log.debug("pubkey: %s", pubkey)
if user is None:
log.error('Cannot parse user from SSH public key.')
sys.exit(1)
log.info('Admin user is %r', user)
log.info('Creating generated files directory...')
generated = util.getGeneratedFilesDir(config=cfg)
util.mkdir(generated)
log.info('Creating repository structure...')
repositories = util.getRepositoryDir(cfg)
util.mkdir(repositories)
admin_repository = os.path.join(repositories, 'gitosis-admin.git')
init_admin_repository(
git_dir=admin_repository,
pubkey=pubkey,
user=user,
)
log.info('Running post-update hook...')
util.mkdir(os.path.expanduser('~/.ssh'), 0700)
run_hook.post_update(cfg=cfg, git_dir=admin_repository)
log.info('Symlinking ~/.gitosis.conf to repository...')
symlink_config(git_dir=admin_repository)
log.info('Done.')