From 218531b03114287dd5251704c55cf99bd3f4bcd5 Mon Sep 17 00:00:00 2001
From: Mark Reynolds <mreynolds@redhat.com>
Date: Fri, 31 Jul 2020 09:34:45 -0400
Subject: [PATCH] Issue 8407 - Support changelog integration into main database

Description: Add support for both the old and new replication changelogs.
             First try to get and update the new entry, if it's not found
             then we know we need to update the old global changelog entry.

https://pagure.io/freeipa/issue/8407

Reviewed by: ?
---
 install/updates/05-pre_upgrade_plugins.update |  1 +
 install/updates/20-replication.update         |  4 --
 .../plugins/update_changelog_maxage.py        | 68 ++++++++++++++++++
 .../plugins/update_unhashed_password.py       | 43 ++++++++---
 ipaserver/install/replication.py              | 72 ++++++++++++++-----
 5 files changed, 156 insertions(+), 32 deletions(-)
 create mode 100644 ipaserver/install/plugins/update_changelog_maxage.py

diff --git a/install/updates/05-pre_upgrade_plugins.update b/install/updates/05-pre_upgrade_plugins.update
index d0e3eb7ced..d7ed5f3aa1 100644
--- a/install/updates/05-pre_upgrade_plugins.update
+++ b/install/updates/05-pre_upgrade_plugins.update
@@ -1,5 +1,6 @@
 # first
 plugin: update_managed_post_first
+plugin: update_changelog_maxage
 
 # middle
 plugin: update_replica_attribute_lists
diff --git a/install/updates/20-replication.update b/install/updates/20-replication.update
index c9d96066d5..34beebc10a 100644
--- a/install/updates/20-replication.update
+++ b/install/updates/20-replication.update
@@ -62,7 +62,3 @@ default: nsslapd-plugin-depends-on-named: Multimaster Replication Plugin
 default: nsslapd-pluginVersion: 1.0
 default: nsslapd-pluginVendor: none
 default: nsslapd-pluginDescription: none
-
-# Set replication changelog limit (#5086)
-dn: cn=changelog5,cn=config
-addifnew: nsslapd-changelogmaxage: 7d
diff --git a/ipaserver/install/plugins/update_changelog_maxage.py b/ipaserver/install/plugins/update_changelog_maxage.py
new file mode 100644
index 0000000000..e14c305602
--- /dev/null
+++ b/ipaserver/install/plugins/update_changelog_maxage.py
@@ -0,0 +1,68 @@
+# Authors:
+#   Mark Reynolds <mreynolds@redhat.com>
+#
+# Copyright (C) 2020 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+
+from ipalib import Registry, errors
+from ipalib import Updater
+from ipapython.dn import DN
+
+logger = logging.getLogger(__name__)
+
+register = Registry()
+
+
+@register()
+class update_changelog_maxage(Updater):
+    """
+    Update the changelog maxage if it is not set
+    """
+
+    def update_entry(cl_entry, conn):
+        maxage = cl_entry.single_value.get(['nsslapd-changelogmaxage'])
+        if maxage is None:
+            cl_entry['nsslapd-changelogmaxage'] = '7d'
+            conn.update_entry(cl_entry)
+
+    def execute(self, **options):
+        ldap = self.api.Backend.ldap2
+
+        for backend in ('userroot', 'ipaca'):
+            dn = DN(
+                ('cn', 'changelog'),
+                ('cn', backend),
+                ('cn', 'ldbm database'),
+                ('cn', 'plugins'),
+                ('cn', 'config'))
+            try:
+                cl_entry = ldap.get_entry(dn, ['nsslapd-changelogmaxage'])
+                self.update_entry(cl_entry, ldap)
+            except errors.NotFound:
+                # Try the old global changelog, and return
+                dn = DN(
+                    ('cn', 'changelog5'),
+                    ('cn', 'config'))
+                try:
+                    cl_entry = ldap.get_entry(dn, ['nsslapd-changelogmaxage'])
+                    self.update_entry(cl_entry, ldap)
+                except errors.NotFound:
+                    logger.warning('Error retrieving: %s', str(dn))
+                return False, []
+
+        return False, []
diff --git a/ipaserver/install/plugins/update_unhashed_password.py b/ipaserver/install/plugins/update_unhashed_password.py
index 8daf3f018f..1ece372c83 100644
--- a/ipaserver/install/plugins/update_unhashed_password.py
+++ b/ipaserver/install/plugins/update_unhashed_password.py
@@ -78,22 +78,47 @@ def execute(self, **options):
             # We are running in a winsync environment
             # Log a warning that changelog will contain sensitive data
             try:
