]> scripts.mit.edu Git - wizard.git/blobdiff - wizard/user.py
Use CLI installer for MediaWiki 1.17.0 and later.
[wizard.git] / wizard / user.py
index 81015eb04318196284667eb4ee151d7bf7b7c9e7..2d364a40b89dbbdbfc688ea4621db111d44064e6 100644 (file)
 """
-Module for querying information about users.  This mostly asks plugins for
-the extra information, and falls back to using a default that should work
-on most systems (but by no means all systems.)
+Module for querying information about users.  This mostly asks plugins
+for the extra information, and falls back to using a default that should
+work on most systems (but by no means all systems.)
 """
 
 import pkg_resources
 import os
+import socket
+import logging
+import pwd
+
+from wizard import plugin
 
 def quota(dir=None):
     """
-    Returns a tuple (quota usage, quota limit).  Returns ``(0, None)`` if
+    Returns a tuple (quota usage, quota limit).  Returns ``None`` if
     the quota usage is unknown.  If ``dir`` is omitted, the current
     working directory is assumed.  Value returned is in bytes.
+
+    This function implements a plugin interface named
+    :ref:`wizard.user.quota`.
     """
     if dir is None:
         dir = os.getcwd()
-    unknown = (0, None)
-    for entry in pkg_resources.iter_entry_points("wizard.user.quota"):
-        func = entry.load()
-        r = func(dir)
-        if r != unknown:
-            return r
-    return unknown
+    return plugin.hook("wizard.user.quota", [dir])
+
+def email(name=None):
+    """
+    Converts a username into an email address to that user.  If you have
+    a UID, you will have to convert it into a username first.  If no
+    canonical source of information is found, an heuristic approach
+    will be used.  If ``name`` is ``None``, the current user will be
+    used unless it is root, in which case :func:`operator` is tried
+    first to determine the real current user.
+
+    This function implements a plugin interface named
+    :ref:`wizard.user.email`.
+    """
+    if name is None:
+        logging.info("wizard.user.email: Determining email for current user")
+        env_email = os.getenv("EMAIL")
+        if env_email is not None:
+            logging.info("wizard.user.email: Used environment email %s", env_email)
+            return env_email
+        name = operator()
+    # run plugins
+    r = plugin.hook("wizard.user.email", [name])
+    if r is not None:
+        return r
+    # guess an email
+    try:
+        mailname = open("/etc/mailname").read()
+    except:
+        mailname = socket.getfqdn()
+    return name + "@" + mailname
+
+def operator():
+    """
+    Determines the username of the true person who is running this
+    program.  If the process's real uid is nonzero, just do a passwd
+    lookup; otherwise attempt to figure out the user behind the root
+    prompt some other way.
+
+    This function implements a plugin interface named
+    :ref:`wizard.user.operator`.
+    """
+    uid = os.getuid()
+    if uid:
+        pwdentry = pwd.getpwuid(uid)
+        return pwdentry.pw_name
+    # run plugins
+    r = plugin.hook("wizard.user.operator", [])
+    if r is not None:
+        return r
+    # use SUDO_USER
+    sudo_user = os.getenv("SUDO_USER")
+    if not sudo_user:
+        return None
+    pwdentry = pwd.getpwnam(sudo_user)
+    return pwdentry.pw_name
+
+def passwd(path=None, uid=None):
+    """
+    Returns a passwd-like entry (a :class:`Info` object) corresponding
+    to the owner of ``path``.  If ``uid`` is specified, ``path`` is used
+    solely to determine the filesystem ``uid`` was determined from.  It
+    will fall back to the local passwd database, and return ``None``
+    if no information is available.  If ``path`` is omitted, it will
+    fall back to the current working directory.
+
+    This function implements a plugin interface named
+    :ref:`wizard.user.passwd`.
+    """
+    if path is None:
+        path = os.getcwd()
+    path = os.path.realpath(path)
+    if not uid:
+        uid = os.stat(path).st_uid
+    r = plugin.hook("wizard.user.passwd", [path, uid])
+    if r is not None:
+        return r
+    try:
+        return Info.pwentry(pwd.getpwuid(uid))
+    except KeyError:
+        return None
+
+def pwnam(name):
+    """
+    This user converts a username into a :class:`Info` object using
+    *only* the local password database.
+    """
+    return Info.pwentry(pwd.getpwnam(name))
+
+class Info(object):
+    """
+    Object containing information describing a user.  It is analogous to
+    passwd, but has dropped the password field and dedicated the
+    ``gecos`` field for real name information.
+
+    .. note::
+
+        If a platform does not support retrieving information about a
+        field, it may have the value ``None``.
+    """
+    #: Login name
+    name = None
+    #: User ID
+    uid = None
+    #: Group ID
+    gid = None
+    #: Real name
+    realname = None
+    #: Home directory
+    homedir = None
+    #: Default command interpreter
+    shell = None
+    @staticmethod
+    def pwentry(pwentry):
+        return Info(pwentry.pw_name, pwentry.pw_uid, pwentry.pw_gid,
+                pwentry.pw_gecos.split(",")[0], pwentry.pw_dir, pwentry.pw_shell)
+    def __init__(self, name, uid, gid, realname, homedir, shell):
+        self.name = name
+        self.uid = uid
+        self.gid = gid
+        self.realname = realname
+        self.homedir = homedir
+        self.shell = shell
+        self._email = None
+    @property
+    def email(self):
+        """The email of this user, calculated on the fly."""
+        if self._email is None:
+            self._email = email(self.name)
+        return self._email