[SCM] Samba Shared Repository - branch master updated

Aurélien Aptel aaptel at samba.org
Mon Oct 8 19:26:02 UTC 2018


The branch, master has been updated
       via  a5a4f97 gpo: Test process_group_policy in gp_sec_ext
       via  f390dbf gpo: test the get_deleted_gpos_list() function
       via  cb3eb79 gpo: Test the new get_applied functions
       via  ba87e23 gpo PEP8: balance whitespace around equals
       via  6ac1445 gpupdate: test the new --force option
       via  4e98d18 gpupdate: Add the --force option
       via  471089e gpo: Remove unused apply_log_pop() and list() funcs
       via  7d07d8b gpo: add unapply to the gp_sec_ext
       via  cab1650 gpo: Calculate deleted gpos and unapply them
       via  30f6ac9 gpo: Use the new process_group_policy() for unapply
       via  9d7a0bb gpo: avoid quadratic behaviour in guid retrieval
       via  cc02de6 gpo: Create a function for returning applied settings
       via  8a21ae6 gpo: apply_map should not be required for gp_ext
       via  96ffc96 gpo: remove unreached non-DC branch in gp_sec_ext.apply_map()
       via  78601b3 gpo: Move policy application to the gp_ext
       via  7cef695 gpo: Remove unused gp_ext.list() function
       via  7bb326a gpo: Implement process_group_policy() gp_ext func
       via  fb22582 gpo: Remove unused methods from gp_sec_ext
       via  4354071 gpo: Initialize gp_ext variables in constructor
       via  f702ad9 gpupdate: Remove the unnecessary url parameter
       via  aa9b07b gpo: gp_sec_ext should check whether to apply
       via  5dddb78 gpo: Initialize SamDB in the gp_sec_ext
       via  f5c6bd5 gpo: abstract methods are defined in the parent class
      from  80f3f7c ctdb-tests: Improve counting of database records

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit a5a4f979351a99de92b277a60e7680d64e5a1bc6
Author: David Mulder <dmulder at suse.com>
Date:   Thu Aug 30 15:22:08 2018 -0600

    gpo: Test process_group_policy in gp_sec_ext
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>
    
    Autobuild-User(master): Aurélien Aptel <aaptel at samba.org>
    Autobuild-Date(master): Mon Oct  8 21:25:59 CEST 2018 on sn-devel-144

commit f390dbfa2c0bc9514eba9d3449e0d106bde90d8c
Author: David Mulder <dmulder at suse.com>
Date:   Thu Aug 30 10:25:45 2018 -0600

    gpo: test the get_deleted_gpos_list() function
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit cb3eb79eff4304e9f112354db5ffa4d526ee9f29
Author: David Mulder <dmulder at suse.com>
Date:   Wed Aug 29 17:28:58 2018 -0600

    gpo: Test the new get_applied functions
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit ba87e23b3842d03dbc1e1cb16f293a3f3eb2bee4
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Wed Aug 29 13:30:59 2018 +1200

    gpo PEP8: balance whitespace around equals
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 6ac1445a99f3a9d22aa98e46e5708bd32dc78d65
Author: David Mulder <dmulder at suse.com>
Date:   Mon Jul 23 13:27:31 2018 -0600

    gpupdate: test the new --force option
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 4e98d18bea527cc6314469d438fe9e44e2a47765
Author: David Mulder <dmulder at suse.com>
Date:   Wed May 16 09:54:38 2018 -0600

    gpupdate: Add the --force option
    
    This option forces the reapplication of policy,
    and works the same as MS 'gpupdate /force'
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 471089eed45cbda3163f570d3cea37474a27f0ef
Author: David Mulder <dmulder at suse.com>
Date:   Thu May 17 16:49:39 2018 -0600

    gpo: Remove unused apply_log_pop() and list() funcs
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 7d07d8bf33754748ccf1703bb4d8564ab062de6c
Author: David Mulder <dmulder at suse.com>
Date:   Thu Jul 19 14:10:33 2018 -0600

    gpo: add unapply to the gp_sec_ext
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit cab1650539a790ec3d3016d4281b0b4e0a7da15a
Author: David Mulder <dmulder at suse.com>
Date:   Tue May 15 14:00:07 2018 -0600

    gpo: Calculate deleted gpos and unapply them
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 30f6ac9c1632d4e7522576d64c6dff2f205dbf96
Author: David Mulder <dmulder at suse.com>
Date:   Thu May 17 16:48:47 2018 -0600

    gpo: Use the new process_group_policy() for unapply
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 9d7a0bb3de2478b7bb7cc1e2bf56e116c4f52588
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Wed Aug 29 16:39:51 2018 +1200

    gpo: avoid quadratic behaviour in guid retrieval
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit cc02de6bfa53af09140a59023f63c2d8bf221ff4
Author: David Mulder <dmulder at suse.com>
Date:   Thu May 17 15:56:15 2018 -0600

    gpo: Create a function for returning applied settings
    
    This returns a list of guids for gpos applied
    plus settings applied and their previous values.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 8a21ae62807c49b890fd677aba74cbed432c0590
