#!/usr/bin/env python## Copyright 2008-2009 LShift Ltd# Copyright 2005-2007 by Intevation GmbH <intevation@intevation.de># Authors:# Paul Crowley <paul@lshift.net># Thomas Arendsen Hein <thomas@intevation.de># with ideas from Mathieu PASQUET <kiorky@cryptelium.net>## This software may be used and distributed according to the terms# of the GNU General Public License, incorporated herein by reference."""hg-ssh - limit access to hg repositories reached via ssh. Part ofmercurial-server.This script is called by hg-ssh-wrapper with no arguments - everythingshould be in enviroment variables:HG_ACCESS_RULES_PATH identifies the paths to the rule filesREMOTE_USER the remote user (which is the key used by ssh)SSH_ORIGINAL_COMMAND the command the user was trying to runIt uses SSH_ORIGINAL_COMMAND to determine what the user was trying todo and to what repository, and then checks each rule in the rule filein turn for a matching rule which decides what to do, defaulting todisallowing the action."""# enable importing on demand to reduce startup timefrom mercurial import demandimport; demandimport.enable()from mercurial import dispatchimport sys, os, os.pathfrom mercurialserver import ruleset, pathsdef fail(message): sys.stderr.write("mercurial-server: %s\n" % message) sys.exit(-1)def checkpath(path) path = os.path.dirname(path) if path == "": return if os.path.exists(path + "/.hg"): raise ruleset.AccessException() checkpath(path)def getrepo(op, repo): repo = os.path.normcase(os.path.normpath(repo.rstrip("/"))) if len(repo) == 0: fail("path to repository seems to be empty") if repo.startswith("/"): fail("absolute paths are not supported") for component in repo.split("/"): if component.startswith("."): fail("paths cannot contain dot file components") ruleset.rules.set(repo=repo) ruleset.rules.check(op, branch=None, file=None) checkpath(repo) return repo#logfile = open("/tmp/hg-ssh.%d.txt" % os.getpid(), "w")#logfile.write("Started: %s\n" % sys.argv)if len(sys.argv) != 2: fail("hg-ssh wrongly called, is authorized_keys corrupt? (%s)" % sys.argv)paths.setExePath()ruleset.rules.set(user = sys.argv[1])# Use a different hgrc for remote pulls - this way you can set# up access.py for everything at once without affecting local operationsos.environ['HGRCPATH'] = paths.getEtcPath() + "/remote-hgrc"os.chdir('repos')for f in [ paths.getEtcPath() + "/access.conf", os.getcwd() + "/hgadmin/access.conf"]: if os.path.isfile(f): ruleset.rules.readfile(f)cmd = os.environ.get('SSH_ORIGINAL_COMMAND', '')try: if cmd.startswith('hg -R ') and cmd.endswith(' serve --stdio'): repo = getrepo("read", cmd[6:-14]) if not os.path.isdir(repo + "/.hg") fail("no such repository %s" % repo) dispatch.dispatch(['-R', repo, 'serve', '--stdio']) elif cmd.startswith('hg init '): repo = getrepo("init", cmd[8:]) if os.path.exists(repo): fail("%s exists" % repo) d = os.path.dirname(repo) if d != "" and not os.path.isdir(d): os.makedirs(d) dispatch.dispatch(['init', repo]) elif cmd == "": fail("direct logins on the hg account prohibited ") else: fail("illegal command %r" % cmd)except ruleset.AccessException: fail("access denied")