aboutsummaryrefslogtreecommitdiff
path: root/gitosis/ssh.py
diff options
context:
space:
mode:
Diffstat (limited to 'gitosis/ssh.py')
-rw-r--r--gitosis/ssh.py66
1 files changed, 43 insertions, 23 deletions
diff --git a/gitosis/ssh.py b/gitosis/ssh.py
index a315a5c..bfd52fb 100644
--- a/gitosis/ssh.py
+++ b/gitosis/ssh.py
@@ -1,14 +1,14 @@
+"""
+Gitosis code to handle SSH authorized_keys files
+"""
import os, errno, re
import logging
+from gitosis import sshkey
+# C0103 - 'log' is a special name
+# pylint: disable-msg=C0103
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``
@@ -20,30 +20,42 @@ def readKeys(keydir):
if ext != '.pub':
continue
- if not isSafeUsername(basename):
+ if not sshkey.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:
+ fp = file(path)
+ for line in fp:
line = line.rstrip('\n')
- yield (basename, line)
- f.close()
+ if line.startswith('#'):
+ continue
+ line = line.strip()
+ if len(line) > 0:
+ yield (basename, sshkey.get_ssh_pubkey(line))
+ fp.close()
COMMENT = '### autogenerated by gitosis, DO NOT EDIT'
+SSH_KEY_ACCEPTED_OPTIONS = ['from']
def generateAuthorizedKeys(keys):
- TEMPLATE=('command="gitosis-serve %(user)s",no-port-forwarding,'
- +'no-X11-forwarding,no-agent-forwarding,no-pty %(key)s')
+ """
+ Genarate the lines for the Gitosis ~/.ssh/authorized_keys.
+ """
+ TEMPLATE = ('%(options)s %(key)s %(comment)s')
+ OPTIONS = ('command="gitosis-serve %(user)s",no-port-forwarding,'
+ +'no-X11-forwarding,no-agent-forwarding,no-pty')
yield COMMENT
for (user, key) in keys:
- yield TEMPLATE % dict(user=user, key=key)
+ options = OPTIONS % dict(user=user, )
+ for k in SSH_KEY_ACCEPTED_OPTIONS:
+ if k in key.options:
+ options += (',%s="%s"' % (k, key.options[k]))
+ yield TEMPLATE % dict(user=user, key=key.key, comment=key.comment, options=options)
-_COMMAND_RE = re.compile('^command="(/[^ "]+/)?gitosis-serve [^"]+",no-port-forw'
- +'arding,no-X11-forwarding,no-agent-forwardi'
- +'ng,no-pty .*')
+_GITOSIS_CMD_RE = '(/[^ "]+/)?gitosis-serve [^ "]+$'
+_COMMAND_RE = re.compile(_GITOSIS_CMD_RE)
def filterAuthorizedKeys(fp):
"""
@@ -56,16 +68,24 @@ def filterAuthorizedKeys(fp):
line = line.rstrip('\n')
if line == COMMENT:
continue
- if _COMMAND_RE.match(line):
- continue
+ try:
+ key = sshkey.get_ssh_pubkey(line)
+ if 'command' in key.options and \
+ _COMMAND_RE.match(key.options['command']):
+ continue
+ except sshkey.MalformedSSHKey:
+ pass
yield line
def writeAuthorizedKeys(path, keydir):
+ """
+ Update the Gitosis ~/.ssh/authorized_keys for the new Gitosis SSH key data.
+ """
tmp = '%s.%d.tmp' % (path, os.getpid())
try:
in_ = file(path)
- except IOError, e:
- if e.errno == errno.ENOENT:
+ except IOError, ex: #pragma: no cover
+ if ex.errno == errno.ENOENT:
in_ = None
else:
raise
@@ -75,11 +95,11 @@ def writeAuthorizedKeys(path, keydir):
try:
if in_ is not None:
for line in filterAuthorizedKeys(in_):
- print >>out, line
+ print >> out, line
keygen = readKeys(keydir)
for line in generateAuthorizedKeys(keygen):
- print >>out, line
+ print >> out, line
os.fsync(out)
finally: