sapling/hgext3rd/ownercheck.py
Jun Wu bdb47a171c ownercheck: new extension to prevent operations on repos not owned
Summary:
This is mainly to address the same issue as D3609747: prevent issues caused
by running hg as root. The difference is this extension reads owner and uid
dynamically without config when initializing of a localrepo object.

As a side effect it covers more situations like running hg in others' repos, while
being less restrictive for some other commands like `sudo hg version`.

Test Plan: Added a new test

Reviewers: #mercurial, ttung, rmcelroy

Reviewed By: rmcelroy

Subscribers: ikostia, rmcelroy, mjpieters

Differential Revision: https://phabricator.intern.facebook.com/D3619997

Tasks: 11723374

Signature: t1:3619997:1470176448:beaf53c09fca498206767641ffa4315a744ee07e
2016-07-26 13:10:15 +01:00

71 lines
2.0 KiB
Python

# ownercheck.py - prevent operations on repos not owned
#
# Copyright 2016 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
"""prevent operations on repos not owned by the current user
This extension checks the ownership of the local repo path (or its parent if
the path does not exist) and aborts if it does not match the current user.
This prevents some common mistakes like using sudo to clone a repo.
"""
import os
from mercurial import (
error,
extensions,
localrepo,
)
from mercurial.i18n import _
try:
import pwd
except ImportError:
pwd = None
def _getowner(path):
"""find uid of a path or its parents. return (uid, path)"""
path = os.path.abspath(path or '')
while True:
try:
stat = os.stat(path)
return stat.st_uid, path
except Exception:
parent = os.path.dirname(path)
if parent == path:
break
path = parent
return None, None
def _describeuser(uid):
"""convert uid to username if possible"""
if pwd:
try:
return pwd.getpwuid(uid).pw_name
except Exception:
pass
return 'user %d' % uid
def _checkownedpath(path):
ownerid, path = _getowner(path)
uid = os.getuid()
# allow access to public places owned by root (ex. /tmp)
if ownerid in [None, 0, uid]:
return
raise error.Abort(_('%s is owned by %s, not you (%s).\n'
'you are likely doing something wrong.')
% (path, _describeuser(ownerid), _describeuser(uid)),
hint=_('you can skip the check using '
'--config extensions.ownercheck=!'))
def _localrepoinit(orig, self, baseui, path=None, create=False):
_checkownedpath(path)
return orig(self, baseui, path, create)
def uisetup(ui):
extensions.wrapfunction(localrepo.localrepository,
'__init__', _localrepoinit)