[sabayon: 4/19] Refactored userdb into systemdb



commit 87ee462dc28201af9f43c0b163e790e5471826de
Author: Scott Balneaves <sbalneav ltsp org>
Date:   Wed Sep 2 23:18:34 2009 -0500

    Refactored userdb into systemdb

 admin-tool/groupsdialog.py     |    4 +-
 admin-tool/profilesdialog.py   |    9 +-
 admin-tool/sabayon-apply       |    6 +-
 admin-tool/usersdialog.py      |    4 +-
 lib/Makefile.am                |    3 +-
 lib/groupdb.py                 |  486 ----------------------------------------
 lib/{userdb.py => systemdb.py} |  202 +++++++++++++----
 lib/unittests.py               |    2 +-
 8 files changed, 165 insertions(+), 551 deletions(-)
---
diff --git a/admin-tool/groupsdialog.py b/admin-tool/groupsdialog.py
index ab3f524..a7676b6 100644
--- a/admin-tool/groupsdialog.py
+++ b/admin-tool/groupsdialog.py
@@ -20,7 +20,7 @@ import os.path
 import pwd
 import gtk
 import gtk.glade
-import groupdb
+import systemdb
 import errors
 import debuglog
 
@@ -44,7 +44,7 @@ class GroupsModel (gtk.ListStore):
 class GroupsDialog:
     def __init__ (self, profile, parent):
         self.profile = profile
-        self.groupdb = groupdb.get_database ()
+        self.groupdb = systemdb.get_group_database ()
 
         apply_to_all = self.groupdb.get_default_profile (False) == profile
         
diff --git a/admin-tool/profilesdialog.py b/admin-tool/profilesdialog.py
index 3f16916..70f3765 100755
--- a/admin-tool/profilesdialog.py
+++ b/admin-tool/profilesdialog.py
@@ -30,8 +30,7 @@ import editorwindow
 import usersdialog
 import groupsdialog
 import util
-import userdb
-import groupdb
+import systemdb
 import protosession
 import debuglog
 import errors
@@ -261,7 +260,7 @@ class ProfilesModel (gtk.ListStore):
 
     def reload (self):
         self.clear ()
