From 28d62750d7af902091682e6d29ab563bb957849e Mon Sep 17 00:00:00 2001
From: Patrick Uiterwijk <puiterwijk@redhat.com>
Date: May 04 2017 15:18:00 +0000
Subject: [PATCH 1/2] Add test for upgrading readonly plugin database


Signed-off-by: Patrick Uiterwijk <puiterwijk@redhat.com>

---

diff --git a/tests/blobs/old_dbs/adminconfig.cfg b/tests/blobs/old_dbs/adminconfig.cfg
new file mode 100644
index 0000000..8f21dba
--- /dev/null
+++ b/tests/blobs/old_dbs/adminconfig.cfg
@@ -0,0 +1,20 @@
+[config]
+[info_config]
+[login_config]
+global enabled=testauth
+[provider_config]
+global enabled=openid,persona,saml2
+openid endpoint url=http://127.0.0.11:45081/idp_v1/openid/
+openid database url=configfile://openid.cfg
+openid identity url template=http://127.0.0.11:45081/idp_v1/openid/id/%(username)s
+openid enabled extensions=
+persona allowed domains=127.0.0.11:45081
+persona issuer domain=127.0.0.11:45081
+persona idp key file=persona/persona.key
+saml2 idp nameid salt=6c78ae3b33db4fe4886edb1679490821
+saml2 idp metadata validity=1825
+saml2 idp certificate file=saml2/idp.pem
+saml2 idp key file=saml2/idp.key
+saml2 session database url=saml2.sessions.db.sqlite
+saml2 idp metadata file=metadata.xml
+saml2 idp storage path=saml2
diff --git a/tests/dbupgrades.py b/tests/dbupgrades.py
index 0d06a57..de469ac 100755
--- a/tests/dbupgrades.py
+++ b/tests/dbupgrades.py
@@ -40,8 +40,12 @@ class IpsilonTest(IpsilonTestBase):
     def setup_servers(self, env=None):
         pass
 
-    def dump_admin_config_db(self, db_outdir):
-        test_db = os.path.join(db_outdir, 'adminconfig.sqlite')
+    def dump_db(self, db_outdir, readonly):
+        if readonly:
+            db_name = 'userprefs'
+        else:
+            db_name = 'adminconfig'
+        test_db = os.path.join(db_outdir, '%s.sqlite' % db_name)
         p = subprocess.Popen(['/usr/bin/sqlite3', test_db, '.dump'],
                              stdout=subprocess.PIPE)
         output, _ = p.communicate()
@@ -50,10 +54,29 @@ class IpsilonTest(IpsilonTestBase):
             sys.exit(1)
         return output
 
-    def test_upgrade_from(self, env, old_version):
+    def use_readonly_adminconfig(self, name):
+        admincfg = os.path.join(self.rootdir, 'tests', 'blobs', 'old_dbs',
+                                'adminconfig.cfg')
+        cfgfile = os.path.join(self.testdir, 'lib', name, 'ipsilon.conf')
+        with open(cfgfile, 'r') as f:
+            cfg = f.readlines()
+        with open(cfgfile, 'w') as f:
+            for line in cfg:
+                if line.startswith('admin.config.db'):
+                    line = 'admin.config.db = "configfile://%s"\n' % admincfg
+                f.write(line)
+        with open(os.path.join(self.testdir, 'lib', name, 'openid.cfg'), 'w'):
+            # Just make the file exist. We don't actually use the OpenID plugin
+            # during this test, but it serves as a test for upgrading with
+            # readonly plugin databases.
+            pass
+
+    def test_upgrade_from(self, env, old_version, with_readonly):
         # Setup IDP Server
         print "Installing IDP server to test upgrade from %i" % old_version
         name = 'idp_v%i' % old_version
+        if with_readonly:
+            name = name + '_readonly'
         addr = '127.0.0.%i' % (10 + old_version)
         port = str(45080 + old_version)
         idp = self.generate_profile(idp_g, idp_a, name, addr, port)
@@ -65,6 +88,9 @@ class IpsilonTest(IpsilonTestBase):
                                 'v%i' % old_version)
         db_outdir = os.path.join(self.testdir, 'lib', name)
 
