--- a/.hgtags Sun Dec 19 09:49:18 2010 +0000
+++ b/.hgtags Tue Sep 06 11:16:58 2011 +0100
@@ -4,10 +4,11 @@
95c9ab8e4bfc6fea6460b3147c3097373eba5d42 debian_0.7
1ad9d5841a48a77f68dc5350bd1f941327a6348a release_0.8
1e4050abb96e72c6324b93709e56a3e135e63ce1 debian_0.8-1
-d42d3f5311c55cab668d6962a61d44ba98645e release_0.9
+fed42d3f5311c55cab668d6962a61d44ba98645e release_0.9
b6887a9b8792bb2b69b428448102140fce121e29 debian_0.9-1
8ce190faa5c2b50f63cc5b11e28daf98836498d8 release_1.0
60c2d676a754e02f1f7656a4b137399438b2ed35 debian_1.0-1
92cb6640a6417edaf52870c8a97000e11bb8b138 release_1.0.1
c7aca86585824f39fa47015d6ebf1bab6a25b82c debian_1.0.1-1
+01eca64f77abbf0da41cd777a365de2a5a02988b release_1.1
32dba1a70a5443cf86a78b5a9471ad7ced034a12 debian_1.1-1
--- a/NEWS Sun Dec 19 09:49:18 2010 +0000
+++ b/NEWS Tue Sep 06 11:16:58 2011 +0100
@@ -40,7 +40,7 @@
* Belatedly added NEWS file :-)
Upgrading: move the paths/hgrc entry in .mercurial-server to env/HGRCPATH,
-and add an entry under paths that reads
+and add an entry under paths that reads
"authorized_keys = ~/.ssh/authorized_keys"
====================
@@ -89,4 +89,3 @@
====================
* First numbered release
-
--- a/README Sun Dec 19 09:49:18 2010 +0000
+++ b/README Tue Sep 06 11:16:58 2011 +0100
@@ -2,7 +2,7 @@
mercurial-server gives your developers remote read/write access to
centralized Mercurial repositories using SSH public key authentication; it
-provides convenient and fine-grained key management and access control.
+provides convenient and fine-grained key management and access control.
http://www.lshift.net/mercurial-server.html
@@ -25,7 +25,7 @@
Though mercurial-server is currently targeted at Debian-based systems such
as Ubuntu, other users have reported success getting it running on other
Unix-based systems such as Red Hat. Running it on a non-Unix system such as
-Windows is not supported. You will need root privileges to install it.
+Windows is not supported. You will need root privileges to install it.
The best way to install mercurial-server is using your package management
system - there are pre-built .deb files on the website. However, there is
@@ -41,4 +41,3 @@
See doc/manual.docbook for the rest of the documentation.
Paul Crowley, paul@lshift.net, 2010
-
--- a/dev/chroot-test/action/go Sun Dec 19 09:49:18 2010 +0000
+++ b/dev/chroot-test/action/go Tue Sep 06 11:16:58 2011 +0100
@@ -15,7 +15,7 @@
perl -i -pe 's/^Port 22$/Port 2222/' /etc/ssh/sshd_config
/etc/init.d/ssh start
-ssh-keyscan -p 2222 localhost > /etc/ssh/ssh_known_hosts
+ssh-keyscan -t rsa,dsa,ecdsa -p 2222 localhost > /etc/ssh/ssh_known_hosts
. ./install-installables
@@ -33,4 +33,3 @@
/etc/init.d/ssh stop
#touch results
-
--- a/dev/chroot-test/action/test1 Sun Dec 19 09:49:18 2010 +0000
+++ b/dev/chroot-test/action/test1 Tue Sep 06 11:16:58 2011 +0100
@@ -43,4 +43,9 @@
echo "Pushing changes"
hg clone . ssh://chroothg/real/project
hg clone nested ssh://chroothg/real/project/nested
+cd ..
+echo "Creating an mq repository"
+hg init qrepo
+(cd qrepo ; hg --config extensions.mq= qinit -c)
+hg --config extensions.mq= qclone qrepo ssh://chroothg/qrepo
echo "Done for user test1"
--- a/dev/chroot-test/run-test Sun Dec 19 09:49:18 2010 +0000
+++ b/dev/chroot-test/run-test Tue Sep 06 11:16:58 2011 +0100
@@ -31,6 +31,7 @@
fi
if [ -e $BACKING ] ; then
echo "Copying deb files into cache"
+ mkdir -p $BUILDDIR/aptcache/$DEBVERSION
cp $BACKING/var/cache/apt/archives/* $BUILDDIR/aptcache/$DEBVERSION || true
echo "Deleting old filesystem backing store"
rm -rf $BACKING
@@ -59,4 +60,4 @@
mount -t proc proc $MOUNT/proc
chroot $MOUNT ./action/go
-
+echo "Completed successfully"
--- a/doc/manual.docbook Sun Dec 19 09:49:18 2010 +0000
+++ b/doc/manual.docbook Tue Sep 06 11:16:58 2011 +0100
@@ -45,7 +45,7 @@
<systemitem class="systemname">spoon</systemitem> and you have
installed mercurial-server on <systemitem
class="systemname">jeeves</systemitem> using the package management system (see the README for more on installation). We assume that you have created your SSH public key, set up your SSH agent with this key, and that this key gives you access to <systemitem
-class="systemname">jeeves</systemitem>.
+class="systemname">jeeves</systemitem>.
</para>
<screen><computeroutput>jay@spoon:~$ </computeroutput><userinput>ssh -A jeeves</userinput>
<computeroutput>jay@jeeves:~$ </computeroutput><userinput>ssh-add -L > my-key</userinput>
@@ -532,4 +532,3 @@
</section>
</section>
</article>
-
--- a/setup.py Sun Dec 19 09:49:18 2010 +0000
+++ b/setup.py Tue Sep 06 11:16:58 2011 +0100
@@ -14,10 +14,9 @@
scripts = ['src/hg-ssh', 'src/refresh-auth'],
data_files = [
('init', [
- 'src/init/hginit',
- 'src/init/dot-mercurial-server',
+ 'src/init/hginit',
+ 'src/init/dot-mercurial-server',
'src/init/hgadmin-hgrc'
]),
],
)
-
--- a/src/hg-ssh Sun Dec 19 09:49:18 2010 +0000
+++ b/src/hg-ssh Tue Sep 06 11:16:58 2011 +0100
@@ -19,6 +19,11 @@
from mercurial import dispatch
+try:
+ request = dispatch.request
+except AttributeError:
+ request = list
+
import sys, os, os.path
import base64
from mercurialserver import config, ruleset
@@ -27,12 +32,41 @@
sys.stderr.write("mercurial-server: %s\n" % message)
sys.exit(-1)
-def checkDots(path):
+config.initExe()
+
+for k,v in config.getEnv():
+ os.environ[k.upper()] = v
+
+if len(sys.argv) == 3 and sys.argv[1] == "--base64":
+ ruleset.rules.set(user = base64.b64decode(sys.argv[2]))
+elif len(sys.argv) == 2:
+ ruleset.rules.set(user = sys.argv[1])
+else:
+ fail("hg-ssh wrongly called, is authorized_keys corrupt? (%s)"
+ % sys.argv)
+
+os.chdir(config.getReposPath())
+
+for f in config.getAccessPaths():
+ if os.path.isfile(f):
+ ruleset.rules.readfile(f)
+
+alloweddots = config.getAllowedDots()
+
+def dotException(pathtail):
+ for ex in alloweddots:
+ splex = ex.split("/")
+ if len(pathtail) >= len(splex) and pathtail[:len(splex)] == splex:
+ return True
+ return False
+
+def checkDots(path, pathtail = []):
head, tail = os.path.split(path)
- if tail.startswith("."):
- fail("paths cannot contain dot file components")
+ pathtail = [tail] + pathtail
+ if tail.startswith(".") and not dotException(pathtail):
+ fail("paths cannot contain dot file components")
if head:
- checkDots(head)
+ checkDots(head, pathtail)
def getrepo(op, repo):
# First canonicalise, then check the string, then the rules
@@ -47,25 +81,6 @@
fail("access denied")
return repo
-config.initExe()
-
-for k,v in config.getEnv():
- os.environ[k.upper()] = v
-
-if len(sys.argv) == 3 and sys.argv[1] == "--base64":
- ruleset.rules.set(user = base64.b64decode(sys.argv[2]))
-elif len(sys.argv) == 2:
- ruleset.rules.set(user = sys.argv[1])
-else:
- fail("hg-ssh wrongly called, is authorized_keys corrupt? (%s)"
- % sys.argv)
-
-os.chdir(config.getReposPath())
-
-for f in config.getAccessPaths():
- if os.path.isfile(f):
- ruleset.rules.readfile(f)
-
cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None)
if cmd is None:
fail("direct logins on the hg account prohibited")
@@ -73,7 +88,7 @@
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'])
+ dispatch.dispatch(request(['-R', repo, 'serve', '--stdio']))
elif cmd.startswith('hg init '):
repo = getrepo("init", cmd[8:])
if os.path.exists(repo):
@@ -81,7 +96,6 @@
d = os.path.dirname(repo)
if d != "" and not os.path.isdir(d):
os.makedirs(d)
- dispatch.dispatch(['init', repo])
+ dispatch.dispatch(request(['init', repo]))
else:
fail("illegal command %r" % cmd)
-
--- a/src/init/conf/remote-hgrc.d/access.rc Sun Dec 19 09:49:18 2010 +0000
+++ b/src/init/conf/remote-hgrc.d/access.rc Tue Sep 06 11:16:58 2011 +0100
@@ -2,4 +2,3 @@
[hooks]
pretxnchangegroup.access = python:mercurialserver.access.hook
-
--- a/src/init/dot-mercurial-server Sun Dec 19 09:49:18 2010 +0000
+++ b/src/init/dot-mercurial-server Tue Sep 06 11:16:58 2011 +0100
@@ -7,9 +7,12 @@
keys = /etc/mercurial-server/keys:~/repos/hgadmin/keys
access = /etc/mercurial-server/access.conf:~/repos/hgadmin/access.conf
+[exceptions]
+# Allow the creation of mq repositories by default
+allowdots = .hg/patches
+
[env]
# Use a different hgrc for remote pulls - this way you can set
# up access.py for everything at once without affecting local operations
HGRCPATH = /etc/mercurial-server/remote-hgrc.d
-
--- a/src/init/hginit Sun Dec 19 09:49:18 2010 +0000
+++ b/src/init/hginit Tue Sep 06 11:16:58 2011 +0100
@@ -14,4 +14,3 @@
cd repos/hgadmin
hg init .
cp $1/init/hgadmin-hgrc .hg/hgrc
-
--- a/src/mercurialserver/access.py Sun Dec 19 09:49:18 2010 +0000
+++ b/src/mercurialserver/access.py Tue Sep 06 11:16:58 2011 +0100
@@ -25,4 +25,3 @@
if not allow(ctx):
raise mercurial.util.Abort(_('%s: access denied for changeset %s') %
(__name__, mercurial.node.short(ctx.node())))
-
--- a/src/mercurialserver/config.py Sun Dec 19 09:49:18 2010 +0000
+++ b/src/mercurialserver/config.py Tue Sep 06 11:16:58 2011 +0100
@@ -20,7 +20,7 @@
def _getPath(name):
return os.path.expanduser(_getConf().get("paths", name))
-def _getPaths(name):
+def _getPaths(name):
return [os.path.expanduser(p)
for p in _getConf().get("paths", name).split(":")]
@@ -40,6 +40,17 @@
def getEnv(): return _getConf().items("env")
+def _getdefault(section, option, default, f = lambda x: x):
+ conf = _getConf()
+ if conf.has_option(section, option):
+ return f(conf.get(section, option))
+ else:
+ return default
+
+def getAllowedDots():
+ return _getdefault("exceptions", "allowdots", [],
+ lambda s: s.split(":"))
+
# Work out where we are, don't use config.
def initExe():
global _exePath
@@ -49,4 +60,3 @@
def getExePath():
return _exePath
-
--- a/src/mercurialserver/refreshauth.py Sun Dec 19 09:49:18 2010 +0000
+++ b/src/mercurialserver/refreshauth.py Tue Sep 06 11:16:58 2011 +0100
@@ -39,7 +39,7 @@
if not goodkey.match(keyname):
# Encode it for safe quoting
keyname = "--base64 " + base64.b64encode(keyname)
- p = subprocess.Popen(("ssh-keygen", "-i", "-f", ffn),
+ p = subprocess.Popen(("ssh-keygen", "-i", "-f", ffn),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
newkey = p.communicate()[0]
if p.wait() == 0:
@@ -57,7 +57,6 @@
akeys.close()
os.chmod(akeyfile + "_new", stat.S_IRUSR)
os.rename(akeyfile + "_new", akeyfile)
-
+
def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
refreshAuth()
-
--- a/src/mercurialserver/ruleset.py Sun Dec 19 09:49:18 2010 +0000
+++ b/src/mercurialserver/ruleset.py Tue Sep 06 11:16:58 2011 +0100
@@ -13,63 +13,56 @@
# ** means "match recursively" ie "ignore directories"
return re.compile(p.replace("[^/]*[^/]*", ".*") + "$")
-# Returns True for a definite match
-# False for a definite non-match
-# None where we can't be sure because a key is None
+# Returns 1 for a definite match
+# -1 for a definite non-match
+# 0 where we can't be sure because a key is None
+def rmatch(k, m, kw):
+ if k not in kw:
+ return -1
+ kkw = kw[k]
+ if kkw is None:
+ return 0
+ elif m.match(kkw) is None:
+ return -1
+ else:
+ return 1
+
def rule(pairs):
matchers = [(k, globmatcher(v)) for k, v in pairs]
def c(kw):
- for k, m in matchers:
- if k not in kw:
- return False
- kkw = kw[k]
- if kkw is None:
- return None
- if m.match(kkw) is None:
- return False
- return True
+ return min(rmatch(k, m, kw) for k, m in matchers)
return c
class Ruleset(object):
'''Class representing the rules in a rule file'''
-
+
levels = ["init", "write", "read", "deny"]
def __init__(self):
self.rules = []
self.preset = {}
- def add(self, action, conditions):
- self.rules.append((action, conditions))
-
def set(self, **kw):
self.preset.update(kw)
-
+
def get(self, k):
return self.preset.get(k, None)
-
- def matchrules(self, kw):
+
+ def allow(self, level, **kw):
+ levelindex = self.levels.index(level)
d = self.preset.copy()
d.update(kw)
- res = set()
for a, c in self.rules:
m = c(d)
- if m is None:
- # "Maybe match" - add it and carry on
- res.add(a)
- elif m:
- # Definite match - add it and stop
- res.add(a)
- break
- return res
-
- def allow(self, level, **kw):
- for a in self.matchrules(kw):
- if a in self.levels:
- if self.levels.index(a) <= self.levels.index(level):
+ if m == 1:
+ # Definite match - what it says goes
+ return a <= levelindex
+ elif m == 0:
+ # "Maybe match" - allow if it says yes, ignore if no
+ if a <= levelindex:
return True
return False
-
+
def readfile(self, fn):
f = open(fn)
try:
@@ -78,9 +71,14 @@
if len(l) == 0 or l.startswith("#"):
continue
l = l.split()
- self.add(l[0], rule([c.split("=", 1) for c in l[1:]]))
+ # Unrecognized actions are off the high end
+ if l[0] in self.levels:
+ ix = self.levels.index(l[0])
+ else:
+ ix = len(self.levels)
+ self.rules.append((ix,
+ rule([c.split("=", 1) for c in l[1:]])))
finally:
f.close()
rules = Ruleset()
-
--- a/src/mercurialserver/servelog.py Sun Dec 19 09:49:18 2010 +0000
+++ b/src/mercurialserver/servelog.py Tue Sep 06 11:16:58 2011 +0100
@@ -9,7 +9,13 @@
import os
import time
import fcntl
-import json
+
+try:
+ import json
+ json.dumps
+except ImportError:
+ import simplejson as json
+
from mercurialserver import ruleset, changes
def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
@@ -25,13 +31,13 @@
fcntl.flock(log.fileno(), fcntl.LOCK_EX)
log.seek(0, os.SEEK_END)
# YAML log file format
- log.write("- {0}\n".format(json.dumps(dict(
+ log.write("- %s\n" % json.dumps(dict(
timestamp=time.strftime("%Y-%m-%d_%H:%M:%S Z", time.gmtime()),
op=op,
key=ruleset.rules.get('user'),
ssh_connection=os.environ['SSH_CONNECTION'],
nodes=[mercurial.node.hex(ctx.node())
for ctx in changes.changes(repo, node)],
- ))))
+ )))
finally:
log.close()