/
gitwrap.py
executable file
·119 lines (102 loc) · 3.15 KB
/
gitwrap.py
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#!/usr/bin/env python
#
# Simple wrapper around the git commands to ensure that we only speak
# git. Should be referenced in the authorized_keys file.
#
# Copyright (C) 2010 PostgreSQL Global Development Group
# Author: Magnus Hagander <magnus@hagander.net>
#
# Released under the PostgreSQL license
#
# Looks for a config file in the same directory as gitwrap.py called
# gitwrap.ini, containing:
#
# [paths]
# logfile=/some/where/gitwrap.log
# repobase=/some/where/
#
import sys
import os
import os.path
import datetime
import ConfigParser
ALLOWED_COMMANDS = ('git-upload-pack', 'git-receive-pack')
class Logger(object):
def __init__(self, cfg):
self.user = "Unknown"
self.logfile = cfg.get('paths','logfile')
def log(self, message):
f = open(self.logfile,"a")
f.write("%s (%s): %s" % (datetime.datetime.now(), self.user, message))
f.write("\n")
f.close()
def setuser(self, user):
if user:
self.user = user
class InternalException(Exception):
pass
class PgGit(object):
user = None
command = None
path = None
def __init__(self, cfg):
self.cfg = cfg
self.logger = Logger(cfg)
self.repobase = cfg.get('paths', 'repobase')
def parse_commandline(self):
if len(sys.argv) != 2:
raise InternalException("Can only be run with one commandline argument!")
self.user = sys.argv[1]
self.logger.setuser(self.user)
def parse_command(self):
env = os.environ.get('SSH_ORIGINAL_COMMAND', None)
if not env:
raise InternalException("No SSH_ORIGINAL_COMMAND present!")
# env contains "git-<command> <argument>" or "git <command> <argument>"
command, args = env.split(None, 1)
if command == "git":
subcommand, args = args.split(None,1)
command = "git-%s" % subcommand
if not command in ALLOWED_COMMANDS:
raise InternalException("Command '%s' not allowed" % command)
self.command = command
if not args.startswith("'/"):
raise InternalException("Expected git path to start with slash!")
self.path = os.path.normpath("%s/%s" % (self.repobase, args[2:].rstrip("'")))
if not self.path.startswith(self.repobase):
raise InternalException("Attempt to escape root directory")
if not self.path.endswith(".git"):
raise InternalException("Repository paths must end in .git")
if not os.path.isdir(self.path):
raise InternalException("Repository does not exist")
def run_command(self):
self.logger.log("Running \"git shell %s %s\"" % (self.command, "'%s'" % self.path))
os.execvp('git', ['git', 'shell', '-c', "%s %s" % (self.command, "'%s'" % self.path) ])
def run(self):
try:
self.parse_commandline()
self.parse_command()
self.run_command()
except InternalException, e:
try:
self.logger.log(e)
except Exception, e:
pass
sys.stderr.write("%s\n" % e)
sys.exit(1)
except Exception, e:
try:
self.logger.log(e)
except Exception, e:
# If we failed to log, try once more with a new logger, otherwise,
# just accept that we failed.
try:
Logger().log(e)
except:
pass
sys.stderr.write("An unhandled exception occurred on the server\n")
sys.exit(1)
if __name__ == "__main__":
c = ConfigParser.ConfigParser()
c.read("%s/gitwrap.ini" % os.path.abspath(sys.path[0]))
PgGit(c).run()