-                cldb_e = ldap.get_entry(
-                    DN(('cn', 'changelog5'),
+                # Check if the new per-backend changelog exists...
+                cldb = ldap.get_entry(
+                    DN(('cn', 'changelog'),
+                       ('cn', 'userRoot'),
+                       ('cn', 'ldbm database'),
+                       ('cn', 'plugins'),
+                       ('cn', 'config')))
+
+                # We have a backend changelog so get the db dir in this case
+                db_entry = ldap.get_entry(
+                    DN(('cn', 'userRoot'),
+                       ('cn', 'ldbm database'),
+                       ('cn', 'plugins'),
                        ('cn', 'config')),
-                    ['nsslapd-changelogdir'])
-                cldb = cldb_e.single_value.get("nsslapd-changelogdir")
+                    ['nsslapd-directory'])
+                cldb = db_entry.single_value.get("nsslapd-directory")
                 logger.warning("This server is configured for winsync, "
                                "the changelog files under %s "
                                "may contain clear text passwords.\n"
                                "Please ensure that these files can be accessed"
                                " only by trusted accounts.\n", cldb)
             except errors.NotFound:
-                logger.warning("This server is configured for winsync, "
-                               "the changelog files may contain "
-                               "clear text passwords.\n"
-                               "Please ensure that these files can be accessed"
-                               " only by trusted accounts.\n")
+                # Did not find backend changelog, check the global changelog
+                try:
+                    cldb_e = ldap.get_entry(
+                        DN(('cn', 'changelog5'),
+                           ('cn', 'config')),
+                        ['nsslapd-changelogdir'])
+                    cldb = cldb_e.single_value.get("nsslapd-changelogdir")
+                    logger.warning("This server is configured for winsync, "
+                                   "the changelog files under %s "
+                                   "may contain clear text passwords.\n"
+                                   "Please ensure that these files can be "
+                                   "accessed only by trusted accounts.\n",
+                                   cldb)
+                except errors.NotFound:
+                    logger.warning("This server is configured for winsync, "
+                                   "the changelog files may contain "
+                                   "clear text passwords.\n"
+                                   "Please ensure that these files can be "
+                                   "accessed only by trusted accounts.\n")
             if toggle.lower() == 'on':
                 # The current DS configuration already logs the
                 # unhashed password
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index cca39779f4..5bec3f771c 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -478,6 +478,15 @@ def replica_dn(self):
         return DN(('cn', 'replica'), ('cn', self.db_suffix),
                   ('cn', 'mapping tree'), ('cn', 'config'))
 
+    def get_be_name(self, conn):
+        # Get the backend name for this suffix
+        suffix_entry = conn.get_entry(
+            DN(('cn', self.db_suffix),
+               ('cn', 'mapping tree'),
+               ('cn', 'config')),
+            ['nsslapd-backend'])
+        return suffix_entry.single_value.get('nsslapd-backend')
+
     def _set_replica_binddngroup(self, r_conn, entry):
         """
         Set nsds5replicabinddngroup attribute on remote master's replica entry.
@@ -566,26 +575,51 @@ def replica_config(self, conn, replica_id, replica_binddn):
         return entry
 
     def setup_changelog(self, conn):
-        ent = conn.get_entry(
-            DN(
-                ('cn', 'config'), ('cn', 'ldbm database'),
-                ('cn', 'plugins'), ('cn', 'config')),
-            ['nsslapd-directory'])
-        dbdir = os.path.dirname(ent.single_value.get('nsslapd-directory'))
-
-        entry = conn.make_entry(
-            DN(('cn', 'changelog5'), ('cn', 'config')),
-            {
-                'objectclass': ["top", "extensibleobject"],
-                'cn': ["changelog5"],
-                'nsslapd-changelogdir': [os.path.join(dbdir, "cldb")],
-                'nsslapd-changelogmaxage': ['7d'],
-            }
-        )
         try:
-            conn.add_entry(entry)
-        except errors.DuplicateEntry:
-            return
+            """Check if we have the new per backend changelog, and set
+            the trimming max-age setting.  If the new changelog entry
+            is not found then we are still using the old global changelog.
+            """
+            cl_dn = DN(
+                ('cn', 'changelog'),
+                ('cn', self.get_be_name()),
+                ('cn', 'ldbm database'),
+                ('cn', 'plugins'),
+                ('cn', 'config'))
+            cl_entry = conn.get_entry(cl_dn)
+        except errors.NotFound:
+            """Did not find a per-backend changelog, so add the global
+            changelog entry.  First get the database directory to build
+            the changelog directory location from.
+            """
+            ent = conn.get_entry(
+                DN(
+                    ('cn', 'config'),
+                    ('cn', 'ldbm database'),
+                    ('cn', 'plugins'),
+                    ('cn', 'config')),
+                ['nsslapd-directory'])
+            dbdir = os.path.dirname(ent.single_value.get('nsslapd-directory'))
+
+            entry = conn.make_entry(
+                DN(
+                    ('cn', 'changelog5'),
+                    ('cn', 'config')),
+                {
+                    'objectclass': ["top", "extensibleobject"],
+                    'cn': ["changelog5"],
+                    'nsslapd-changelogdir': [os.path.join(dbdir, "cldb")],
+                    'nsslapd-changelogmaxage': ['7d'],
+                }
+            )
+            try:
+                conn.add_entry(entry)
+            except errors.DuplicateEntry:
+                return
+        else:
+            # Set the changelog trimming
+            cl_entry['nsslapd-changelogmaxage'] = '7d'
+            conn.update_entry(cl_entry)
 
     def _finalize_replica_settings(self, conn):
         """Change replica settings to final values
