From 73eac46994f1601f4f485d573331676e75e28035 Mon Sep 17 00:00:00 2001 From: Jun Wu Date: Tue, 20 Sep 2016 23:15:09 +0100 Subject: [PATCH] grpcheck: new extension to check if the user is in given groups Summary: We have seen issues that users have outdated groups when running hg commands, which will probably always cause issues: - Authentication issue, unable to ssh - Filesystem permission issue, unable to write hgcache - Even worse with chg server since the long-running server process will keep the wrong groups information This extension is to address the above issues. It allows us to print a message to let the user know they have group issues. Besides, it allows us to override configs like `chgserver.idletimeout` so chg servers with wrong groups can have a much smaller TTL and won't be long-running and causing issues. Test Plan: Run the newly added test Reviewers: #sourcecontrol, simonfar Reviewed By: simonfar Subscribers: simonfar, mjpieters Differential Revision: https://phabricator.intern.facebook.com/D3896628 Signature: t1:3896628:1474454162:22785ff23e3ada75013ce5f1eead3407068ba172 --- hgext3rd/grpcheck.py | 72 ++++++++++++++++++++++++++++++++++++++++++ tests/test-grpcheck.t | 73 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 hgext3rd/grpcheck.py create mode 100644 tests/test-grpcheck.t diff --git a/hgext3rd/grpcheck.py b/hgext3rd/grpcheck.py new file mode 100644 index 0000000000..bcc6c94dd1 --- /dev/null +++ b/hgext3rd/grpcheck.py @@ -0,0 +1,72 @@ +# grpcheck.py - check if the user is in specified groups +# +# Copyright 2015 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. + +"""check if the user is in specified groups + +If the user is not a member of specified groups, optionally show a warning +message and override some other config items. + +:: + [grpcheck] + # the user is expected to be a member of devs and users group + groups = devs, users + # warning to show if the user is not a member of any of those groups + warning = You are not a member of %s group. Consult IT department for help. + # if the user is not a member of any of those groups, override chgserver + # config to make chgserver exit earlier + overrides.chgserver.idletimeout = 2 +""" + +import os +import grp + +testedwith = '3.9' + +_missinggroup = None + +def _grpname2gid(name): + try: + return grp.getgrnam(name).gr_gid + except KeyError: + return None + +def _firstmissinggroup(groupnames): + usergids = set(os.getgroups()) + for name in groupnames: + expectedgid = _grpname2gid(name) + # ignore unknown groups + if expectedgid is not None and expectedgid not in usergids: + return name + +def _overrideconfigs(ui): + for k, v in ui.configitems('grpcheck'): + if not k.startswith('overrides.'): + continue + section, name = k[len('overrides.'):].split('.', 1) + ui.setconfig(section, name, v) + +def extsetup(ui): + groupnames = ui.configlist('grpcheck', 'groups') + if not groupnames: + return + missing = _firstmissinggroup(groupnames) + if not missing: + return + message = ui.config('grpcheck', 'warning') + if message and not ui.plain(): + if '%s' in message: + message = message % missing + ui.warn(message + '\n') + _overrideconfigs(ui) + # re-used by reposetup. groups information is immutable for a process, + # so we can re-use the "missing" calculation result safely. + global _missinggroup + _missinggroup = missing + +def reposetup(ui, repo): + if _missinggroup: + _overrideconfigs(ui) diff --git a/tests/test-grpcheck.t b/tests/test-grpcheck.t new file mode 100644 index 0000000000..d01d20827e --- /dev/null +++ b/tests/test-grpcheck.t @@ -0,0 +1,73 @@ + $ cat >> $HGRCPATH << EOF + > [extensions] + > grpcheck = $TESTDIR/../hgext3rd/grpcheck.py + > EOF + + $ hg init repo + $ cd repo + +mock os.getgroups and grp.getgrnam + + $ cat >> $TESTTMP/mockgrp.py << EOF + > import os, grp + > def _getgroups(): + > return map(int, os.environ.get('HGMOCKGRPS', '1000').split()) + > class _grp(object): + > def __init__(self, gid): + > self.gr_gid = gid + > def _getgrnam(name): + > if name == 'users': + > gid = 1000 + > elif name == 'devs': + > gid = 2000 + > else: + > raise KeyError() + > return _grp(gid) + > os.getgroups = _getgroups + > grp.getgrnam = _getgrnam + > EOF + + $ cat >> $HGRCPATH << EOF + > mockgrp = $TESTTMP/mockgrp.py + > [grpcheck] + > groups = users, devs + > warning = You should be in %s group. + > overrides.chgserver.idletimeout = 3 + > overrides.ui.foo = bar + > EOF + +when the user is not in those groups, warnings are printed + + $ hg log + You should be in devs group. + $ HGMOCKGRPS=100 hg log + You should be in users group. + $ HGMOCKGRPS='100 1000' hg log + You should be in devs group. + +customized warning message + + $ hg --config grpcheck.warning=foo log + foo + +no warning if HGPLAIN or grpcheck.warning is empty + + $ hg --config grpcheck.warning= log + $ HGPLAIN=1 hg log + +warning does not affect write action (commit) + + $ touch a + $ hg commit -A a -m a + You should be in devs group. + $ hg log -T '{rev} {node}\n' + You should be in devs group. + 0 3903775176ed42b1458a6281db4a0ccf4d9f287a + +config overrides + + $ HGPLAIN=1 hg config --untrusted chgserver.idletimeout + 3 + $ cd .. # use repo ui + $ HGPLAIN=1 hg config --untrusted ui.foo + bar