90 lines
2.3 KiB
Python
90 lines
2.3 KiB
Python
import os, errno, re
|
|
import logging
|
|
|
|
log = logging.getLogger('gitosis.ssh')
|
|
|
|
_ACCEPTABLE_USER_RE = re.compile(r'^[a-zA-Z][a-zA-Z0-9_.-]*(@[a-zA-Z][a-zA-Z0-9.-]*)?$')
|
|
|
|
def isSafeUsername(user):
|
|
match = _ACCEPTABLE_USER_RE.match(user)
|
|
return (match is not None)
|
|
|
|
def readKeys(keydir):
|
|
"""
|
|
Read SSH public keys from ``keydir/*.pub``
|
|
"""
|
|
for filename in os.listdir(keydir):
|
|
if filename.startswith('.'):
|
|
continue
|
|
basename, ext = os.path.splitext(filename)
|
|
if ext != '.pub':
|
|
continue
|
|
|
|
if not isSafeUsername(basename):
|
|
log.warn('Unsafe SSH username in keyfile: %r', filename)
|
|
continue
|
|
|
|
path = os.path.join(keydir, filename)
|
|
f = file(path)
|
|
for line in f:
|
|
line = line.rstrip('\n')
|
|
yield (basename, line)
|
|
f.close()
|
|
|
|
COMMENT = '### autogenerated by gitosis, DO NOT EDIT'
|
|
|
|
def generateAuthorizedKeys(keys):
|
|
TEMPLATE=('command="gitosis-serve %(user)s",no-port-forwarding,'
|
|
+'no-X11-forwarding,no-agent-forwarding,no-pty %(key)s')
|
|
|
|
yield COMMENT
|
|
for (user, key) in keys:
|
|
yield TEMPLATE % dict(user=user, key=key)
|
|
|
|
_COMMAND_RE = re.compile('^command="(/[^ "]+/)?gitosis-serve [^"]+",no-port-forw'
|
|
+'arding,no-X11-forwarding,no-agent-forwardi'
|
|
+'ng,no-pty .*')
|
|
|
|
def filterAuthorizedKeys(fp):
|
|
"""
|
|
Read lines from ``fp``, filter out autogenerated ones.
|
|
|
|
Note removes newlines.
|
|
"""
|
|
|
|
for line in fp:
|
|
line = line.rstrip('\n')
|
|
if line == COMMENT:
|
|
continue
|
|
if _COMMAND_RE.match(line):
|
|
continue
|
|
yield line
|
|
|
|
def writeAuthorizedKeys(path, keydir):
|
|
tmp = '%s.%d.tmp' % (path, os.getpid())
|
|
try:
|
|
in_ = file(path)
|
|
except IOError, e:
|
|
if e.errno == errno.ENOENT:
|
|
in_ = None
|
|
else:
|
|
raise
|
|
|
|
try:
|
|
out = file(tmp, 'w')
|
|
try:
|
|
if in_ is not None:
|
|
for line in filterAuthorizedKeys(in_):
|
|
print >>out, line
|
|
|
|
keygen = readKeys(keydir)
|
|
for line in generateAuthorizedKeys(keygen):
|
|
print >>out, line
|
|
|
|
os.fsync(out)
|
|
finally:
|
|
out.close()
|
|
finally:
|
|
if in_ is not None:
|
|
in_.close()
|
|
os.rename(tmp, path)
|