aboutsummaryrefslogtreecommitdiff
blob: 3fe846f9acb8a328aad898c7c719b00893660fa1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import os, errno, re
import logging

log = logging.getLogger('gitosis.ssh')

_ACCEPTABLE_USER_RE = re.compile(r'^[a-z][a-z0-9_.-]*(@[a-z][a-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)