hg-ssh
changeset 18 538d6b198f4a
parent 15 f3654416d178
child 19 62ee928ac9b3
child 20 f4daa224dc7e
--- a/hg-ssh	Mon Apr 21 12:37:56 2008 +0100
+++ b/hg-ssh	Tue Apr 22 09:46:29 2008 +0100
@@ -2,7 +2,7 @@
 #
 # Copyright 2008 LShift Ltd
 # Copyright 2005-2007 by Intevation GmbH <intevation@intevation.de>
-# Author(s):
+# Authors:
 # Paul Crowley <paul@lshift.net>
 # Thomas Arendsen Hein <thomas@intevation.de>
 # with ideas from  Mathieu PASQUET <kiorky@cryptelium.net>
@@ -14,15 +14,18 @@
 hg-ssh - limit access to hg repositories reached via ssh.  Part of
 hg-admin-tools.
 
-This script is called by hg-ssh-wrapper with two arguments:
-
-hg-ssh <rulefile> <keyname>
+This script is called by hg-ssh-wrapper with no arguments - everything
+should be in enviroment variables:
 
-It expects to find the command the SSH user was trying to run in the
-environment variable SSH_ORIGINAL_COMMAND, and uses it to determine
-what the user was trying to do and to what repository, and then checks
-each rule in the rule file in turn for a matching rule which decides
-what to do, defaulting to disallowing the action.
+HG_ACCESS_RULES_FILE identifies the path to the rules file
+REMOTE_USER the remote user (which is the key used by ssh)
+SSH_ORIGINAL_COMMAND the command the user was trying to run
+
+It uses SSH_ORIGINAL_COMMAND to determine what the user was trying to
+do and to what repository, and then checks each rule in the rule file
+in turn for a matching rule which decides what to do, defaulting to
+disallowing the action.
+
 """
 
 # enable importing on demand to reduce startup time
@@ -30,70 +33,44 @@
 
 from mercurial import dispatch
 
-import sys, os, re
+import sys, os
+import rules
 
 def fail(message):
     #logfile.write("Fail: %s\n" % message)
     sys.stderr.write(message + "\n")
     sys.exit(-1)
 
-# Note that this currently disallows dots in path components - if you change it
-# to allow them ensure that "." and ".." are disallowed in path components.
-allowedchars = "A-Za-z0-9_-"
-goodpathre = re.compile("([%s]+/)*[%s]+$" % (allowedchars, allowedchars))
-def goodpath(path):
-    if goodpathre.match(path) is None:
+def getpath(path):
+    if path.endswith("/"):
+        path = path[:-1]
+    if not rules.goodpath(path):
         fail("Disallowing path: %s" % path)
-
-# Don't put anything except *A-Za-z0-9_- in rule globs or
-# you'll probably break security.  No regexp metachars, not even .
-# We may fix this later.
-goodglobre = re.compile("[*/%s]+$" % allowedchars)
-def globmatch(pattern, match):
-    if goodglobre.match(pattern) is None:
-        fail("Bad glob pattern in auth config: %s" % pattern)
-    pattern = pattern.replace(".", r'\.')
-    pattern = pattern.replace("*", "[%s]*" % allowedchars)
-    return re.compile(pattern + "$").match(match) is not None
+    return path
 
-def testrule(rulefile, keyname, path, applicable):
-    goodpath(keyname)
-    goodpath(path)
-    f = open(rulefile)
-    try:
-        for l in f:
-            l = l.strip()
-            if l == "" or l.startswith("#"):
-                continue
-            rule, rk, rp = l.split()
-            if globmatch(rk, keyname) and globmatch(rp, path):
-                #logfile.write("Used rule: %s\n" % l)
-                return rule in applicable
-    finally:
-        f.close()
-    return False
-
-def get_cmd(rulefile, keyname, cmd):
+def get_cmd(rules, remoteuser, cmd):
     if cmd.startswith('hg -R ') and cmd.endswith(' serve --stdio'):
-        path = cmd[6:-14]
-        if testrule(rulefile, keyname, path, set(["allow", "init"])):
-            return ['-R', path, 'serve', '--stdio']
+        repo = getpath(cmd[6:-14])
+        if rules.allow("read", user=remoteuser, repo=repo, file=None):
+            os.environ["HG_REPO_PATH"] = repo
+            return ['-R', repo, 'serve', '--stdio']
     elif cmd.startswith('hg init '):
-        path = cmd[8:]
-        if testrule(rulefile, keyname, path, set(["init"])):
-            return ['init', path]
+        repo = getpath(cmd[8:])
+        if rules.allow("init", user=remoteuser, repo=repo, file=None):
+            os.environ["HG_REPO_PATH"] = repo
+            return ['init', repo]
     fail("Illegal command %r" % cmd)
 
 #logfile = open("/tmp/hg-ssh.%d.txt" % os.getpid(), "w")
 #logfile.write("Started: %s\n" % sys.argv)
 
-if len(sys.argv) != 3:
-    fail("hg-ssh must have exactly two arguments (%s)" 
+if len(sys.argv) != 1:
+    fail("hg-ssh must have no arguments (%s)" 
         % sys.argv)
 
-rulefile = sys.argv[1]
-keyname = sys.argv[2]
-todispatch = get_cmd(rulefile, keyname, 
+rules = rules.Ruleset.readfile(os.environ['HG_ACCESS_RULES_FILE'])
+remoteuser = getpath(os.environ['REMOTE_USER'])
+todispatch = get_cmd(rules, remoteuser, 
     os.environ.get('SSH_ORIGINAL_COMMAND', '?'))
 dispatch.dispatch(todispatch)