Author: David Mulder <dmulder at suse.com>
Date:   Thu Jul 19 12:55:00 2018 -0600

    gpo: apply_map should not be required for gp_ext
    
    The apply_map function should not be a requirement
    to implement the gp_ext class, since only the
    gp_sec_ext uses it now.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 96ffc9690bdeb6b37885addeac9fd699857d350e
Author: David Mulder <dmulder at suse.com>
Date:   Wed Aug 29 15:44:35 2018 +1200

    gpo: remove unreached non-DC branch in gp_sec_ext.apply_map()
    
    We don't get this far if we are not a DC, and if somehow we do the
    errors will be no more informative due to this special case.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 78601b3516c495c87455732fec8c19c4d21e48f8
Author: David Mulder <dmulder at suse.com>
Date:   Thu May 17 16:23:51 2018 -0600

    gpo: Move policy application to the gp_ext
    
    Policy specific setting application should be
    handled by the group policy extension, not the
    read/parse handler.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 7cef695368f587573d22b4143fc176fac2ac55b6
Author: David Mulder <dmulder at suse.com>
Date:   Thu Jul 19 10:56:29 2018 -0600

    gpo: Remove unused gp_ext.list() function
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 7bb326a60d2bd2210e76bb20071df114f9c30462
Author: David Mulder <dmulder at suse.com>
Date:   Wed May 9 13:16:38 2018 -0600

    gpo: Implement process_group_policy() gp_ext func
    
    MS spec describes the policy callback as a
    function called ProcessGroupPolicy which accepts
    a pDeletedGPOList and a pChangedGPOList param.
    The Group Policy Client Side Extension then
    iterates over the deleted, then the changed gpo
    lists and applies/unapplies policy. We should do
    this also.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit fb22582aef50491e48ab3d8ed69f15b7500d10c2
Author: David Mulder <dmulder at suse.com>
Date:   Wed May 16 11:08:13 2018 -0600

    gpo: Remove unused methods from gp_sec_ext
    
    These functions were added by Luke, but have
    never actually done anything. If/when we
    read from these *.pol files, we won't need these
    separate functions to do it.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 4354071b7a862e5e459b1be8bec9ec185e6a784f
Author: David Mulder <dmulder at suse.com>
Date:   Wed May 16 10:58:29 2018 -0600

    gpo: Initialize gp_ext variables in constructor
    
    Initialize variables for the gp_ext in the
    constructor instead of passing them via the parse
    function.
    This is a dependency of the "gpo: Implement
    process_group_policy() gp_ext func" patch, since
    the parse() function is now called by the ext,
    instead of by gpupdate within apply_gp(). The
    parse() function should only take the path
    variable, to simplify writing Client Side
    Extensions.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit f702ad943eb94d9d7964e8dec91a4708a43c81a3
Author: David Mulder <dmulder at suse.com>
Date:   Wed May 16 08:04:20 2018 -0600

    gpupdate: Remove the unnecessary url parameter
    
    The samdb object isn't initialized here anymore,
    but in the gp_sec_ext, so this parameter to
    gpupdate does nothing.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit aa9b07ba0f8c906e99bef3ce584c7c16d3620f5f
