diff contrib/hg-ssh @ 16836:1ba3e17186c8

hg-ssh: read-only flag Allows you to restrict a ssh key to have read-only access to a set of repos by passing the --read-only flag to hg-ssh. This is useful in an environment where the number of unix users you can or are willing to create is limited. In such an environment, multiple users or applications will share a single unix account. Some of those applications will likely need read-only access to the repository. This change makes it possible to grant them such access without requiring that they use a separate unix account.
author David Schleimer <dschleimer@fb.com>
date Tue, 22 May 2012 15:17:37 -0700
parents 67bfe7f64e57
children 2b9cda9040f7
line wrap: on
line diff
--- a/contrib/hg-ssh	Sun May 13 10:21:27 2012 +0200
+++ b/contrib/hg-ssh	Tue May 22 15:17:37 2012 -0700
@@ -24,6 +24,9 @@
 
 You can use pattern matching of your normal shell, e.g.:
 command="cd repos && hg-ssh user/thomas/* projects/{mercurial,foo}"
+
+You can also add a --read-only flag to allow read-only access to a key, e.g.:
+command="hg-ssh --read-only repos/*"
 """
 
 # enable importing on demand to reduce startup time
@@ -35,9 +38,17 @@
 
 def main():
     cwd = os.getcwd()
+    readonly = False
+    args = sys.argv[1:]
+    while len(args):
+        if args[0] == '--read-only':
+            readonly = True
+            args.pop(0)
+        else:
+            break
     allowed_paths = [os.path.normpath(os.path.join(cwd,
                                                    os.path.expanduser(path)))
-                     for path in sys.argv[1:]]
+                     for path in args]
     orig_cmd = os.getenv('SSH_ORIGINAL_COMMAND', '?')
     try:
         cmdargv = shlex.split(orig_cmd)
@@ -49,9 +60,15 @@
         path = cmdargv[2]
         repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
         if repo in allowed_paths:
-            dispatch.dispatch(dispatch.request(['-R', repo,
-                                                'serve',
-                                                '--stdio']))
+            cmd = ['-R', repo, 'serve', '--stdio']
+            if readonly:
+                cmd += [
+                    '--config',
+                    'hooks.prechangegroup.hg-ssh=python:__main__.rejectpush',
+                    '--config',
+                    'hooks.prepushkey.hg-ssh=python:__main__.rejectpush'
+                    ]
+            dispatch.dispatch(dispatch.request(cmd))
         else:
             sys.stderr.write('Illegal repository "%s"\n' % repo)
             sys.exit(255)
@@ -59,5 +76,11 @@
         sys.stderr.write('Illegal command "%s"\n' % orig_cmd)
         sys.exit(255)
 
+def rejectpush(ui, **kwargs):
+    ui.warn("Permission denied\n")
+    # mercurial hooks use unix process conventions for hook return values
+    # so a truthy return means failure
+    return True
+
 if __name__ == '__main__':
     main()