-        profiles = userdb.get_database ().get_profiles ()
+        profiles = systemdb.get_user_database ().get_profiles ()
         profiles.sort ()
         for profile in profiles:
             self.set (self.append (),
@@ -464,7 +463,7 @@ class ProfilesDialog:
             dprint ("Deleting '%s'", profile_name)
             os.remove (_get_profile_path_for_name (profile_name))
 
-            db = userdb.get_database ()
+            db = systemdb.get_user_database ()
             if db.get_default_profile (False) == profile_name:
                 db.set_default_profile (None)
             for user in db.get_users ():
@@ -495,7 +494,7 @@ class ProfilesDialog:
             self.__delete_currently_selected ()
 
     def __make_unique_profile_name (self, profile_name):
-        profiles = userdb.get_database ().get_profiles ()
+        profiles = systemdb.get_user_database ().get_profiles ()
 
         name = profile_name
         idx = 1
diff --git a/admin-tool/sabayon-apply b/admin-tool/sabayon-apply
index ecab0ce..62e86d0 100755
--- a/admin-tool/sabayon-apply
+++ b/admin-tool/sabayon-apply
@@ -42,7 +42,7 @@ if __name__ == '__main__':
     from sabayon import debuglog
     from sabayon import errors
     from sabayon import util
-    from sabayon import userdb
+    from sabayon import systemdb
 
     def dprint (fmt, *args):
         debuglog.debug_log (False, debuglog.DEBUG_LOG_DOMAIN_SABAYON_APPLY, fmt % args)
@@ -82,7 +82,7 @@ if __name__ == '__main__':
 
         user_name = util.get_user_name ()
 
-        if userdb.get_database().is_sabayon_controlled (user_name):
+        if systemdb.get_user_database().is_sabayon_controlled (user_name):
             try:
                 shutil.rmtree (os.path.join (util.get_home_dir (), ".gconf.xml.defaults"), True)
                 shutil.rmtree (os.path.join (util.get_home_dir (), ".gconf.xml.mandatory"), True)
@@ -93,7 +93,7 @@ if __name__ == '__main__':
 
         num_args = len (args)
         if num_args == 0:
-            profile_name = userdb.get_database().get_profile (user_name)
+            profile_name = systemdb.get_user_database().get_profile (user_name)
             if not profile_name:
                 mprint ("No profile for user '%s' found", user_name)
                 sys.stderr.write (_("No profile for user '%s' found\n") % user_name)
diff --git a/admin-tool/usersdialog.py b/admin-tool/usersdialog.py
index 564619e..a22d4de 100644
--- a/admin-tool/usersdialog.py
+++ b/admin-tool/usersdialog.py
@@ -20,7 +20,7 @@ import os.path
 import pwd
 import gtk
 import gtk.glade
-import userdb
+import systemdb
 import errors
 import debuglog
 
@@ -53,7 +53,7 @@ class UsersModel (gtk.ListStore):
 class UsersDialog:
     def __init__ (self, profile, parent):
         self.profile = profile
-        self.userdb = userdb.get_database ()
+        self.userdb = systemdb.get_user_database ()
 
         apply_to_all = self.userdb.get_default_profile (False) == profile
         
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 931c731..08cdf1a 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -23,11 +23,10 @@ PYTHON_FILES =				\
 	errors.py			\
 	debuglog.py			\
 	dirmonitor.py			\
-	groupdb.py			\
 	mozilla_bookmarks.py		\
 	protosession.py			\
 	storage.py			\
-	userdb.py			\
+	systemdb.py			\
 	usermod.py			\
 	userprofile.py			\
 	util.py
diff --git a/lib/userdb.py b/lib/systemdb.py
similarity index 76%
rename from lib/userdb.py
rename to lib/systemdb.py
index 88c8806..9127008 100644
--- a/lib/userdb.py
+++ b/lib/systemdb.py
@@ -19,6 +19,7 @@
 import sys
 import string
 import pwd
+import grp
 import os
 import libxml2
 import config
@@ -29,6 +30,9 @@ import ldap
 import socket
 import debuglog
 
+#
+# Default empty config.
+#
 defaultConf="""<profiles>
   <default profile=""/>
 </profiles>"""
@@ -72,10 +76,10 @@ def expand_string (string, attrs):
     return res
 
 
-class UserDatabaseException (Exception):
+class SystemDatabaseException (Exception):
     pass
 
-class UserDatabase:
+class SystemDatabase(object):
     """An encapsulation of the database which maintains an
     association between users and profiles.
 
@@ -89,19 +93,20 @@ class UserDatabase:
     an absolute path or a http/file URL.
     """
     def __init__ (self, db_file = None):
-        """Create a UserDatabase object.
+        """Create a SystemDatabase object.
 
-        @db_file: an (optional) path which specifes the location
-        of the database file. If not specified, the default
-        location of /etc/desktop-profiles/users.xml is used.
+        @db_file: a mandatory path which specifes the location
+        of the database file. If only a file is specified, the
+        directory /etc/desktop-profiles is used.
         """
         if db_file is None:
-            file = os.path.join (config.PROFILESDIR, "users.xml")
+            raise SystemDatabaseException(_("No database file provided"))
         elif db_file[0] != '/':
             file = os.path.join (config.PROFILESDIR, db_file)
         else:
             file = db_file
-        self.file = file;
+        self.file = file
+        self.xmlquery = None
         self.modified = 0
         dprint("New UserDatabase(%s) object\n" % self.file)
 
@@ -117,7 +122,7 @@ class UserDatabase:
         if self.doc == None:
             self.doc = libxml2.readMemory(defaultConf, len(defaultConf),
                                           None, None,
-                                          libxml2.XML_PARSE_NOBLANKS);
+                                          libxml2.XML_PARSE_NOBLANKS)
 
     def __del__ (self):
         if self.doc != None:
@@ -143,7 +148,7 @@ class UserDatabase:
             try:
                 base = node.getBase(None)
                 if base != None and base != "" and \
-                   base != os.path.join (config.PROFILESDIR, "users.xml"):
+                   base != self.file:
                     # URI composition from the base
                     ret = libxml2.buildURI(profile, base)
                     if ret[0] == '/':
@@ -277,8 +282,8 @@ class UserDatabase:
         
         return self.__profile_name_to_location (profile, default)
 
-    def get_profile (self, username, profile_location = True, ignore_default = False):
-        """Look up the profile for a given username.
+    def gen_get_profile (self, searchterm, replace, profile_location = True, ignore_default = False):
+        """Look up the profile for a given searchterm.
 
         @username: the user whose profile location should be
         returned.
@@ -293,10 +298,10 @@ class UserDatabase:
         @profile_location is False.
         """
         user = None
-        profile = self.__ldap_query ("profilemap", {"u":username, "h":socket.getfqdn()})
+        profile = self.__ldap_query ("profilemap", replace)
         if not profile:
             try:
-                query = "/profiles/user[ name='%s']" % username
+                query = self.xmlquery % searchterm
                 user = self.doc.xpathEval(query)[0]
                 profile = user.prop("profile")
             except:
@@ -311,7 +316,7 @@ class UserDatabase:
         
         if not profile_location:
             return profile
-        
+
         # TODO Check the resulting file path exists
         return self.__profile_name_to_location (profile, user)
 
@@ -357,7 +362,7 @@ class UserDatabase:
         self.modified = 0
 
     def set_default_profile (self, profile):
-        """Set the default profile to be used for all users.
+        """Set the default profile to be used in this database.
 
         @profile: the location of the profile.
         """
@@ -374,32 +379,31 @@ class UserDatabase:
             try:
                 profiles = self.doc.xpathEval("/profiles")[0]
             except:
-                raise UserDatabaseException(
+                raise SystemDatabaseException(
                     _("File %s is not a profile configuration") %
                                            (self.file))
             try:
                 default = profiles.newChild(None, "default", None)
                 default.setProp("profile", profile)
             except:
-                raise UserDatabaseException(
+                raise SystemDatabaseException(
                     _("Failed to add default profile %s to configuration") %
                                            (profile))
             self.modified = 1
         if self.modified == 1:
             self.__save_as()
 
-    def set_profile (self, username, profile):
-        """Set the profile for a given username.
+    def gen_set_profile (self, searchterm, child, profile):
+        """Set the profile for a given searchterm.
 
-        @username: the user whose profile location should be
-        set.
+        @searchterm: the term whose profile location should be set.
         @profile: the location of the profile.
         """
         if profile is None:
             profile = ""
         self.modified = 0
         try:
-            query = "/profiles/user[ name='%s']" % username
+            query = self.xmlquery % searchterm
             user = self.doc.xpathEval(query)[0]
             oldprofile = user.prop("profile")
             if oldprofile != profile:
@@ -409,21 +413,40 @@ class UserDatabase:
             try:
                 profiles = self.doc.xpathEval("/profiles")[0]
             except:
-                raise UserDatabaseException(
-                    _("File %s is not a profile configuration") %
-                                           (self.file))
+                raise SystemDatabaseException(
+                    _("File %s is not a profile configuration") % (self.file))
             try:
-                user = profiles.newChild(None, "user", None)
-                user.setProp("name", username)
+                user = profiles.newChild(None, child, None)
+                user.setProp("name", searchterm)
                 user.setProp("profile", profile)
             except:
-                raise UserDatabaseException(
+                raise SystemDatabaseException(
                     _("Failed to add user %s to profile configuration") %
                                            (username))
             self.modified = 1
         if self.modified == 1:
             self.__save_as()
 
+    def gen_is_sabayon_controlled (self, searchterm, replace):
+        """Return True if user's configuration was ever under Sabayon's
+        control.
+        """
+        profile = self.__ldap_query ("profilemap", replace)
+
+        if profile:
+            return True
+        
+        try:
+            query = self.xmlquery % searchterm
+            user = self.doc.xpathEval(query)[0]
+        except:
+            return False
+
+        if user:
+            return True
+
+        return False
+
     def get_profiles (self):
         """Return the list of currently available profiles.
         This is basically just list of zip files in
@@ -440,25 +463,26 @@ class UserDatabase:
         # TODO: also list remote profiles as found in self.doc
         return list
 
-    def is_sabayon_controlled (self, username):
-        """Return True if user's configuration was ever under Sabayon's
-        control.
-        """
-        profile = self.__ldap_query ("profilemap", {"u":username, "h":socket.getfqdn()})
+class UserDatabase(SystemDatabase):
+    """Encapsulate a user mapping
+    """
+    def __init__ (self, db_file = None):
+        if db_file is None:
+            SystemDatabase.__init__(self, "users.xml")
+        else:
+            SystemDatabase.__init__(self, db_file)
 
-        if profile:
-            return True
-        
-        try:
-            query = "/profiles/user[ name='%s']" % username
-            user = self.doc.xpathEval(query)[0]
-        except:
-            return False
+        self.xmlquery = "/profiles/user[ name='%s']"
 
-        if user:
-            return True
+    def get_profile (self, username, profile_location = True, ignore_default = False):
+        return self.gen_get_profile(username, {"u":username,
+            "h":socket.getfqdn()}, profile_location, ignore_default)
 
-        return False
+    def is_sabayon_controlled (self, username):
+        return self.gen_is_sabayon_controlled(username, {"u":username, "h":socket.getfqdn()})
+
+    def set_profile (self, username, profile):
+        return self.gen_set_profile (username, "user", profile)
 
     def get_users (self):
         """Return the list of users on the system. These should
@@ -469,13 +493,15 @@ class UserDatabase:
         try:
             users = pwd.getpwall()
         except:
-            raise UserDatabaseException(_("Failed to get the user list"))
+            raise SystemDatabaseException(_("Failed to get the user list"))
 
         for user in pwd.getpwall():
             try:
                 # remove non-users
                 if user[2] < 500:
                     continue
+                if user[0] == "nobody":
+                    continue
                 if user[0] in list:
                     continue
                 if user[6] == "" or string.find(user[6], "nologin") != -1:
@@ -487,26 +513,85 @@ class UserDatabase:
                 pass
         return list
 
+class GroupDatabase(SystemDatabase):
+    """Encapsulate a user mapping
+    """
+    def __init__ (self, db_file = None):
+        if db_file is None:
+            SystemDatabase.__init__(self, "groups.xml")
+        else:
+            SystemDatabase.__init__(self, db_file)
+
+        self.xmlquery = "/profiles/group[ name='%s']"
+
+    def get_profile (self, groupname, profile_location = True, ignore_default = False):
+        return self.gen_get_profile(groupname, {"g":groupname,
+            "h":socket.getfqdn()}, profile_location, ignore_default)
+
+    def is_sabayon_controlled (self, groupname):
+        return self.gen_is_sabayon_controlled(groupname, {"g":groupname, "h":socket.getfqdn()})
 
+    def set_profile (self, groupname, profile):
+        return self.gen_set_profile (groupname, "group", profile)
+
+    def get_groups (self):
+        """Return the list of groups on the system. These should
+        be real groups - i.e. should not include system groups
+        like lp, udev, etc.
+        """
+        list = []
+        try:
+            groups = grp.getgrall()
+        except:
+            raise GroupDatabaseException(_("Failed to get the group list"))
+
+        for group in groups:
+            # remove non-groups
+            if group[2] < 500:
+                continue
+            if group[0] == "nogroup":
+                continue
+            if group[0] in list:
+                continue
+            # We don't want to include "user" primary groups
+            try:
+                user = pwd.getpwnam(group[0])
+            except:
+                user = None
+            if user is not None and user[2] == group[2]:
+                continue
+            list.append(group[0])
+        return list
 
 user_database = None
-def get_database ():
+group_database = None
+
+def get_user_database ():
     """Return a UserDatabase singleton"""
     global user_database
     if user_database is None:
         user_database = UserDatabase ()
     return user_database
 
+def get_group_database ():
+    """Return a UserDatabase singleton"""
+    global group_database
+    if group_database is None:
+        group_database = GroupDatabase ()
+    return group_database
 #
 # Unit tests
 #
+
 def run_unit_tests ():
-    testfile = "/tmp/test_users.xml"
+    testuserfile = "/tmp/test_users.xml"
+    testgroupfile = "/tmp/test_groups.xml"
     try:
-        os.unlink(testfile)
+        os.unlink(testuserfile)
+        os.unlink(testgroupfile)
     except:
         pass
-    db = UserDatabase(testfile)
+    db = UserDatabase(testuserfile)
     db.set_default_profile("default")
     res = db.get_profile("localuser", False)
     assert not res is None
@@ -519,6 +604,23 @@ def run_unit_tests ():
     res = db.get_profile("localuser")
     assert not res is None
     assert res[-28:] == "/desktop-profiles/groupB.zip"
+    res = db.get_users()
+    print res
+    db = GroupDatabase(testgroupfile)
+    db.set_default_profile("default")
+    res = db.get_profile("localuser", False)
+    assert not res is None
+    assert res == "default"
+    db.set_profile("localgroup", "groupA")
+    res = db.get_profile("localgroup")
+    assert not res is None
+    assert res[-28:] == "/desktop-profiles/groupA.zip"
+    db.set_profile("localgroup", "groupB")
+    res = db.get_profile("localgroup")
+    assert not res is None
+    assert res[-28:] == "/desktop-profiles/groupB.zip"
+    res = db.get_groups()
+    print res
 
 if __name__ == "__main__":
     util.init_gettext ()
diff --git a/lib/unittests.py b/lib/unittests.py
index 989ad68..2e491bf 100755
--- a/lib/unittests.py
+++ b/lib/unittests.py
@@ -39,7 +39,7 @@ if __name__ == "__main__":
         ( "paneldelegate", _("Ignore WARNINGs"), add_mod_dir, remove_mod_dir ),
         ( "mozillasource",   None,                 add_mod_dir, remove_mod_dir ),
         ( "userprofile",   None,                 None,        None ),
-        ( "userdb",        None,                 None,        None ),
+        ( "systemdb",      None,                 None,        None ),
         ( "cache",         None,                 None,        None )
           ]
     



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]