Author: David Mulder <dmulder at suse.com>
Date:   Fri May 4 14:09:30 2018 -0600

    gpo: gp_sec_ext should check whether to apply
    
    Whether an extension should apply should be
    determined by the extension, not by the
    calling script.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit 5dddb784f8214e2b6753b7544cf4cb9eb3829a90
Author: David Mulder <dmulder at suse.com>
Date:   Fri Jul 13 14:45:06 2018 -0600

    gpo: Initialize SamDB in the gp_sec_ext
    
    The SamDB is only used by the gp_sec_ext, and
    isn't needed elsewhere, so initialize it where
    we need it and avoid passing it around
    everywhere.
    It makes the most sense to put this in the setter
    class that uses it, so pass our creds down so we
    have access to it, then initialize it there.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

commit f5c6bd5c843ce519170a16f885bfb0d742af6b97
Author: David Mulder <dmulder at suse.com>
Date:   Thu Jul 19 09:48:11 2018 -0600

    gpo: abstract methods are defined in the parent class
    
    These methods don't need redefined in the child
    class.
    
    Signed-off-by: David Mulder <dmulder at suse.com>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Aurelien Aptel <aaptel at suse.com>

-----------------------------------------------------------------------

Summary of changes:
 python/samba/gp_sec_ext.py           |  69 +++++++++--
 python/samba/gpclass.py              | 227 +++++++++++++++--------------------
 python/samba/tests/gpo.py            | 165 ++++++++++++++++++++++++-
 selftest/target/Samba4.pm            |   2 +-
 source4/scripting/bin/samba-gpupdate |  31 ++---
 source4/torture/gpo/apply.c          | 112 ++++++++++++++++-
 6 files changed, 435 insertions(+), 171 deletions(-)


Changeset truncated at 500 lines:

diff --git a/python/samba/gp_sec_ext.py b/python/samba/gp_sec_ext.py
index 772f678..fcde468 100644
--- a/python/samba/gp_sec_ext.py
+++ b/python/samba/gp_sec_ext.py
@@ -17,6 +17,12 @@
 
 import os.path
 from samba.gpclass import gp_ext_setter, gp_inf_ext
+from samba.auth import system_session
+try:
+    from ldb import LdbError
+    from samba.samdb import SamDB
+except ImportError:
+    pass
 
 
 class inf_to_kdc_tdb(gp_ext_setter):