+        if with_readonly:
+            self.use_readonly_adminconfig(name)
+
         if old_version > 0:
             for database in ['adminconfig',
                              'openid',
@@ -74,8 +100,10 @@ class IpsilonTest(IpsilonTestBase):
                 db_in = os.path.join(db_indir, '%s.sqlite.dump' % database)
                 db_out = os.path.join(db_outdir, '%s.sqlite' % database)
                 os.unlink(db_out)
-                cmd = ['/usr/bin/sqlite3', db_out, '.read %s' % db_in]
-                subprocess.check_call(cmd)
+                if database not in ['adminconfig',
+                                    'openid'] or not with_readonly:
+                    cmd = ['/usr/bin/sqlite3', db_out, '.read %s' % db_in]
+                    subprocess.check_call(cmd)
 
             # Upgrade that database
             cmd = [os.path.join(self.rootdir,
@@ -89,7 +117,7 @@ class IpsilonTest(IpsilonTestBase):
         if old_version == 0:
             # Check all features in a newly created database
             # Let's verify if at least one index was created
-            output = self.dump_admin_config_db(db_outdir)
+            output = self.dump_db(db_outdir, with_readonly)
             if 'CREATE INDEX' not in output:
                 raise Exception('Database upgrade did not introduce index')
             if 'PRIMARY KEY' not in output:
@@ -98,15 +126,15 @@ class IpsilonTest(IpsilonTestBase):
         elif old_version == 1:
             # In 1 -> 2, we added indexes and primary keys
             # Let's verify if at least one index was created
-            output = self.dump_admin_config_db(db_outdir)
+            output = self.dump_db(db_outdir, with_readonly)
             if 'CREATE INDEX' not in output:
                 raise Exception('Database upgrade did not introduce index')
             # SQLite did not support creating primary keys, so we can't test
 
-        elif old_version == 2:
+        elif old_version == 2 and not with_readonly:
             # Version 3 added the authz_config table
             # Make sure it exists
-            output = self.dump_admin_config_db(db_outdir)
+            output = self.dump_db(db_outdir, with_readonly)
             if 'TABLE authz_config' not in output:
                 raise Exception('Database upgrade did not introduce ' +
                                 'authz_config table')
@@ -120,6 +148,10 @@ class IpsilonTest(IpsilonTestBase):
             exe = exe[:-1]
         exe = [exe]
         exe.append(str(old_version))
+        if with_readonly:
+            exe.append('readonly')
+        else:
+            exe.append('no-readonly')
         exe.append(name)
         exe.append('%s:%s' % (addr, port))
         exit_code = subprocess.call(exe, env=env)
@@ -132,13 +164,15 @@ class IpsilonTest(IpsilonTestBase):
 
     def run(self, env):
         for version in range(ipsilon.util.data.CURRENT_SCHEMA_VERSION):
-            self.test_upgrade_from(env, version)
+            for with_readonly in [True, False]:
+                self.test_upgrade_from(env, version, with_readonly)
 
 
 if __name__ == '__main__':
     from_version = sys.argv[1]
-    idpname = sys.argv[2]
-    url = sys.argv[3]
+    with_ro = sys.argv[2]
+    idpname = sys.argv[3]
+    url = sys.argv[4]
 
     user = pwd.getpwuid(os.getuid())[0]
 
@@ -146,7 +180,8 @@ if __name__ == '__main__':
     sess.add_server(idpname, 'https://%s' % url, user,
                     'ipsilon')
 
-    print "dbupgrades: From v%s: Authenticate to IDP ..." % from_version,
+    print "dbupgrades: From v%s %s: Authenticate to IDP ..." % (from_version,
+                                                                with_ro),
     try:
         sess.auth_to_idp(idpname)
     except Exception, e:  # pylint: disable=broad-except

From f178eebb02ad3862e3fdc863b52289e6cdd952e1 Mon Sep 17 00:00:00 2001
From: Patrick Uiterwijk <puiterwijk@redhat.com>
Date: May 04 2017 15:18:00 +0000
Subject: [PATCH 2/2] Skip database upgrades for readonly databases


This fixes upgrading setups with readonly plugin databases.

Fixes: #273
Signed-off-by: Patrick Uiterwijk <puiterwijk@redhat.com>

---

diff --git a/ipsilon/util/data.py b/ipsilon/util/data.py
index 5266a85..e2c9220 100644
--- a/ipsilon/util/data.py
+++ b/ipsilon/util/data.py
@@ -860,6 +860,12 @@ class Store(Log):
         raise NotImplementedError()
 
     def upgrade_database(self):
+        if self.is_readonly:
+            # If the database is readonly, we cannot do anything to the
+            #  schema. Let's just return, and assume people checked the
+            #  upgrade notes
+            return
+
         # Do whatever is needed to get schema to current version
         old_schema_version = self._get_schema_version()
         if old_schema_version is None:

