From 4a1fb1e678d0024d9ee51fcda0d83f74f1715f4b Mon Sep 17 00:00:00 2001
From: Ade Lee <alee@redhat.com>
Date: Thu, 2 Jun 2016 09:41:35 -0400
Subject: [PATCH 319/319] Modify pki-srever db-upgrade to do realm related
 upgrades

Tickets 2320, 2319
---
 base/server/python/pki/server/__init__.py |  10 +-
 base/server/python/pki/server/cli/db.py   | 211 +++++++++++++++++++++++++++++-
 2 files changed, 213 insertions(+), 8 deletions(-)

diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py
index 47f6aa5b46af6cf38b231d94a88e6bb02c7d99b1..8fe2d29b8d7dabdd50cc43f26aa9f1345481e385 100644
--- a/base/server/python/pki/server/__init__.py
+++ b/base/server/python/pki/server/__init__.py
@@ -318,7 +318,7 @@ class PKISubsystem(object):
     def disable(self):
         self.instance.undeploy(self.name)
 
-    def open_database(self, name='internaldb'):
+    def open_database(self, name='internaldb', user=None, password=None):
 
         # TODO: add LDAPI support
         hostname = self.config['%s.ldapconn.host' % name]
@@ -341,7 +341,13 @@ class PKISubsystem(object):
         connection.set_security_database(self.instance.nssdb_dir)
 
         auth_type = self.config['%s.ldapauth.authtype' % name]
-        if auth_type == 'BasicAuth':
+        if (user is not None and password is not None):
+            # connect using the provided credentials
+            connection.set_credentials(
+                bind_dn=user,
+                bind_password=password
+            )
+        elif auth_type == 'BasicAuth':
             connection.set_credentials(
                 bind_dn=self.config['%s.ldapauth.bindDN' % name],
                 bind_password=self.instance.get_password(name)
diff --git a/base/server/python/pki/server/cli/db.py b/base/server/python/pki/server/cli/db.py
index 6cfd6b410eb75394f4b88b087b43a0538d3db6c6..d011a97d6967e0b5cf1e63a7c54f522f94733ba2 100644
--- a/base/server/python/pki/server/cli/db.py
+++ b/base/server/python/pki/server/cli/db.py
@@ -22,8 +22,13 @@ from __future__ import absolute_import
 from __future__ import print_function
 import getopt
 import ldap
+import ldap.modlist
+import ldif
 import nss.nss as nss
+import os
+import subprocess
 import sys
+from tempfile import NamedTemporaryFile
 
 import pki.cli
 
@@ -38,6 +43,17 @@ class DBCLI(pki.cli.CLI):
 
 
 class DBUpgrade(pki.cli.CLI):
+
+    KRA_VLV_PATH = '/usr/share/pki/kra/conf/vlv.ldif'
+
+    KRA_VLVS = ['allKeys', 'kraAll',
+                'kraArchival', 'kraRecovery',
+                'kraCanceled', 'kraCanceledEnrollment', 'kraCanceledRecovery',
+                'kraRejected', 'kraRejectedEnrollment', 'kraRejectedRecovery',
+                'kraComplete', 'kraCompleteEnrollment', 'kraCompleteRecovery']
+
+    SCHEMA_PATH = '/usr/share/pki/server/conf/schema.ldif'
+
     def __init__(self):
         super(DBUpgrade, self).__init__(
             'upgrade', 'Upgrade PKI server database')
@@ -46,6 +62,9 @@ class DBUpgrade(pki.cli.CLI):
         print('Usage: pki-server db-upgrade [OPTIONS]')
         print()
         print('  -i, --instance <instance ID>       Instance ID (default: pki-tomcat).')
+        print('  -u, --user <User ID>               User to connect to DB.')
+        print('  -w, --password <password>          Password to connect to DB.')
+        print('  -x  --server-id <ds ID>            Server ID for the DB')
         print('  -v, --verbose                      Run in verbose mode.')
         print('      --help                         Show help message.')
         print()
@@ -53,7 +72,11 @@ class DBUpgrade(pki.cli.CLI):
     def execute(self, args):
         try:
             opts, _ = getopt.gnu_getopt(
-                args, 'i:v', ['instance=', 'verbose', 'help'])
+                args,
+                'i:u:w:x:v',
+                ['instance=', 'user=', 'password=', 'server_id=',
+                 'verbose', 'help']
+            )
 
         except getopt.GetoptError as e:
             print('ERROR: ' + str(e))
@@ -66,6 +89,15 @@ class DBUpgrade(pki.cli.CLI):
             if o in ('-i', '--instance'):
                 instance_name = a
 
+            elif o in ('-u', '--user'):
+                user = a
+
+            elif o in ('-w', '--password'):
+                password = a
+
+            elif o in ('-x', '--server-id'):
+                server_id = a
+
             elif o in ('-v', '--verbose'):
                 self.set_verbose(True)
 
@@ -83,10 +115,22 @@ class DBUpgrade(pki.cli.CLI):
         instance = pki.server.PKIInstance(instance_name)
         instance.load()
 
+        self.add_issuer_name_to_ca(instance)
+
+        # realm changes
+        self.modify_schema(instance, user, password)
+        self.create_realm_index(instance, user, password, server_id)
+        self.modify_kra_vlv(instance, user, password, server_id)
+
+        self.print_message('Upgrade complete')
+
+    def add_issuer_name_to_ca(self, instance):
         subsystem = instance.get_subsystem('ca')
         if not subsystem:
-            print('ERROR: missing subsystem ca')
-            sys.exit(1)
+            if self.verbose:
+                print('add_issuer_name: No CA subsystem available.  '
+                      'Skipping ...')
+            return
 
         base_dn = subsystem.config['internaldb.basedn']
         conn = subsystem.open_database()
@@ -94,7 +138,8 @@ class DBUpgrade(pki.cli.CLI):
         try:
             repo_dn = 'ou=certificateRepository,ou=ca,%s' % base_dn
             if self.verbose:
-                print('Searching certificates records with missing issuerName in %s' % repo_dn)
+                print('Searching for certificate records with missing '
+                      'issuerName in %s' % repo_dn)
 
             entries = conn.ldap.search_s(
                 repo_dn,
@@ -108,8 +153,6 @@ class DBUpgrade(pki.cli.CLI):
         finally:
             conn.close()
 
-        self.print_message('Upgrade complete')
-
     def add_issuer_name(self, conn, entry):
         dn, attrs = entry
 
@@ -129,3 +172,159 @@ class DBUpgrade(pki.cli.CLI):
             print(
                 'Failed to add issuerName to certificate {}: {}'
                 .format(attrs.get('cn', ['<unknown>'])[0], e))
+
+    def modify_kra_vlv(self, instance, user, password, server_id=None):
+        subsystem = instance.get_subsystem('kra')
+        if not subsystem:
+            if self.verbose:
+                print('modify_kra_vlv: No KRA subsystem available.  '
+                      'Skipping ...')
+                return
+
+        if self.verbose:
+            print("Modifying KRA VLVs for realm")
+
+        conn = subsystem.open_database(user=user, password=password)
+
+        ldif_file = self.create_ldif(instance, subsystem, self.KRA_VLV_PATH)
+        database = subsystem.config['internaldb.database']
+
+        try:
+            # remove old entries
+            for vlv in self.KRA_VLVS:
+                dn = ("cn=" + vlv + '-' + instance.name + ',cn=' + database +
+                      ',cn=ldbm database, cn=plugins, cn=config')
+                index_dn = "cn=" + vlv + '-' + instance.name + "Index," + dn
+
+                try:
+                    conn.ldap.delete_s(index_dn)
+                except ldap.NO_SUCH_OBJECT:
+                    pass
+
+                try:
+                    conn.ldap.delete_s(dn)
+                except ldap.NO_SUCH_OBJECT:
+                    pass
+
+            # add new entries
+            parser = ldif.LDIFRecordList(open(ldif_file, "rb"))
+            parser.parse()
+            for dn, entry in parser.all_records:
+                add_modlist = ldap.modlist.addModlist(entry)
+                conn.ldap.add_s(dn, add_modlist)
+        finally:
+            conn.close()
+
+        os.unlink(ldif_file)
+        self.reindex_kra_vlv(instance, subsystem, user, password, server_id)
+
+    def create_ldif(self, instance, subsystem, ldif_path):
+        subs = {'{instanceId}': instance.name,
+                '{database}': subsystem.config['internaldb.database'],
+                '{rootSuffix}': subsystem.config['internaldb.basedn']}
+
+        out_file = NamedTemporaryFile(delete=False)
+
+        with open(ldif_path) as infile, open(out_file.name, 'w') as outfile:
+            for line in infile:
+                for src, target in subs.items():
+                    line = line.replace(src, target)
+                outfile.write(line)
+
+        return out_file.name
+
+    def modify_schema(self, instance, user, password):
+        if self.verbose:
+            print("Modifying schema to latest")
+
+        subsystem = instance.subsystems[0]
+        host = subsystem.config['internaldb.ldapconn.host']
+        port = subsystem.config['internaldb.ldapconn.port']
+        cmd = ['ldapmodify',
+               '-c',
+               '-D', user,
+               '-w', password,
+               '-h', host,
+               '-p', port,
+               '-f', self.SCHEMA_PATH
+               ]
+
+        try:
+            subprocess.check_output(cmd)
+        except subprocess.CalledProcessError as e:
+            print('ldapmodify returns {}: {}'.format(e.returncode, e.output))
+
+    def create_realm_index(self, instance, user, password, server_id=None):
+        if self.verbose:
+            print("Adding realm index")
+
+        subsystem = instance.subsystems[0]
+        conn = subsystem.open_database(user=user, password=password)
+        database = subsystem.config['internaldb.database']
+
+        try:
+            dn = ("cn=realm,cn=index,cn=" + database +
+                  ",cn=ldbm database, cn=plugins, cn=config")
+
+            attrs = {'objectclass': ['top', 'nsIndex'],
+                     'cn': 'realm',
+                     'nsIndexType': ['eq', 'pres'],
+                     'nsSystemIndex': 'false'}
+
+            mods = ldap.modlist.addModlist(attrs)
+
+            conn.ldap.add_s(dn, mods)
+
+            self.index_for_realm(instance, user, password, server_id)
+        except ldap.ALREADY_EXISTS:
+            if self.verbose:
+                print("realm index already exists.  Skipping ..")
+        finally:
+            conn.close()
+
+    def index_for_realm(self, instance, user, password, server_id=None):
+        subsystem = instance.get_subsystem('kra')
+        if not subsystem:
+            if self.verbose:
+                print('index_for_realm: No KRA subsystem available.  '
+                      'Skipping ...')
+            return
+
+        if self.verbose:
+            print('Indexing for new realm attribute')
+
+        database = subsystem.config['internaldb.database']
+        cmd = ['db2index.pl',
+               '-Z', server_id,
+               '-n', database,
+               '-D', user,
+               '-w', password,
+               '-t', 'realm:eq,pres'
+               ]
+
+        try:
+            subprocess.check_output(cmd)
+        except subprocess.CalledProcessError as e:
+            print('db2index.pl returns {}: {}'.format(e.returncode, e.output))
+
+    def reindex_kra_vlv(self, instance, subsystem, user,
+                        password, server_id=None):
+        if self.verbose:
+            print('Re-indexing KRA VLV indexes')
+
+        database = subsystem.config['internaldb.database']
+        cmd = ['db2index.pl',
+               '-Z', server_id,
+               '-n', database,
+               '-D', user,
+               '-w', password
+               ]
+
+        for key in self.KRA_VLVS:
+            cmd.append("-T")
+            cmd.append(key + '-' + instance.name + "Index")
+
+        try:
+            subprocess.check_output(cmd)
+        except subprocess.CalledProcessError as e:
+            print('db2index.pl returns {}: {}'.format(e.returncode, e.output))
-- 
2.4.3