@@ -55,6 +61,16 @@ class inf_to_ldb(gp_ext_setter):
     object to update the parameter to Samba4. Not registry oriented whatsoever.
     '''
 
+    def __init__(self, logger, gp_db, lp, creds, key, value):
+        super(inf_to_ldb, self).__init__(logger, gp_db, lp, creds, key, value)
+        try:
+            self.ldb = SamDB(self.lp.samdb_url(),
+                             session_info=system_session(),
+                             credentials=self.creds,
+                             lp=self.lp)
+        except (NameError, LdbError):
+            raise Exception('Failed to load SamDB for assigning Group Policy')
+
     def ch_minPwdAge(self, val):
         old_val = self.ldb.get_minPwdAge()
         self.logger.info('KDC Minimum Password age was changed from %s to %s'
@@ -119,16 +135,6 @@ class gp_sec_ext(gp_inf_ext):
     def __str__(self):
         return "Security GPO extension"
 
-    def list(self, rootpath):
-        return os.path.join(rootpath,
-                            "MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf")
-
-    def listmachpol(self, rootpath):
-        return os.path.join(rootpath, "Machine/Registry.pol")
-
-    def listuserpol(self, rootpath):
-        return os.path.join(rootpath, "User/Registry.pol")
-
     def apply_map(self):
         return {"System Access": {"MinimumPasswordAge": ("minPwdAge",
                                                          inf_to_ldb),
@@ -154,3 +160,46 @@ class gp_sec_ext(gp_inf_ext):
                                     }
                 }
 
+    def process_group_policy(self, deleted_gpo_list, changed_gpo_list):
+        if self.lp.get('server role') != 'active directory domain controller':
+            return
+        inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
+        apply_map = self.apply_map()
+        for gpo in deleted_gpo_list:
+            self.gp_db.set_guid(gpo[0])
+            for section in gpo[1].keys():
+                current_section = apply_map.get(section)
+                if not current_section:
+                    continue
+                for key, value in gpo[1][section].items():
+                    setter = None
+                    for _, tup in current_section.items():
+                        if tup[0] == key:
+                            setter = tup[1]
+                    if setter:
+                        value = value.encode('ascii', 'ignore') \
+                             if value else value
+                        setter(self.logger, self.gp_db, self.lp, self.creds,
+                               key, value).delete()
+                        self.gp_db.delete(section, key)
+                        self.gp_db.commit()
+
+        for gpo in changed_gpo_list:
+            if gpo.file_sys_path:
+                self.gp_db.set_guid(gpo.name)
+                path = os.path.join(gpo.file_sys_path, inf_file)
+                inf_conf = self.parse(path)
+                if not inf_conf:
+                    continue
+                for section in inf_conf.sections():
+                    current_section = apply_map.get(section)
+                    if not current_section:
+                        continue
+                    for key, value in inf_conf.items(section):
+                        if current_section.get(key):
+                            (att, setter) = current_section.get(key)
+                            value = value.encode('ascii', 'ignore')
+                            setter(self.logger, self.gp_db, self.lp,
+                                   self.creds, att, value).update_samba()
+                            self.gp_db.commit()
+
diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py
index 3cf1f10..b7c53eb 100644
--- a/python/samba/gpclass.py
+++ b/python/samba/gpclass.py
@@ -146,24 +146,6 @@ class gp_log:
                 item.attrib['count'] = '%d' % (len(apply_log) - 1)
                 item.attrib['value'] = guid
 
-    def apply_log_pop(self):
-        ''' Pop a GPO guid from the applylog
-        return              - last applied GPO guid
-
-        Removes the GPO guid last added to the list, which is the most recently
-        applied GPO.
-        '''
-        user_obj = self.gpdb.find('user[@name="%s"]' % self.user)
-        apply_log = user_obj.find('applylog')
-        if apply_log is not None:
-            ret = apply_log.find('guid[@count="%d"]' % (len(apply_log) - 1))
-            if ret is not None:
-                apply_log.remove(ret)
-                return ret.attrib['value']
-            if len(apply_log) == 0 and apply_log in user_obj:
-                user_obj.remove(apply_log)
-        return None
-
     def store(self, gp_ext_name, attribute, old_val):
         ''' Store an attribute in the gp_log
         param gp_ext_name   - Name of the extension applying policy
@@ -203,36 +185,43 @@ class gp_log:
                 return attr.text
         return None
 
