--- a/.hgignore Tue Oct 13 18:32:26 2009 +0100
+++ b/.hgignore Mon Nov 09 16:23:04 2009 +0000
@@ -1,3 +1,4 @@
+^build/
syntax: glob
*~
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile Mon Nov 09 16:23:04 2009 +0000
@@ -0,0 +1,58 @@
+#!/usr/bin/env make -f
+
+TOPDIR=
+PREFIX=$(TOPDIR)/usr/local/share
+LIBDIR=$(PREFIX)/mercurial-server
+DOCDIR=$(PREFIX)/doc/mercurial-server
+ETCDIR=$(TOPDIR)/etc/mercurial-server
+NEWUSER=hg
+
+INSTALL=install
+
+build: build/html/index.html pythonbuild
+
+setup-adduser: installfiles adduser inituser
+
+# WARNING: this is experimental
+setup-useradd: installfiles useradd inituser
+
+installetc:
+ $(INSTALL) -d $(ETCDIR)
+ $(INSTALL) -m 644 -t $(ETCDIR) \
+ src/init/conf/remote-hgrc src/init/conf/access.conf
+ $(INSTALL) -d $(ETCDIR)/keys/root
+ $(INSTALL) -d $(ETCDIR)/keys/user
+
+installdoc: build/html/index.html
+ $(INSTALL) -d $(DOCDIR)
+ $(INSTALL) -m 644 -t $(DOCDIR) README build/html/index.html
+
+build/html/index.html: doc/manual.docbook
+ xsltproc -o $@ /usr/share/xml/docbook/stylesheet/nwalsh/html/docbook.xsl $^
+
+pythonbuild:
+ python setup.py build
+
+pythoninstall:
+ python setup.py install \
+ --install-purelib=$(LIBDIR) \
+ --install-platlib=$(LIBDIR) \
+ --install-scripts=$(LIBDIR) \
+ --install-data=$(LIBDIR)
+
+installfiles: installetc installdoc pythoninstall
+
+adduser:
+ adduser --system --shell /bin/sh --group --disabled-password \
+ --home /var/lib/mercurial-server \
+ --gecos "Mercurial repositories" $(NEWUSER)
+
+# WARNING: this is experimental
+useradd:
+ useradd --system --shell /bin/sh \
+ --home /var/lib/mercurial-server --create-home \
+ --comment "Mercurial repositories" $(NEWUSER)
+
+inituser:
+ su -l -c "$(LIBDIR)/init/hginit $(LIBDIR)" $(NEWUSER)
+
--- a/README Tue Oct 13 18:32:26 2009 +0100
+++ b/README Mon Nov 09 16:23:04 2009 +0000
@@ -1,8 +1,6 @@
mercurial-server
-mercurial-server makes a group of repositories available to the developers
-you choose, identified by ssh keys, with easy key and access management
-based on hg.
+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.
http://www.lshift.net/mercurial-server.html
@@ -22,134 +20,17 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-SUMMARY
-
-mercurial-server makes a group of repositories available to the developers
-you choose, identified by ssh keys, with easy key and access management
-based on hg.
-
-All of the repositories controlled by mercurial-server are owned by a
-single user (the "hg" user in what follows), but many remote users can act
-on them, and different users can have different permissions. We don't use
-file permissions to achieve that - instead, developers log in as the "hg"
-user when they connect to the repository host using ssh, using ssh URLs of
-the form "ssh://hg@repository-host/repository-name". A restricted shell
-prevents them from using this access for unauthorized purposes. Developers
-are authenticated only using SSH keys; no other form of authentication is
-supported.
-
-To give a user access to the repository, place their key in an
-appropriately-named subdirectory of "/etc/mercurial-server/keys" and run
-"/usr/local/share/mercurial-server/refresh-auth". You can then control what
-access they have to what repositories by editing the control file
-"/etc/mercurial-server/access.conf", which can match the names of these keys
-against a glob pattern.
-
-For convenient remote control of access, you can instead (if you have the
-privileges) make changes to a special repository called "hgadmin", which
-contains its own "access.conf" file and "keys" directory. Changes pushed to
-this repository take effect immediately. The two "access.conf" files are
-concatenated, and the keys directories merged.
-
-QUICK START
+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.
-You and all developers using this system will need an SSH public key, and
-will almost certainly want to be running ssh-agent (or its equivalent, eg
-Pageant under Windows). If you're not familiar with ssh-agent, you should
-learn about that before using this.
-
-In what follows, certain operations (eg installing mercurial-server itself)
-have to be done on the repository server (which we call "repository-host"),
-but any operation that involves checking in or out of Mercurial can be done
-wherever is most convenient to you; the most usual arrangment would be that
-you'd do these things at the machine you sit at, and on which you run
-ssh-agent, which is what authenticates you when you talk to the repository
-server.
+The best way to install mercurial-server is using your package management system. However, there is some provision for installing it directly. On Debian based systems such as Ubuntu, use the command
-Ensure there is no user called "hg" on the repository host, and run
-"./install". This installs the mercurial-server files and control files, and
-creates and sets up the "hg" user.
-
-Place your SSH public key in the directory "/etc/mercurial-server/keys/root".
-I suggest creating yourself a directory and naming the key after your hostname
-(ie the file is called something like
-"/etc/mercurial-server/keys/root/yourname/yourhostname") so that you can
-easily manage users who have a different key on each host they use. Then run
-"/usr/local/share/mercurial-server/refresh-auth".
-
-The repository is now ready to use, and you are now the sole user able to
-change and create repositories on this repository host.
-
-CREATING REPOSITORIES
-
-To create a new repository, you clone a local repository onto the remote
-server. So if you want a new empty repository called "myproject", you can do
-(as yourself):
+ sudo make setup-adduser
- hg init myproject
- hg clone myproject ssh://hg@repository-host/myproject
-
-ADDING OTHER USERS
-
-Because your key is in the "keys/root" subdirectory, you have the equivalent
-of "root privileges" over mercurial-server (not the whole computer, just
-mercurial-server). You can add other root users by putting their keys next to
-yours, or you can make less privileged users by putting their keys in the
-"keys/users" subdirectory - these users will be able to read and write to any
-repository (except one - see below) but will not be able to create new
-repositories. As always, when you change "/etc/mercurial-server/keys" you need
-to re-run "/usr/local/share/mercurial-server/refresh-auth".
-
-LOGGING
-
-Every push and pull is logged with the key used: see the file .hg/serve-log in
-each repository.
-
-USING HGADMIN
-
-It can be inconvenient to log on to the repository server, become root, copy
-keys around, and run "refresh-auth" every time you want to change user
-privileges. This is where mercurial-server shines :-) Suppose you have another
-user's SSH public key in the file "/tmp/theirkey" (on the machine you sit at,
-not necessarily the repository server) and you want to give them user-level
-access to the repository server. Run these commands:
+On Red Hat and possibly other variants of Unix, try
- hg clone ssh://hg@repository-server/hgadmin
- cd hgadmin
- mkdir keys/users/thatuser
- cp /tmp/theirkey keys/users/thatuser/theirhostname
- hg add
- hg commit -m "Added key for thatuser"
- hg push
-
-In other words, hgadmin is a version controlled version of
-"/etc/mercurial-server", and changes to it take effect immediately -
-"refresh-auth" is run after every push.
-
-With the default access.conf file (see doc/configuring-access for more
-details) only users in "keys/root" can act on "hgadmin" - those with keys in
-"keys/users" cannot even read this repository. So multiple admins can use
-Mercurial's version control to cooperate on controlling access to the
-repository server in a natural way.
+ sudo make setup-useradd
-You can also create an "access.conf" file in hgadmin, and this is appended to
-/etc/mercurial-server/access.conf whenever this is read - in other words,
-rules in the latter take precedence over those in the former. So once you're
-working with "hgadmin", it can be convenient to remove all the keys in
-"/etc/mercurial-server/keys" and all the entries in
-"/etc/mercurial-server/access.conf" and use hgadmin to control everything. If
-you find yourself locked out, you can get back in again by restoring some of
-the entries you removed from these files.
-
-MORE INFORMATION
-
-For more on how to use mercurial-server and configure access, see the files in
-the doc directory.
-
-THANKS
-
-Thanks for reading this far. If you use mercurial-server, please tell me about
-it.
+See doc/manual.docbook for the rest of the documentation.
Paul Crowley, paul@lshift.net, 2009
--- a/debian/dirs Tue Oct 13 18:32:26 2009 +0100
+++ b/debian/dirs Mon Nov 09 16:23:04 2009 +0000
@@ -1,5 +1,6 @@
usr/share/mercurial-server
usr/share/mercurial-server/init
+usr/share/mercurial-server/init/conf
usr/share/mercurial-server/mercurialserver
usr/share/doc/mercurial-server
etc/mercurial-server
--- a/debian/docs Tue Oct 13 18:32:26 2009 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-README
-doc/configuring-access
-doc/file-conditions
-doc/how-it-works
-doc/security
-
--- a/debian/rules Tue Oct 13 18:32:26 2009 +0100
+++ b/debian/rules Mon Nov 09 16:23:04 2009 +0000
@@ -16,7 +16,6 @@
touch configure-stamp
-
build: build-stamp
build-stamp: configure-stamp
@@ -43,27 +42,9 @@
dh_installdirs
# Add here commands to install the package into debian/mercurial-server.
- cp \
- src/hg-ssh \
- src/refresh-auth \
- debian/mercurial-server/usr/share/mercurial-server
- cp \
- src/mercurialserver/__init__.py \
- src/mercurialserver/paths.py \
- src/mercurialserver/changes.py \
- src/mercurialserver/access.py \
- src/mercurialserver/servelog.py \
- src/mercurialserver/refreshauth.py \
- src/mercurialserver/ruleset.py \
- debian/mercurial-server/usr/share/mercurial-server/mercurialserver
- cp \
- src/init/hginit \
- src/init/hgadmin-hgrc \
- debian/mercurial-server/usr/share/mercurial-server/init
- cp \
- src/init/conf/remote-hgrc \
- src/init/conf/access.conf \
- debian/mercurial-server/etc/mercurial-server
+ $(MAKE) installfiles \
+ TOPDIR=debian/mercurial-server \
+ PREFIX=debian/mercurial-server/usr/share
# Build architecture-independent files here.
binary-indep: build install
@@ -83,7 +64,7 @@
# dh_installemacsen
# dh_installpam
# dh_installmime
-# dh_python
+ dh_python
# dh_installinit
# dh_installcron
# dh_installinfo
--- a/doc/configuring-access Tue Oct 13 18:32:26 2009 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-ACCESS.CONF
-
-Out of the box, there are just two kinds of users: the ones with keys in
-"keys/root" and those in "keys/users". However, you can change this by
-editing "access.conf". There are two "access.conf" files, one in
-"/etc/mercurial-server" and one in "hgadmin"; the two are simply
-concatenated before being read.
-
-Each line of access.conf has the following syntax:
-
-<rule> <condition> <condition> ...
-
-Rule is one of
-
-init - allow any operation, including the creation of new repositories
-write - allow reads and writes to this file in this repository
-read - allow the repo to be read but reject matching writes
-deny - deny all requests
-
-A condition is a globpattern matched against a relative path. The two most
-important conditions are
-
- user=<globpattern> - user's key
- repo=<globpattern> - repo (as the user supplies it)
-
-The first rule in the file which has all its conditions satisfied is used
-to determine whether an action is allowed. If no rule is matched the
-request is denied.
-
-"*" only matches one directory level, where "**" matches as many as you
-want. More precisely, "*" matches zero or more characters not including "/"
-while "**" matches zero or more characters including "/".
-
-Blank lines and lines that start with "#" are ignored.
-
-access.conf ships with the following contents:
-
- init user=root/**
- deny repo=hgadmin
- write user=users/**
-
-This means: keys in "root" can do anything; keys in "users" cannot create
-repositories, cannot even read the hgadmin repository, but can read and
-write any other repository; no other key has any access.
-
-More advanced access configuration is covered in file-conditions.
-
--- a/doc/file-conditions Tue Oct 13 18:32:26 2009 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-FILE CONDITIONS
-
-Read configuring-access before you read this.
-
-mercurial-server supports file and branch conditions, which restrict an
-operation depending on what files it modifies and what branch the work is
-on. However, the way these conditions work is subtle and can be
-counterintuitive - if you want to keep things simple, stick to user and
-repo conditions, and then things are likely to work the way you would
-expect.
-
-File and branch conditions are added to the conditions against which a rule
-matches, just like user and repo conditions; they have this form:
-
- file=<globpattern> - file in the repo
- branch=<globpattern> - name of the branch
-
-However, in order to understand what effect adding these conditions will
-have, it helps to understand how and when these rules are applied.
-
-The rules file is used to make four decisions:
-
-- Whether to allow a repository to be created
-- Whether to allow access to a repository
-- Whether to allow a changeset on a particular branch at all
-- Whether to allow a changeset to change a particular file
-
-When the first two of these decisions are being made, nothing is known
-about what files might be changed, and so all file conditions automatically
-succeed for the purpose of such decisions. This means that doing tricky
-things with file conditions can have counterintuitive consequences:
-
-- You cannot limit read access to a subset of a repository with a "read"
-rule and a file condition: any user who has access to a repository can read
-all of it and its full history. Such a rule can only have the effect of
-masking a later "write" rule, as in this example:
-
- read repo=specialrepo file=dontwritethis
- write repo=specialrepo
-
-allows all users to read specialrepo, and to write to all files *except*
-that any changeset which writes to "dontwritethis" will be rejected.
-
-- For similar reasons, don't give "init" rules file conditions.
-
-- Don't try to deny write access to a particular file on a particular
-branch - a developer can write to the file on another branch and then merge
-it in. Either deny all writes to the branch from that user, or allow them
-to write to all the files they can write to on any branch. In other words,
-something like this will have the intended effect:
-
- write user=docs/* branch=docs file=docs/*
-
-But something like this will not have the intended effect; it will
-effectively allow these users to write to any file on any branch, by
-writing it to "docs" first:
-
- write user=docs/* branch=docs
- write user=docs/* file=docs/*
- read user=docs/*
-
-
--- a/doc/how-it-works Tue Oct 13 18:32:26 2009 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-HOW IT WORKS
-
-When a developer attempts to connect to a repository via ssh, the SSH
-daemon searches for a match for that user's key in
-~hg/.ssh/authorized_keys. If the developer is authorised to connect to the
-repository they will have an entry in this file. The entry includes a
-"command" prefix which specifies that the restricted shell
-"/usr/local/share/mercurial-server/hg-ssh" should be used; this shell is
-passed an argument identifying the developer. The shell parses the command
-the developer is trying to execute, and consults a rules file to see if
-that developer is allowed to perform that action on that repository.
-
-The file ~hg/.ssh/authorized_keys is generated by "refresh-auth", which
-recurses through two directories of files containing SSH keys and generates
-an entry in authorized_keys for each one, using the name of the key file as
-the identifier for the developer. These keys will live in the "keys"
-subdirectory "/etc/mercurial-server" and the "keys" subdirectory of a
-repository called "hgadmin". A hook in this repository re-runs
-"refresh-auth" on the most recent version after every push.
-
-When users try to commit new changesets, a hook is run which consults the
-rules file to decide whether to allow the changeset into the repository.
-This can depend not only on the user and the repository, but also the
-branch and files in the changeset.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/manual.docbook Mon Nov 09 16:23:04 2009 +0000
@@ -0,0 +1,527 @@
+<?xml version="1.0" encoding="utf-8"?>
+<article xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+<info>
+ <title>Sharing Mercurial repositories with mercurial-server</title>
+ <author><firstname>Paul</firstname><surname>Crowley</surname></author>
+ <copyright><year>2009</year><holder>Paul Crowley, LShift Ltd</holder></copyright>
+</info>
+<section>
+<title>About mercurial-server</title>
+<para>
+Home page: <link xlink:href="http://www.lshift.net/mercurial-server.html"/>
+</para>
+<para>
+mercurial-server gives your developers remote read/write access to
+centralized <link xlink:href="http://hg-scm.org/">Mercurial</link>
+repositories using SSH public key authentication; it provides convenient
+and fine-grained key management and access control.
+</para>
+<para>
+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.
+</para>
+</section>
+<section>
+<title>Step by step</title>
+<para>
+mercurial-server authenticates users not using passwords but using SSH
+public keys; everyone who wants access to a mercurial-server repository
+will need such a key. In combination with <command>ssh-agent</command> (or
+equivalents such as the Windows program <link
+xlink:href="http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter9.html#pageant">Pageant</link>),
+this means that users will not need to type in a password to access the
+repository. If you're not familiar with SSH public keys, the <link
+xlink:href="http://sial.org/howto/openssh/publickey-auth/">OpenSSH Public
+Key Authentication tutorial</link> may be helpful.
+</para>
+<section>
+<title>Installing mercurial-server</title>
+<para>
+In what follows, we assume that your username is <systemitem
+class="username">jay</systemitem>, that you usually sit at a machine called
+<systemitem class="systemname">spoon</systemitem> and you wish to
+install mercurial-server on <systemitem
+class="systemname">jeeves</systemitem>. 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>.
+</para>
+<para>First install mercurial-server on <systemitem
+class="systemname">jeeves</systemitem>:</para>
+<screen><computeroutput>jay@spoon:~$ </computeroutput><userinput>scp mercurial-server_0.6.1_amd64.deb jeeves:</userinput>
+<computeroutput>mercurial-server_0.6.1_amd64.deb 100%
+jay@spoon:~$ </computeroutput><userinput>ssh -A jeeves</userinput>
+<computeroutput>jay@jeeves:~$ </computeroutput><userinput>sudo dpkg -i mercurial-server_0.6.1_amd64.deb</userinput>
+<computeroutput>[sudo] password for jay:
+Selecting previously deselected package mercurial-server.
+(Reading database ... 144805 files and directories currently installed.)
+Unpacking mercurial-server (from .../mercurial-server_0.6.1_amd64.deb) ...
+Setting up mercurial-server (0.6.1) ...
+jay@jeeves:~$ </computeroutput></screen>
+<para>
+mercurial-server is now installed on the repository host. Next, we need to give you permission to access its repositories.
+</para>
+<screen><computeroutput>jay@jeeves:~$ </computeroutput><userinput>ssh-add -L > my-key</userinput>
+<computeroutput>jay@jeeves:~$ </computeroutput><userinput>sudo mkdir -p /etc/mercurial-server/keys/root/jay</userinput>
+<computeroutput>jay@jeeves:~$ </computeroutput><userinput>sudo cp my-key /etc/mercurial-server/keys/root/jay/spoon</userinput>
+<computeroutput>jay@jeeves:~$ </computeroutput><userinput>sudo -u hg /usr/share/mercurial-server/refresh-auth</userinput>
+<computeroutput>jay@jeeves:~$ </computeroutput><userinput>exit</userinput>
+<computeroutput>Connection to jeeves closed.
+jay@spoon:~$ </computeroutput></screen>
+<para>
+You can now create repositories on the remote machine and have complete
+read-write access to all of them.
+</para>
+</section>
+<section>
+<title>Creating repositories</title>
+<para>
+To store a repository on the server, clone it over.
+</para>
+<screen><computeroutput>jay@spoon:~$ </computeroutput><userinput>cd myproj</userinput>
+<computeroutput>jay@spoon:~/myproj$ </computeroutput><userinput>hg clone . ssh://hg@jeeves/jays/project</userinput>
+<computeroutput>searching for changes
+remote: adding changesets
+remote: adding manifests
+remote: adding file changes
+remote: added 119 changesets with 284 changes to 61 files
+jay@spoon:~/myproj$ </computeroutput><userinput>hg pull ssh://hg@jeeves/jays/project</userinput>
+<computeroutput>pulling from ssh://hg@jeeves/jays/project
+searching for changes
+no changes found
+<computeroutput>jay@spoon:~/myproj$ </computeroutput><userinput>cd ..</userinput>
+jay@spoon:~$ </computeroutput></screen>
+</section>
+<section>
+<title>Adding other users</title>
+<para>
+At this stage, no-one but you has any access to any repositories you
+create on this system. In order to give anyone else access, you'll need a
+copy of their SSH public key; we'll assume you have that key in
+<filename>~/sam-saucer-key.pub</filename>. To manage access, you make changes to the special <filename
+class='directory'>hgadmin</filename> repository.
+</para>
+<screen><computeroutput>jay@spoon:~$ </computeroutput><userinput>hg clone ssh://hg@jeeves/hgadmin</userinput>
+<computeroutput>destination directory: hgadmin
+no changes found
+updating working directory
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+jay@spoon:~$ </computeroutput><userinput>cd hgadmin</userinput>
+<computeroutput>jay@spoon:~/hgadmin$ </computeroutput><userinput>mkdir -p keys/users/sam</userinput>
+<computeroutput>jay@spoon:~/hgadmin$ </computeroutput><userinput>cp ~/sam-saucer-key.pub keys/users/sam/saucer</userinput>
+<computeroutput>jay@spoon:~/hgadmin$ </computeroutput><userinput>hg add</userinput>
+<computeroutput>adding keys/users/sam/saucer
+jay@spoon:~/hgadmin$ </computeroutput><userinput>hg commit -m "Add Sam's key"</userinput>
+<computeroutput>jay@spoon:~/hgadmin$ </computeroutput><userinput>hg push</userinput>
+<computeroutput>pushing to ssh://hg@jeeves/hgadmin
+searching for changes
+remote: adding changesets
+remote: adding manifests
+remote: adding file changes
+remote: added 1 changesets with 1 changes to 1 files
+jay@spoon:~/hgadmin$ </computeroutput></screen>
+<para>
+Sam can now read and write to your
+<uri>ssh://hg@jeeves/jays/project</uri> repository.
+Most other changes to access control can be made simply by making and
+pushing changes to <filename
+class='directory'>hgadmin</filename>, and you can use Mercurial to
+cooperate with other root users in the normal way.
+</para>
+<para>
+If you prefer, you could give them access by
+logging into <systemitem class="systemname">jeeves</systemitem>,
+putting the key in the right place under <filename
+class='directory'>/etc/mercurial-server/keys</filename>, and re-running
+<userinput>sudo -u hg /usr/share/mercurial-server/refresh-auth</userinput>.
+However, using <filename
+class='directory'>hgadmin</filename> is usually more convenient if you need to make more than a very few changes; it also makes it easier to share administration with others and provides a log of all changes.
+</para>
+</section>
+</section>
+<section>
+<title>Access control</title>
+<para>
+Out of the box, mercurial-server supports two kinds of users: "root" users and normal users. If you followed the steps above, you are a "root" user because your key is under <filename class='directory'>keys/root</filename>, while the other user you gave access to is a normal user since their key is under <filename class='directory'>keys/users</filename>. Keys that are not in either of these directories will by default have no access to anything.
+</para>
+<para>
+Root users can edit <filename
+class='directory'>hgadmin</filename>, create new repositories and read and write to existing ones. Normal users cannot access <filename
+class='directory'>hgadmin</filename> or create new repositories, but they can read and write to any other repository.
+</para>
+<section>
+<title>Using access.conf</title>
+<para>
+mercurial-server offers much more fine-grained access control than this division into two classes of users. Let's suppose you wish to give Pat access to the <filename
+class='directory'>widget</filename> repository, but no other. We first copy Pat's SSH public key into the <filename
+class='directory'>keys/pat</filename> directory in <filename
+class='directory'>hgadmin</filename>. This tells mercurial-server about Pat's key, but gives Pat no access to anything because the key is not under either <filename
+class='directory'>keys/root</filename> or <filename
+class='directory'>keys/users</filename>. To grant this key access, we must give mercurial-server a new access rule, so we create a file in <filename
+class='directory'>hgadmin</filename> called <filename>access.conf</filename>, with the following contents:</para>
+<programlisting># Give Pat access to the "widget" repository
+write repo=widget user=pat
+</programlisting>
+<para>
+Pat will have read and write access to the <filename
+class='directory'>widget</filename> repository as soon as we add, commit, and push these files.
+</para>
+<para>
+Each line of <filename>access.conf</filename> has the following syntax:
+</para>
+<programlisting><replaceable>rule</replaceable> <replaceable>condition</replaceable> <replaceable>condition...</replaceable>
+</programlisting>
+<para>
+Blank lines and lines that start with <code>#</code> are ignored. Rule is
+one of
+</para>
+<itemizedlist>
+<listitem>
+<literal>init</literal>: allow reads, writes, and the creation of new repositories
+</listitem>
+<listitem>
+<literal>write</literal>: allow reads and writes
+</listitem>
+<listitem>
+<literal>read</literal>: allow only read operations
+</listitem>
+<listitem>
+<literal>deny</literal>: deny all requests
+</listitem>
+</itemizedlist>
+<para>
+A condition is a globpattern matched against a relative path. The two most
+important conditions are
+</para>
+<itemizedlist>
+<listitem>
+<code>user=<replaceable>globpattern</replaceable></code>: path to the user's key
+</listitem>
+<listitem>
+<code>repo=<replaceable>globpattern</replaceable></code>: path to the repository
+</listitem>
+</itemizedlist>
+<para>
+<code>*</code> only matches one directory level, where <code>**</code>
+matches as many as you want. More precisely, <code>*</code> matches zero or
+more characters not including <code>/</code> while <code>**</code> matches
+zero or more characters including <code>/</code>. So
+<code>projects/*</code> matches <filename
+class='directory'>projects/foo</filename> but not <filename
+class='directory'>projects/foo/bar</filename>, while
+<code>projects/**</code> matches both.
+</para>
+<para>
+When considering a request, mercurial-server steps through all the rules in
+<filename>/etc/mercurial-server/access.conf</filename> and then all the
+rules in <filename>access.conf</filename> in <filename
+class='directory'>hgadmin</filename>
+looking for a rule which matches on every condition. The first match
+determines whether the request will be allowed; if there is no match in
+either file, the request will be denied.
+</para>
+<para>
+By default, <filename>/etc/mercurial-server/access.conf</filename> has the
+following rules:
+</para>
+<programlisting>init user=root/**
+deny repo=hgadmin
+write user=users/**
+</programlisting>
+<para>
+These rules ensure that root users can do any operation on any repository,
+that no other users can access the <filename
+class='directory'>hgadmin</filename> repository,
+and that those with keys in <filename
+class='directory'>keys/users</filename> can read or write to any repository
+but not create repositories. Some examples of how these rules work:
+</para>
+<itemizedlist>
+<listitem>
+User <filename class='directory'>root/jay</filename> creates a repository
+<filename class='directory'>foo/bar/baz</filename>. This matches the first
+rule and so will be allowed.
+</listitem>
+<listitem>
+User <filename class='directory'>root/jay</filename> changes repository
+<filename class='directory'>hgadmin</filename>. Again, this matches the
+first rule and so will be allowed; later rules have no effect.
+</listitem>
+<listitem>
+User <filename class='directory'>users/sam</filename> tries to read
+repository <filename class='directory'>hgadmin</filename>. This does not
+match the first rule, but matches the second, and so will be denied.
+</listitem>
+<listitem>
+User <filename class='directory'>users/sam</filename> tries to create
+repository <filename class='directory'>sams-project</filename>. This does
+not match the first two rules, but matches the third; this is a
+<literal>write</literal> rule, which doesn't grant the privilege to create
+repositories, so the request will be denied.
+</listitem>
+<listitem>
+User <filename class='directory'>users/sam</filename> writes to existing
+repository <filename class='directory'>projects/main</filename>. Again,
+this matches the third rule, which allows the request.
+</listitem>
+<listitem>
+User <filename class='directory'>pat</filename> tries to write to existing
+repository <filename class='directory'>widget</filename>. Until we change
+the <filename>access.conf</filename> file in <filename
+class='directory'>hgadmin</filename>, this will match no rule, and so will
+be denied.
+</listitem>
+<listitem>
+Any request from a user whose key not under the <filename
+class='directory'>keys</filename> directory at all will always be denied,
+no matter what rules are in effect; because of the way SSH authentication
+works, they will be prompted to enter a password, but no password will
+work. This can't be changed.
+</listitem>
+</itemizedlist>
+</section>
+<section>
+<title>/etc/mercurial-server and hgadmin</title>
+<para>
+mercurial-server consults two distinct locations to collect information about what to allow: <filename
+class='directory'>/etc/mercurial-server</filename> and its own <filename
+class='directory'>hgadmin</filename> repository. This is useful for several reasons:
+</para>
+<itemizedlist>
+<listitem>
+Some users may not need the convenience of access control via mercurial; for these users updating <filename
+class='directory'>/etc/mercurial-server</filename> may offer a simpler route.
+</listitem>
+<listitem>
+<filename class='directory'>/etc/mercurial-server</filename> is suitable
+for management with tools such as <link
+xlink:href="http://reductivelabs.com/products/puppet">Puppet</link>
+</listitem>
+<listitem>
+If a change to <filename
+class='directory'>hgadmin</filename> leaves you "locked out", <filename
+class='directory'>/etc/mercurial-server</filename> allows you a way back in.
+</listitem>
+<listitem>
+At install time, all users are "locked out", and so some mechanism to allow some users in is needed.
+</listitem>
+</itemizedlist>
+<para>
+Rules in <filename>/etc/mercurial-server/access.conf</filename> are checked before those in <filename
+class='directory'>hgadmin</filename>, and keys in <filename class='directory'>/etc/mercurial-server/keys</filename> will be present no matter how <filename
+class='directory'>hgadmin</filename> changes.
+</para>
+<para>
+We anticipate that once mercurial-server is successfully installed and
+working you will usually want to use <filename
+class='directory'>hgadmin</filename> for most
+access control tasks. Once you have the right keys and
+<filename>access.conf</filename> set up in <filename
+class='directory'>hgadmin</filename>, you
+can delete <filename>/etc/mercurial-server/access.conf</filename> and all
+of <filename class='directory'>/etc/mercurial-server/keys</filename>,
+turning control entirely over to <filename
+class='directory'>hgadmin</filename>.
+</para>
+<para>
+<filename>/etc/mercurial-server/remote-hgrc</filename> is in the
+<systemitem>HGRCPATH</systemitem> for all remote access to mercurial-server
+repositories. This file contains the hooks that mercurial-server uses for
+access control and logging. You can add hooks to this file, but obviously
+breaking the existing hooks will disable the relevant functionality and
+isn't advisable.
+</para>
+</section>
+<section>
+<title>File and branch conditions</title>
+<para>
+mercurial-server supports file and branch conditions, which restrict an
+operation depending on what files it modifies and what branch the work is
+on. </para>
+<caution>
+The way these conditions work is subtle and can be counterintuitive. Unless
+you need what they provide, ignore this section, stick to user and repo
+conditions, and then things are likely to work the way you would expect. If
+you do need what they provide, read what follows very carefully.
+</caution>
+<para>
+File and branch conditions are added to the conditions against which a rule
+matches, just like user and repo conditions; they have this form:
+</para>
+<itemizedlist>
+<listitem>
+<code>file=<replaceable>globpattern</replaceable></code>: file within the repo
+</listitem>
+<listitem>
+<code>branch=<replaceable>globpattern</replaceable></code>: Mercurial branch name
+</listitem>
+</itemizedlist>
+<para>
+However, in order to understand what effect adding these conditions will
+have, it helps to understand how and when these rules are applied.
+</para>
+<para>
+The rules file is used to make three decisions:
+</para>
+<itemizedlist>
+<listitem>
+Whether to allow a repository to be created
+</listitem>
+<listitem>
+Whether to allow any access to a repository
+</listitem>
+<listitem>
+Whether to allow a changeset
+</listitem>
+</itemizedlist>
+<para>
+When the first two of these decisions are being made, nothing is known
+about any changsets that might be pushed, and so all file and branch
+conditions automatically succeed for the purpose of such decisions. For the
+third condition, every file changed in the changeset must be allowed by a
+<literal>write</literal> or <literal>init</literal> rule for the changeset
+to be allowed.
+</para>
+<para>
+This means that doing tricky things with file conditions can have
+counterintuitive consequences:
+</para>
+<itemizedlist>
+<listitem>
+<para>You cannot limit read access to a subset of a repository with a <literal>read</literal>
+rule and a file condition: any user who has access to a repository can read
+all of it and its full history. Such a rule can only have the effect of
+masking a later <literal>write</literal> rule, as in this example:</para>
+<programlisting>read repo=specialrepo file=dontwritethis
+write repo=specialrepo
+</programlisting>
+<para>
+allows all users to read <literal>specialrepo</literal>, and to write to all files
+<emphasis>except</emphasis> that any changeset which writes to
+<filename>dontwritethis</filename> will be rejected.
+</para>
+</listitem>
+<listitem>
+For similar reasons, don't give <literal>init</literal> rules file conditions.
+</listitem>
+<listitem>
+<para>Don't try to deny write access to a particular file on a particular
+branch—a developer can write to the file on another branch and then merge
+it in. Either deny all writes to the branch from that user, or allow them
+to write to all the files they can write to on any branch.
+</para>
+<programlisting>write user=docs/* branch=docs file=docs/*
+</programlisting>
+<para>
+This rule grants users whose keys are in the <filename
+class='directory'>docs</filename> subdirectory the power to push changesets
+into any repository only if those changesets are on the
+<literal>docs</literal> branch and they affect only those files directly
+under the <filename class='directory'>docs</filename> directory. However,
+the rules below have more counterintuitive consequences.
+</para>
+<programlisting>write user=docs/* branch=docs
+write user=docs/* file=docs/*
+read user=docs/*
+</programlisting>
+<para>
+These rules grant users whose keys are in the <filename
+class='directory'>docs</filename> subdirectory the power to change any file directly under the <filename class='directory'>docs</filename> directory, or any file at all in the <literal>docs</literal> branch. Indirectly, however, this adds up to the power to change any file on any branch, simply by making the change on the docs branch and then merging the change into another branch.
+</para>
+</listitem>
+</itemizedlist>
+</section>
+</section>
+<section>
+<title>How mercurial-server works</title>
+<para>
+All of the repositories controlled by mercurial-server are owned by a
+single user, the <systemitem
+class="username">hg</systemitem> user, which is why all URLs for
+mercurial-server repositories start with <uri>ssh://hg@...</uri>.
+Each SSH key that has access to the repository has an entry in
+<filename>~hg/.ssh/authorized_keys</filename>; this is how the SSH daemon
+knows to give that key access. When the user connects over SSH, their
+commands are run in a custom restricted shell; this shell knows which key
+was used to connect, determines what the user is trying to do, checks the
+access rules to decide whether to allow it, and if allowed invokes
+Mercurial internally, without forking.
+</para>
+<para>
+This restricted shell also ensures that certain Mercurial extensions are
+loaded when the user acts on a repository; these extensions check the
+access control rules for any changeset that the user tries to commit, and
+log all pushes and pulls into a per-repository access log.
+</para>
+<para>
+<command>refresh-auth</command> recurses through the <filename
+class='directory'>/etc/mercurial-server/keys</filename> and the <filename
+class='directory'>keys</filename> directory in the
+<filename
+class='directory'>hgadmin</filename> repository, creating an entry in
+<filename>~hg/.ssh/authorized_keys</filename> for each one. This is redone
+automatically whenever a change is pushed to <filename
+class='directory'>hgadmin</filename>.
+</para>
+</section>
+<section>
+<title>Security</title>
+<para>
+mercurial-server relies entirely on <command>sshd</command> to grant access to remote users.
+As a result, it runs no daemons, installs no setuid programs, and no part
+of it runs as <systemitem
+class="username">root</systemitem> except the install process: all programs run as the user
+<systemitem
+class="username">hg</systemitem>. Any attack on mercurial-server can only be started if the attacker
+already has a public key in <filename>~hg/.ssh/authorized_keys</filename>,
+otherwise <command>sshd</command> will bar the way.
+</para>
+<para>
+No matter what command the user tries to run on the remote system via SSH,
+mercurial-server is run. It parses the command line the user asked for, and
+interprets and runs the corresponding operation itself if access is
+allowed, so users can only read and add to history within repositories;
+they cannot run any other command. In addition, every push and pull is
+logged with a datestamp, changeset ID and the key that performed the
+operation.
+</para>
+<para>
+However, while the first paragraph holds no matter what bugs
+mercurial-server contains, the second depends on the relevant code being
+correct; though the entire codebase is short, mercurial-server is a fairly
+new program and may harbour bugs. Backups are essential!
+</para>
+</section>
+<section>
+<title>Legalese</title>
+<para>
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+</para>
+<para>
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+</para>
+<para>
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc., 51
+Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+</para>
+</section>
+<section>
+<title>Thanks</title>
+<para>
+Thanks for reading this far. If you use mercurial-server, please tell me about
+it.
+</para>
+<para>
+Paul Crowley, <email>paul@lshift.net</email>, 2009
+</para>
+</section>
+</article>
+
--- a/doc/security Tue Oct 13 18:32:26 2009 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-SECURITY OF MERCURIAL-SERVER
-
-mercurial-server relies entirely on sshd to grant access to remote users.
-As a result, it runs no daemons, installs no setuid programs, and no part
-of it runs as root except the install process: all programs run as the user
-hg. And any attack on mercurial-server can only be started if the Bad Guys
-already have a public key in ~hg/.ssh/authorized_keys, otherwise sshd will
-bar the way.
-
-No matter what command the user tries to run on the remote system via ssh,
-mercurial-server is run. It parses the command line the user asked for, and
-interprets and runs the corresponding hg operation itself if access is
-allowed, so users can only read and add to history within repositories;
-they cannot run any other hg command. In addition, every push and pull is
-logged with a datestamp, changeset ID and the key that performed the
-operation.
-
-However, while the first paragraph holds no matter what bugs
-mercurial-server contains, the second depends on the relevant code being
-correct; though the entire codebase is short, mercurial-server is a fairly
-new program and may harbour bugs. Backups are essential!
--- a/install Tue Oct 13 18:32:26 2009 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-import shutil
-import os
-import pwd
-import subprocess
-import optparse
-
-oparser = optparse.OptionParser()
-
-oparser.add_option("--prefix")
-oparser.add_option("--root")
-oparser.set_defaults(root="", prefix="/usr/local")
-(options, args) = oparser.parse_args()
-
-if len(args) > 0:
- oparser.print_help()
- sys.exit(-1)
-
-# This must be run as root, because it must create an hg user.
-# Normally the clean thing to do is let it fail with a permission error
-# if a non-root user tries to run it, but I don't want anyone thinking
-# that they can make it work as non-root by changing install paths.
-# Patches for doing this more cleanly welcome of course.
-
-if os.getgid() != 0:
- print >>sys.stderr, "Install must be run as root user"
- sys.exit(-1)
-
-
-def installFiles(d, *sources):
- d = options.root + d
- os.makedirs(d)
- for f in sources:
- shutil.copy(f, d)
-
-installFiles(options.prefix + '/share/mercurial-server',
- 'src/hg-ssh',
- 'src/refresh-auth')
-installFiles(options.prefix + '/share/mercurial-server/mercurialserver',
- 'src/mercurialserver/__init__.py',
- 'src/mercurialserver/paths.py',
- 'src/mercurialserver/changes.py',
- 'src/mercurialserver/access.py',
- 'src/mercurialserver/servelog.py',
- 'src/mercurialserver/refreshauth.py',
- 'src/mercurialserver/ruleset.py')
-installFiles(options.prefix + '/share/mercurial-server/init',
- 'src/init/hginit',
- 'src/init/hgadmin-hgrc')
-installFiles(options.prefix + '/share/doc/mercurial-server',
- 'README',
- 'doc/configuring-access',
- 'doc/file-conditions',
- 'doc/how-it-works',
- 'doc/security')
-installFiles('/etc/mercurial-server',
- 'src/init/conf/remote-hgrc',
- 'src/init/conf/access.conf')
-installFiles('/etc/mercurial-server/keys/root')
-installFiles('/etc/mercurial-server/keys/users')
-
-def becomeFunc(u):
- p = pwd.getpwnam(u)
- def become():
- os.setgid(p.pw_gid)
- os.setegid(p.pw_gid)
- os.setuid(p.pw_uid)
- os.seteuid(p.pw_uid)
- return become
-
-if options.root == '':
- try:
- pwd.getpwnam('hg')
- except KeyError:
- subprocess.check_call(
- "adduser --system --shell /bin/sh --group --disabled-password".split() +
- ["--gecos", "Mercurial repositories", "hg"])
- subprocess.check_call([options.prefix + '/share/mercurial-server/init/hginit',
- options.prefix + '/share/mercurial-server'],
- preexec_fn = becomeFunc('hg'))
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/setup.py Mon Nov 09 16:23:04 2009 +0000
@@ -0,0 +1,26 @@
+# WARNING: this file is NOT meant to be directly executed, but
+# run from the Makefile.
+
+from distutils.core import setup
+
+setup(
+ name="mercurial-server",
+ description="Centralized Mercurial repository manager",
+ url="http://www.lshift.net/mercurial-server.html",
+ version="0.6+", # FIXME: infer this
+ package_dir = {'': 'src'},
+ packages = ["mercurialserver"],
+ requires = ["mercurial"], # FIXME: what version?
+ scripts = ['src/hg-ssh', 'src/refresh-auth'],
+ data_files = [
+ ('init', [
+ 'src/init/hginit',
+ 'src/init/dot-mercurial-server',
+ 'src/init/hgadmin-hgrc'
+ ]), ('init/conf', [
+ 'src/init/conf/remote-hgrc',
+ 'src/init/conf/access.conf',
+ ]),
+ ],
+)
+
--- a/src/hg-ssh Tue Oct 13 18:32:26 2009 +0100
+++ b/src/hg-ssh Mon Nov 09 16:23:04 2009 +0000
@@ -84,13 +84,11 @@
# Use a different hgrc for remote pulls - this way you can set
# up access.py for everything at once without affecting local operations
-os.environ['HGRCPATH'] = paths.getEtcPath() + "/remote-hgrc"
-
-os.chdir('repos')
+os.environ['HGRCPATH'] = paths.getHgrcPaths()
-for f in [
- paths.getEtcPath() + "/access.conf",
- os.getcwd() + "/hgadmin/access.conf"]:
+os.chdir(paths.getReposPath())
+
+for f in paths.getAccessPaths():
if os.path.isfile(f):
ruleset.rules.readfile(f)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/init/dot-mercurial-server Mon Nov 09 16:23:04 2009 +0000
@@ -0,0 +1,9 @@
+# WARNING: a .mercurial-server file in your home directory means
+# that refresh-auth can and will trash your ~/.ssh/authorized_keys file.
+
+[paths]
+repos = ~/repos
+keys = /etc/mercurial-server/keys:~/repos/hgadmin/keys
+access = /etc/mercurial-server/access.conf:~/repos/hgadmin/access.conf
+hgrc = /etc/mercurial-server/remote-hgrc
+
--- a/src/init/hginit Tue Oct 13 18:32:26 2009 +0100
+++ b/src/init/hginit Mon Nov 09 16:23:04 2009 +0000
@@ -2,8 +2,16 @@
set -e
-cd ~hg
+cd
+
+if [ -e .ssh/authorized_keys ] ; then
+ echo "This user already exists with authorized keys, aborting"
+ exit -1
+fi
+
+cp $1/init/dot-mercurial-server .mercurial-server
mkdir -p repos/hgadmin .ssh
cd repos/hgadmin
hg init .
cp $1/init/hgadmin-hgrc .hg/hgrc
+
--- a/src/mercurialserver/paths.py Tue Oct 13 18:32:26 2009 +0100
+++ b/src/mercurialserver/paths.py Mon Nov 09 16:23:04 2009 +0000
@@ -1,10 +1,43 @@
# Copyright 2008-2009 LShift Ltd
-# Crude but it will do
-
import sys
import os.path
+import mercurial.config
+globalconfig = None
+
+def _getConf():
+ global globalconfig
+ if globalconfig is None:
+ globalconfig = mercurial.config.config()
+ globalconfig.read(os.path.expanduser("~/.mercurial-server"))
+ return globalconfig
+
+def configExists():
+ try:
+ _getConf()
+ return True
+ except:
+ return False
+
+def _getPath(name):
+ return os.path.expanduser(_getConf()["paths"][name])
+
+def _getPaths(name):
+ return [os.path.expanduser(p)
+ for p in _getConf()["paths"][name].split(":")]
+
+
+def getExePath(): return _getPath("exe")
+def getReposPath(): return _getPath("repos")
+
+def getKeysPaths(): return _getPaths("keys")
+def getAccessPaths(): return _getPaths("access")
+
+# This goes into an env var, so pass it on verbatim.
+def getHgrcPaths(): return _getConf()["paths"]["hgrc"]
+
+# Work out where we are, don't use config.
def setExePath():
global _exePath
_exePath = os.path.dirname(os.path.abspath(sys.argv[0]))
@@ -12,5 +45,3 @@
def getExePath():
return _exePath
-def getEtcPath():
- return "/etc/mercurial-server"
--- a/src/mercurialserver/refreshauth.py Tue Oct 13 18:32:26 2009 +0100
+++ b/src/mercurialserver/refreshauth.py Mon Nov 09 16:23:04 2009 +0000
@@ -9,16 +9,14 @@
import base64
import os
import os.path
-import pwd
import subprocess
from mercurialserver import paths
goodkey = re.compile("[/A-Za-z0-9._-]+$")
-def refreshAuth(pw_dir):
- akeyfile = pw_dir + "/.ssh/authorized_keys"
+def refreshAuth():
+ akeyfile = os.path.expanduser("~/.ssh/authorized_keys")
wrappercommand = paths.getExePath() + "/hg-ssh"
- keydirs = [paths.getEtcPath() + "/keys", pw_dir + "/repos/hgadmin/keys"]
prefix='no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,command='
if os.path.exists(akeyfile):
@@ -31,7 +29,7 @@
f.close()
akeys = open(akeyfile + "_new", "w")
- for keyroot in keydirs:
+ for keyroot in paths.getKeysPaths():
kr = keyroot + "/"
#print "Processing keyroot", keyroot
for root, dirs, files in os.walk(keyroot):
@@ -63,6 +61,5 @@
os.rename(akeyfile + "_new", akeyfile)
def hook(ui, repo, hooktype, node=None, source=None, **kwargs):
- pentry = pwd.getpwuid(os.geteuid())
- refreshAuth(pentry.pw_dir)
+ refreshAuth()
--- a/src/refresh-auth Tue Oct 13 18:32:26 2009 +0100
+++ b/src/refresh-auth Mon Nov 09 16:23:04 2009 +0000
@@ -8,19 +8,17 @@
import sys
import os
-import pwd
from mercurialserver import refreshauth, paths
if len(sys.argv) != 1:
sys.stderr.write("refresh-auth: must be called with no arguments (%s)\n" % sys.argv)
sys.exit(-1)
-pentry = pwd.getpwuid(os.geteuid())
-if pentry.pw_name != "hg":
- # FIXME: re-execute
+# To protect the authorized_keys file for innocent users, you have to have
+# a ~/.mercurial-server file to run this.
+if not paths.configExists():
print >>sys.stderr, "Must be run as the 'hg' user"
sys.exit(-1)
paths.setExePath()
-
-refreshauth.refreshAuth(pentry.pw_dir)
+refreshauth.refreshAuth()