-    def list(self, gp_extensions):
-        ''' Return a list of attributes, their previous values, and functions
-            to set them
-        param gp_extensions - list of extension objects, for retrieving attr to
-                              func mappings
-        return              - list of (attr, value, apply_func) tuples for
-                              unapplying policy
+    def get_applied_guids(self):
+        ''' Return a list of applied ext guids
+        return              - List of guids for gpos that have applied settings
+                              to the system.
         '''
+        guids = []
         user_obj = self.gpdb.find('user[@name="%s"]' % self.user)
-        guid_obj = user_obj.find('guid[@value="%s"]' % self.guid)
-        assert guid_obj is not None, "gpo guid was not set"
+        if user_obj is not None:
+            apply_log = user_obj.find('applylog')
+            if apply_log is not None:
+                guid_objs = apply_log.findall('guid[@count]')
+                guids_by_count = [(g.get('count'), g.get('value'))
+                                  for g in guid_objs]
+                guids_by_count.sort(reverse=True)
+                guids.extend(guid for count, guid in guids_by_count)
+        return guids
+
+    def get_applied_settings(self, guids):
+        ''' Return a list of applied ext guids
+        return              - List of tuples containing the guid of a gpo, then
+                              a dictionary of policies and their values prior
+                              policy application. These are sorted so that the
+                              most recently applied settings are removed first.
+        '''
         ret = []
-        data_maps = {}
-        for gp_ext in gp_extensions:
-            data_maps.update(gp_ext.apply_map())
-        exts = guid_obj.findall('gp_ext')
-        if exts is not None:
+        user_obj = self.gpdb.find('user[@name="%s"]' % self.user)
+        for guid in guids:
+            guid_settings = user_obj.find('guid[@value="%s"]' % guid)
+            exts = guid_settings.findall('gp_ext')
+            settings = {}
             for ext in exts:
+                attr_dict = {}
                 attrs = ext.findall('attribute')
                 for attr in attrs:
-                    func = None
-                    if attr.attrib['name'] in data_maps[ext.attrib['name']]:
-                        func = data_maps[ext.attrib['name']][attr.attrib['name']][-1]
-                    else:
-                        for dmap in data_maps[ext.attrib['name']].keys():
-                            if data_maps[ext.attrib['name']][dmap][0] == \
-                               attr.attrib['name']:
-                                func = data_maps[ext.attrib['name']][dmap][-1]
-                                break
-                    ret.append((attr.attrib['name'], attr.text, func))
+                    attr_dict[attr.attrib['name']] = attr.text
+                settings[ext.attrib['name']] = attr_dict
+            ret.append((guid, settings))
         return ret
 
     def delete(self, gp_ext_name, attribute):
@@ -262,7 +251,7 @@ class GPOStorage:
         if os.path.isfile(log_file):
             self.log = tdb.open(log_file)
         else:
-            self.log = tdb.Tdb(log_file, 0, tdb.DEFAULT, os.O_CREAT |os.O_RDWR)
+            self.log = tdb.Tdb(log_file, 0, tdb.DEFAULT, os.O_CREAT | os.O_RDWR)
 
     def start(self):
         self.log.transaction_start()
@@ -298,26 +287,21 @@ class GPOStorage:
 class gp_ext(object):
     __metaclass__ = ABCMeta
 
-    def __init__(self, logger):
+    def __init__(self, logger, lp, creds, store):
         self.logger = logger
+        self.lp = lp
+        self.creds = creds
+        self.gp_db = store.get_gplog(creds.get_username())
 
     @abstractmethod
-    def list(self, rootpath):
-        pass
-
-    @abstractmethod
-    def apply_map(self):
+    def process_group_policy(self, deleted_gpo_list, changed_gpo_list):
         pass
 
     @abstractmethod
     def read(self, policy):
         pass
 
-    def parse(self, afile, ldb, gp_db, lp):
-        self.ldb = ldb
-        self.gp_db = gp_db
-        self.lp = lp
-
+    def parse(self, afile):
         local_path = self.lp.cache_path('gpo_cache')
         data_file = os.path.join(local_path, check_safe_path(afile).upper())
         if os.path.exists(data_file):
@@ -329,15 +313,15 @@ class gp_ext(object):
         pass
 
 
-class gp_ext_setter():
+class gp_ext_setter(object):
     __metaclass__ = ABCMeta
 
-    def __init__(self, logger, ldb, gp_db, lp, attribute, val):
+    def __init__(self, logger, gp_db, lp, creds, attribute, val):
         self.logger = logger
-        self.ldb = ldb
         self.attribute = attribute
         self.val = val
         self.lp = lp
+        self.creds = creds
         self.gp_db = gp_db
 
     def explicit(self):
@@ -351,56 +335,24 @@ class gp_ext_setter():
     def mapper(self):
         pass
 
+    def delete(self):
+        upd_sam, _ = self.mapper().get(self.attribute)
+        upd_sam(self.val)
+
     @abstractmethod
     def __str__(self):
         pass
 
 
 class gp_inf_ext(gp_ext):
-    @abstractmethod
-    def list(self, rootpath):
-        pass
-
-    @abstractmethod
-    def apply_map(self):
-        pass
-
     def read(self, policy):
-        ret = False
-        inftable = self.apply_map()
-
-        current_section = None
-
-        # So here we would declare a boolean,
-        # that would get changed to TRUE.
-        #
-        # If at any point in time a GPO was applied,
-        # then we return that boolean at the end.
-
         inf_conf = ConfigParser()
         inf_conf.optionxform = str
         try:
             inf_conf.readfp(StringIO(policy))
         except:
             inf_conf.readfp(StringIO(policy.decode('utf-16')))
-
-        for section in inf_conf.sections():
-            current_section = inftable.get(section)
-            if not current_section:
-                continue
-            for key, value in inf_conf.items(section):
-                if current_section.get(key):
-                    (att, setter) = current_section.get(key)
-                    value = value.encode('ascii', 'ignore')
-                    ret = True
-                    setter(self.logger, self.ldb, self.gp_db, self.lp, att,
-                           value).update_samba()
-                    self.gp_db.commit()
-        return ret
-
-    @abstractmethod
-    def __str__(self):
-        pass
+        return inf_conf
 
 
 ''' Fetch the hostname of a writable DC '''
@@ -462,6 +414,12 @@ def check_refresh_gpo_list(dc_hostname, lp, creds, gpos):
         cache_gpo_dir(conn, cache_path, check_safe_path(gpo.file_sys_path))
 
 
+def get_deleted_gpos_list(gp_db, gpos):
+    applied_gpos = gp_db.get_applied_guids()
+    current_guids = set([p.name for p in gpos])
+    deleted_gpos = [guid for guid in applied_gpos if guid not in current_guids]
+    return gp_db.get_applied_settings(deleted_gpos)
+
 def gpo_version(lp, path):
     # gpo.gpo_get_sysvol_gpt_version() reads the GPT.INI from a local file,
     # read from the gpo client cache.
@@ -469,10 +427,11 @@ def gpo_version(lp, path):
     return int(gpo.gpo_get_sysvol_gpt_version(gpt_path)[1])
 
 
-def apply_gp(lp, creds, test_ldb, logger, store, gp_extensions):
+def apply_gp(lp, creds, logger, store, gp_extensions, force=False):
     gp_db = store.get_gplog(creds.get_username())
     dc_hostname = get_dc_hostname(creds, lp)
     gpos = get_gpo_list(dc_hostname, creds, lp)
+    del_gpos = get_deleted_gpos_list(gp_db, gpos)
     try:
         check_refresh_gpo_list(dc_hostname, lp, creds, gpos)
     except:
@@ -480,52 +439,54 @@ def apply_gp(lp, creds, test_ldb, logger, store, gp_extensions):
                      % dc_hostname)
         return
 
+    if force:
+        changed_gpos = gpos
+        gp_db.state(GPOSTATE.ENFORCE)
+    else:
+        changed_gpos = []
+        for gpo_obj in gpos:
+            if not gpo_obj.file_sys_path:
+                continue
+            guid = gpo_obj.name
+            path = check_safe_path(gpo_obj.file_sys_path).upper()
+            version = gpo_version(lp, path)
+            if version != store.get_int(guid):
+                logger.info('GPO %s has changed' % guid)
+                changed_gpos.append(gpo_obj)
+        gp_db.state(GPOSTATE.APPLY)
+
+    store.start()
+    for ext in gp_extensions:
+        try:
+            ext.process_group_policy(del_gpos, changed_gpos)
+        except Exception as e:
+            logger.error('Failed to apply extension  %s' % str(ext))
+            logger.error('Message was: ' + str(e))
+            continue
     for gpo_obj in gpos:
-        guid = gpo_obj.name
-        if guid == 'Local Policy':
+        if not gpo_obj.file_sys_path:
             continue
-        path = os.path.join(lp.get('realm'), 'Policies', guid).upper()
+        guid = gpo_obj.name
+        path = check_safe_path(gpo_obj.file_sys_path).upper()
         version = gpo_version(lp, path)
-        if version != store.get_int(guid):
-            logger.info('GPO %s has changed' % guid)
-            gp_db.state(GPOSTATE.APPLY)
-        else:
-            gp_db.state(GPOSTATE.ENFORCE)
-        gp_db.set_guid(guid)
-        store.start()
-        for ext in gp_extensions:
-            try:
-                ext.parse(ext.list(path), test_ldb, gp_db, lp)
-            except Exception as e:
-                logger.error('Failed to parse gpo %s for extension %s' %
-                             (guid, str(ext)))
-                logger.error('Message was: ' + str(e))
-                store.cancel()
-                continue
         store.store(guid, '%i' % version)
-        store.commit()
-
-
-def unapply_log(gp_db):
-    while True:
-        item = gp_db.apply_log_pop()
-        if item:
-            yield item
-        else:
-            break
+    store.commit()
 
 
-def unapply_gp(lp, creds, test_ldb, logger, store, gp_extensions):
+def unapply_gp(lp, creds, logger, store, gp_extensions):
     gp_db = store.get_gplog(creds.get_username())
     gp_db.state(GPOSTATE.UNAPPLY)
-    for gpo_guid in unapply_log(gp_db):
-        gp_db.set_guid(gpo_guid)
-        unapply_attributes = gp_db.list(gp_extensions)
-        for attr in unapply_attributes:
-            attr_obj = attr[-1](logger, test_ldb, gp_db, lp, attr[0], attr[1])
-            attr_obj.mapper()[attr[0]][0](attr[1])  # Set the old value
-            gp_db.delete(str(attr_obj), attr[0])
-        gp_db.commit()
+    # Treat all applied gpos as deleted
+    del_gpos = gp_db.get_applied_settings(gp_db.get_applied_guids())
+    store.start()
+    for ext in gp_extensions:
+        try:
+            ext.process_group_policy(del_gpos, [])
+        except Exception as e:
+            logger.error('Failed to unapply extension  %s' % str(ext))
+            logger.error('Message was: ' + str(e))
+            continue
+    store.commit()
 
 
 def parse_gpext_conf(smb_conf):
diff --git a/python/samba/tests/gpo.py b/python/samba/tests/gpo.py
index 8817285..6cb0edf 100644
--- a/python/samba/tests/gpo.py
+++ b/python/samba/tests/gpo.py
@@ -17,15 +17,61 @@
 import os
 from samba import gpo, tests
 from samba.gpclass import register_gp_extension, list_gp_extensions, \
-    unregister_gp_extension
+    unregister_gp_extension, gp_log, GPOStorage
 from samba.param import LoadParm
 from samba.gpclass import check_refresh_gpo_list, check_safe_path, \
-    check_guid, parse_gpext_conf, atomic_write_conf
+    check_guid, parse_gpext_conf, atomic_write_conf, get_deleted_gpos_list
+from subprocess import Popen, PIPE
+from tempfile import NamedTemporaryFile
+from samba.gp_sec_ext import gp_sec_ext
+import logging
+from samba.credentials import Credentials
 
 poldir = r'\\addom.samba.example.com\sysvol\addom.samba.example.com\Policies'
 dspath = 'CN=Policies,CN=System,DC=addom,DC=samba,DC=example,DC=com'
 gpt_data = '[General]\nVersion=%d'
 
+def days2rel_nttime(val):
+    seconds = 60
+    minutes = 60
+    hours = 24
+    sam_add = 10000000
+    return -(val * seconds * minutes * hours * sam_add)
+
+def gpupdate_force(lp):
+    gpupdate = lp.get('gpo update command')
+    gpupdate.append('--force')
+
+    return Popen(gpupdate, stdout=PIPE, stderr=PIPE).wait()
+
+def gpupdate_unapply(lp):
+    gpupdate = lp.get('gpo update command')
+    gpupdate.append('--unapply')
+
+    return Popen(gpupdate, stdout=PIPE, stderr=PIPE).wait()
+
+def stage_file(path, data):
+    dirname = os.path.dirname(path)
+    if not os.path.exists(dirname):
+        try:
+            os.makedirs(dirname)
+        except OSError as e:
+            if not (e.errno == errno.EEXIST and os.path.isdir(dirname)):
+                return False
+    if os.path.exists(path):
+        os.rename(path, '%s.bak' % path)
+    with NamedTemporaryFile(delete=False, dir=os.path.dirname(path)) as f:
+        f.write(data)
+        os.rename(f.name, path)
+        os.chmod(path, 0o644)
+    return True
+
+def unstage_file(path):
+    backup = '%s.bak' % path
+    if os.path.exists(backup):
+        os.rename(backup, path)


-- 
Samba Shared Repository



More information about the samba-cvs mailing list