You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by rh...@apache.org on 2007/03/14 17:49:37 UTC

svn commit: r518214 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/jdbc/ engine/org/apache/derby/loc/ shared/org/apache/derby/shared/common/reference/ testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/ testing/org/apache/derbyTe...

Author: rhillegas
Date: Wed Mar 14 09:49:35 2007
New Revision: 518214

URL: http://svn.apache.org/viewvc?view=rev&rev=518214
Log:
DERBY-2264: Commit Dag's patch DERBY-2264.5.diff, which restricts encryption powers to the DBA.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DboPowersTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabasePropertyTestSetup.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java?view=diff&rev=518214&r1=518213&r2=518214
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/jdbc/EmbedConnection.java Wed Mar 14 09:49:35 2007
@@ -119,7 +119,7 @@
 	//////////////////////////////////////////////////////////
 	DatabaseMetaData dbMetadata;
 
-	final TransactionResourceImpl tr; // always access tr thru getTR()
+	TransactionResourceImpl tr; // always access tr thru getTR()
 
 	private HashMap lobHashMap = null;
 	private int lobHMKey = 0;
@@ -215,16 +215,37 @@
 
 			// See if user wants to create a new database.
 			boolean	createBoot = createBoot(info);	
+
+			// DERBY-2264: keeps track of whether we do a plain boot before an
+			// (re)encryption boot to (possibly) authenticate first. We can not
+			// authenticate before we have booted, so in order to enforce data
+			// base owner powers over encryption, we need a plain boot, then
+			// authenticate, then, if all is well, boot with (re)encryption.
+			// Encryption at create time is not checked.
+			boolean isTwoPhaseEncryptionBoot = (!createBoot &&
+												isEncryptionBoot(info));
+
+			// Save original properties if we modified them for
+			// isTwoPhaseEncryptionBoot.
+			Properties savedInfo = null;
+
 			if (database != null)
 			{
 				// database already booted by someone else
 				tr.setDatabase(database);
+				isTwoPhaseEncryptionBoot = false;
 			}
 			else if (!shutdown)
 			{
-				// Return false iff the monitor cannot handle a service of the type
-				// indicated by the proptocol within the name.  If that's the case
-				// then we are the wrong driver.
+				if (isTwoPhaseEncryptionBoot) {
+					savedInfo = info;
+					info = removeEncryptionProps((Properties)info.clone());
+				}
+
+				// Return false iff the monitor cannot handle a service of the
+				// type indicated by the proptocol within the name.  If that's
+				// the case then we are the wrong driver.
+
 				if (!bootDatabase(info))
 				{
 					tr.clearContextInError();
@@ -236,7 +257,8 @@
 
 			if (createBoot && !shutdown)
 			{
-				// if we are shutting down don't attempt to boot or create the database
+				// if we are shutting down don't attempt to boot or create the
+				// database
 
 				if (tr.getDatabase() != null) {
 					addWarning(EmbedSQLWarning.newEmbedSQLWarning(SQLState.DATABASE_EXISTS, getDBName()));
@@ -276,12 +298,47 @@
 			// the rest.
 			tr.startTransaction();
 
+			if (isTwoPhaseEncryptionBoot) {
+				// DERBY-2264: shutdown and boot again with encryption
+				// attributes active. This is restricted to the database owner.
+				if (!usingNoneAuth) {
+					// a failure here leaves database booted, but no
+					// (re)encryption has taken place and the connection is
+					// rejected.
+					checkIsDBOwner(OP_ENCRYPT);
+				}
+
+				// shutdown and reboot using saved properties which
+				// include the (re)encyption attributes
+				info = savedInfo;
+				handleException(tr.shutdownDatabaseException());
+				restoreContextStack();
+				tr = new TransactionResourceImpl(driver, url, info);
+				active = true;
+				setupContextStack();
+
+				if (!bootDatabase(info))
+				{
+					if (SanityManager.DEBUG) {
+						SanityManager.THROWASSERT(
+							"bootDatabase failed after initial plain boot " +
+							"for (re)encryption");
+					}
+					tr.clearContextInError();
+					setInactive();
+					return;
+				}
+				// don't need to check user credentials again, did
+				// that on first plain boot, so just start
+				tr.startTransaction();
+			}
+
 			// now we have the database connection, we can shut down
 			if (shutdown) {
 				if (!usingNoneAuth) {
-					// DERBY-2264: Only allow db owner to shut down if
+					// DERBY-2264: Only allow database owner to shut down if
 					// authentication is on.
-					checkIsDBOwner();
+					checkIsDBOwner(OP_SHUTDOWN);
 				}
 				throw tr.shutdownDatabaseException();
 			}
@@ -354,14 +411,8 @@
         // combination with createFrom/restoreFrom/rollForwardRecoveryFrom
         // attributes.  Re-encryption is not
         // allowed when restoring from backup.
-        if (restoreCount != 0 && 
-            (Boolean.valueOf(p.getProperty(
-                            Attribute.DATA_ENCRYPTION)).booleanValue() ||
-             p.getProperty(Attribute.NEW_BOOT_PASSWORD) != null ||
-             p.getProperty(Attribute.NEW_CRYPTO_EXTERNAL_KEY) != null
-             )) 
-        {
-            throw newSQLException(SQLState.CONFLICTING_RESTORE_ATTRIBUTES);
+        if (restoreCount != 0 && isEncryptionBoot(p)) {
+			throw newSQLException(SQLState.CONFLICTING_RESTORE_ATTRIBUTES);
         }
 
 
@@ -377,6 +428,37 @@
 	}
 
 	/**
+	 * Examine boot properties and determine if a boot with the given
+	 * attributes would entail an encryption operation.
+	 *
+	 * @param p the attribute set
+	 * @return true if a boot will encrypt or re-encrypt the database
+	 */
+	private boolean isEncryptionBoot(Properties p)
+	{
+		return ((Boolean.valueOf(
+					 p.getProperty(Attribute.DATA_ENCRYPTION)).booleanValue()) ||
+				(p.getProperty(Attribute.NEW_BOOT_PASSWORD) != null)           ||
+				(p.getProperty(Attribute.NEW_CRYPTO_EXTERNAL_KEY) != null));
+	}
+
+
+	/**
+	 * Remove any encryption properties from the given properties
+	 *
+	 * @param p the attribute set
+	 * @return clone sans encryption properties
+	 */
+	private Properties removeEncryptionProps(Properties p)
+	{
+		p.remove(Attribute.DATA_ENCRYPTION);
+		p.remove(Attribute.NEW_BOOT_PASSWORD);
+		p.remove(Attribute.NEW_CRYPTO_EXTERNAL_KEY);
+		return p;
+	}
+
+
+	/**
 	 * Create a new connection based off of the 
 	 * connection passed in.  Initializes state
 	 * based on input connection, and copies 
@@ -482,21 +564,38 @@
 			usingNoneAuth = true;
 	}
 
+	/* Enumerate operations controlled by database owner powers */
+	private static final int OP_ENCRYPT = 0;
+	private static final int OP_SHUTDOWN = 1;
 	/**
 	 * Check if actual authenticationId is equal to the database owner's.
 	 *
+	 * @param operation attempted operation which needs database owner powers
 	 * @throws SQLException if actual authenticationId is different
 	 * from authenticationId of database owner.
 	 */
-	private void checkIsDBOwner() throws SQLException
+	private void checkIsDBOwner(int operation) throws SQLException
 	{
 		final LanguageConnectionContext lcc = getLanguageConnection();
 		final String actualId = lcc.getAuthorizationId();
 		final String dbOwnerId = lcc.getDataDictionary().
 			getAuthorizationDatabaseOwner();
 		if (!actualId.equals(dbOwnerId)) {
-			throw newSQLException(SQLState.AUTH_NOT_DB_OWNER, 
-								  actualId, tr.getDBName());
+			switch (operation) {
+			case OP_ENCRYPT:
+				throw newSQLException(SQLState.AUTH_ENCRYPT_NOT_DB_OWNER,
+									  actualId, tr.getDBName());
+			case OP_SHUTDOWN:
+				throw newSQLException(SQLState.AUTH_SHUTDOWN_NOT_DB_OWNER,
+									  actualId, tr.getDBName());
+			default:
+				if (SanityManager.DEBUG) {
+					SanityManager.THROWASSERT(
+						"illegal checkIsDBOwner operation");
+				}
+				throw newSQLException(
+					SQLState.AUTH_DATABASE_CONNECTION_REFUSED);
+			}
 		}
 	}
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?view=diff&rev=518214&r1=518213&r2=518214
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Wed Mar 14 09:49:35 2007
@@ -851,6 +851,13 @@
                 <arg>databaseName</arg>
             </msg>
 
+            <msg>
+                <name>2850I.C</name>
+                <text>User '{0}' cannot (re)encrypt database '{1}'. Only the database owner can perform this operation.</text>
+                <arg>authorizationID</arg>
+                <arg>databaseName</arg>
+            </msg>
+
         </family>
 
 

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?view=diff&rev=518214&r1=518213&r2=518214
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java (original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java Wed Mar 14 09:49:35 2007
@@ -1379,7 +1379,8 @@
 	String AUTH_NOT_DATABASE_OWNER                                     = "2850E";
 	String AUTH_GRANT_REVOKE_NOT_ALLOWED                               = "2850F";
 	String AUTH_NO_OBJECT_PERMISSION                                   = "2850G";
-	String AUTH_NOT_DB_OWNER                                           = "2850H.C";
+	String AUTH_SHUTDOWN_NOT_DB_OWNER                                  = "2850H.C";
+	String AUTH_ENCRYPT_NOT_DB_OWNER                                   = "2850I.C";
 
 	/*
 	** Dependency manager

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DboPowersTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DboPowersTest.java?view=diff&rev=518214&r1=518213&r2=518214
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DboPowersTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/jdbcapi/DboPowersTest.java Wed Mar 14 09:49:35 2007
@@ -36,7 +36,7 @@
  *
  * The tests are run in the cross product (cardinality 10) of contexts:
  *
- *    {client/server, embedded} x 
+ *    {client/server, embedded} x
  *    {no authentication, authentication and authentication/sqlAuthorization} x
  *    {data base owner, other user }
  *
@@ -46,24 +46,54 @@
  */
 public class DboPowersTest extends BaseJDBCTestCase
 {
+    /* internal state */
+    final private int _authLevel;
+    final private String _dbo;
+    final private String _dboPassword;
+
     /* test execution security context: one of three below */
-    final private int authLevel; 
     final private static int NOAUTHENTICATION=0;
     final private static int AUTHENTICATION=1;
     final private static int SQLAUTHORIZATION=2;
-    
+
+    final private static String[] secLevelNames = {
+        "noAuthentication",
+        "authentication",
+        "authentication + sqlAuthorization"};
+
+    /**
+     * Create a new instance of DboPowersTest (for shutdown test)
+     *
+     * @param name Fixture name
+     * @param authLevel authentication level with which test is run
+     */
+    public DboPowersTest(String name, int authLevel)
+    {
+        super(name);
+        this._authLevel = authLevel;
+        this._dbo = null;
+        this._dboPassword = null;
+    }
+
     /**
-     * Create a new instance of DboPowersTest
+     * Create a new instance of DboPowersTest (for encryption tests)
      *
      * @param name Fixture name
      * @param authLevel authentication level with which test is run
+     * @param dbo Database owner
+     * @param dboPassword Database owner's password
      */
-    public DboPowersTest(String name, int authLevel) 
-    { 
-        super(name); 
-        this.authLevel = authLevel;
+
+    public DboPowersTest(String name, int authLevel,
+                         String dbo, String dboPassword)
+    {
+        super(name);
+        this._authLevel = authLevel;
+        this._dbo = dbo;
+        this._dboPassword = dboPassword;
     }
 
+
     /**
      * Construct top level suite in this JUnit test
      *
@@ -72,67 +102,73 @@
     public static Test suite()
     {
         TestSuite suite = new TestSuite("DboPowersTest");
-        suite.addTest(dboSuite("embedded"));
-        suite.addTest(TestConfiguration.clientServerDecorator(
-                          dboSuite("client")));
+
+        suite.addTest(dboShutdownSuite("suite: shutdown powers, embedded"));
+        suite.addTest(
+            TestConfiguration.clientServerDecorator(
+                dboShutdownSuite("suite: shutdown powers, client")));
+
+        suite.addTest(dboEncryptionSuite("suite: encryption powers, embedded"));
+        suite.addTest(
+            TestConfiguration.clientServerDecorator(
+                dboEncryptionSuite("suite: encryption powers, client")));
+
         return suite;
     }
-        
+
+    /**
+     * Users used by both dboShutdownSuite and dboEncryptionSuite
+     */
+    final static String[][] users = {
+        /* authLevel == AUTHENTICATION: dbo is APP/APP for db 'wombat',
+         * so use that as first user.  Otherwise,
+         * builtinAuthentication decorator's db shutdown fails to
+         * work after DERBY-2264(!).
+         */
+        {"APP", "U1"},
+        /* authLevel == SQLAUTHORIZATION: sqlAuthorizationDecorator
+         * decorator presumes TEST_DBO as dbo, so add it to set of
+         * valid users. Uses a fresh db 'dbsqlauth', not 'wombat'.
+         */
+        {"TEST_DBO", "U1"}};
+
+    final static String pwSuffix = "pwSuffix";
+
+
     /**
      *
-     * Construct default suite of tests
+     * Construct suite of tests for shutdown database action
      *
-     * @param framework Derby framework
-     * @return A suite containing the test cases incarnated for the three
-     * security levels no authentication, authentication, and
-     * authentication plus sqlAuthorization, 
-     * The latter two has an instance for dbo, and one for ordinary user,
-     * in all five incarnations of tests.
-     */
-    private static Test dboSuite(String framework) 
-    {
-        final String[][] users = {
-            /* authLevel == AUTHENTICATION: dbo is APP/APP for db 'wombat',
-             * so use that as first user.  Otherwise,
-             * builtinAuthentication decorator's db shutdown fails to
-             * work after DERBY-2264(!).
-             */
-            {"APP", "U1"}, 
-            /* authLevel == SQLAUTHORIZATION: sqlAuthorizationDecorator
-             * decorator presumes TEST_DBO as dbo, so add it to set of
-             * valid users. Uses a fresh db 'dbsqlauth', not 'wombat'.
-             */
-            {"TEST_DBO", "U1"}};
-        
-        final String pwSuffix = "pwSuffix";
-
+     * @param framework Derby framework name
+     * @return A suite containing the test case for shutdown
+     * incarnated for the three security levels no authentication,
+     * authentication, and authentication plus sqlAuthorization, The
+     * latter two has an instance for dbo, and one for ordinary user,
+     * so there are in all five incarnations of tests.
+     */
+    private static Test dboShutdownSuite(String framework)
+    {
         Test tests[] = new Test[SQLAUTHORIZATION+1]; // one per authLevel
 
-        tests[NOAUTHENTICATION] = collectFixtures(NOAUTHENTICATION);
+        /* Tests without any authorization active (level ==
+         * NOAUTHENTICATION).
+         */
+        TestSuite noauthSuite =
+            new TestSuite("suite: security level=" +
+                          secLevelNames[NOAUTHENTICATION]);
+        noauthSuite.addTest(new DboPowersTest("testShutDown",
+                                                    NOAUTHENTICATION));;
+        tests[NOAUTHENTICATION] = noauthSuite;
 
         /* First decorate with users, then with authentication. Do this
          * twice, once for authentication only, and once for
-         * authentication and sqlAuthorization (see extra decorator
+         * authentication + sqlAuthorization (see extra decorator
          * added below).
          */
-        for (int autLev = AUTHENTICATION; 
+        for (int autLev = AUTHENTICATION;
              autLev <= SQLAUTHORIZATION ; autLev++) {
 
-            // add decorator for different users authenticated
-            TestSuite userSuite =  new TestSuite(
-                "userSuite:"+ (autLev == AUTHENTICATION ? "authentication"
-                               : "sqlAuthorization"));
-
-            for (int userNo = 0; userNo < users.length; userNo++) {
-                userSuite.addTest
-                    (TestConfiguration.changeUserDecorator
-                     (collectFixtures(autLev),
-                      users[autLev-1][userNo], 
-                      users[autLev-1][userNo].concat(pwSuffix)));
-            }
-        
-            tests[autLev] = DatabasePropertyTestSetup.
-                builtinAuthentication(userSuite, users[autLev-1], pwSuffix);
+            tests[autLev] = wrapShutdownUserTests(autLev);
         }
 
         TestSuite suite = new TestSuite("dboPowers:"+framework);
@@ -149,23 +185,39 @@
         suite.addTest(
             TestConfiguration.
             sqlAuthorizationDecorator(tests[SQLAUTHORIZATION]));
-                         
+
         return suite;
     }
 
+
     /**
-     * Pick up individual test fixtures explicitly, since we need to
-     * provide the context.
+     * Wraps the shutdown fixture in decorators to run with data
+     * base owner and other valid user.
      *
-     * @param authLevel tests to be run with this security level
+     * @param autLev security context to use
      */
-    private static TestSuite collectFixtures(int authLevel)
+
+    private static Test wrapShutdownUserTests(int autLev)
     {
-        TestSuite suite = new TestSuite("dboPowersTests");
-        suite.addTest(new DboPowersTest("testShutDown", authLevel));
-        return suite;
+        // add decorator for different users authenticated
+        TestSuite usersSuite =
+            new TestSuite("usersSuite: security level=" +
+                          secLevelNames[autLev]);
+
+        // First decorate with users, then with
+        for (int userNo = 0; userNo < users.length; userNo++) {
+            usersSuite.addTest
+                (TestConfiguration.changeUserDecorator
+                 (new DboPowersTest("testShutDown", autLev),
+                  users[autLev-1][userNo],
+                  users[autLev-1][userNo].concat(pwSuffix)));
+        }
+
+        return DatabasePropertyTestSetup.
+            builtinAuthentication(usersSuite, users[autLev-1], pwSuffix);
     }
 
+
     /**
      * Test database shutdown power enforcement
      *
@@ -173,6 +225,9 @@
      */
     public void testShutDown() throws SQLException
     {
+        println("testShutDown: auth=" + this._authLevel +
+                " user="+getTestConfiguration().getUserName());
+
         // make sure db is booted
         getConnection().close();
 
@@ -202,22 +257,22 @@
      * Decide if the result of trying to shut down the database is
      * compliant with the semantics introduced by DERBY-2264.
      *
-     * @throws SQLException
      */
     private void vetShutdownException (String user, SQLException e)
     {
-        switch (authLevel) {
+        switch (_authLevel) {
         case NOAUTHENTICATION:
-            assertSQLState("database shutdown, no authentication", 
+            assertSQLState("database shutdown, no authentication",
                            "08006", e);
             break;
         case AUTHENTICATION:
             if ("APP".equals(user)) {
-                assertSQLState("database shutdown, authentication, db owner", 
+                assertSQLState("database shutdown, authentication, db owner",
                                "08006", e);
             } else {
-                assertSQLState("database shutdown restriction, authentication," +
-                               " not db owner", "2850H", e);
+                assertSQLState("database shutdown restriction, " +
+                               "authentication,  not db owner",
+                               "2850H", e);
             }
             break;
         case SQLAUTHORIZATION:
@@ -225,13 +280,304 @@
                 assertSQLState("database shutdown, SQL authorization, db owner",
                                "08006", e);
             } else {
-                assertSQLState("database shutdown restriction, " + 
+                assertSQLState("database shutdown restriction, " +
                                "SQL authorization, not db owner",
                                "2850H", e);
             }
             break;
         default:
-            fail("test error");
+            fail("test error: invalid authLevel: " + _authLevel);
+            break;
+        }
+    }
+
+    /**
+     *
+     * Construct suite of tests for database encryption action
+     *
+     * @param framework Derby framework name
+     * @return A suite containing the test case for encryption
+     * incarnated for the three security levels no authentication,
+     * authentication, and authentication plus sqlAuthorization, The
+     * latter two has an instance for dbo, and one for ordinary user,
+     * so there are in all five incarnations of tests.
+     */
+    private static Test dboEncryptionSuite(String framework)
+    {
+        Test tests[] = new Test[SQLAUTHORIZATION+1]; // one per authLevel
+
+        /* Tests without any authorization active (level ==
+         * NOAUTHENTICATION).  Note use of no shutdown decorator
+         * variants: Necessary since framework doesn't know
+         * bootPassword.
+         */
+        TestSuite noauthSuite =
+            new TestSuite("suite: security level=" +
+                          secLevelNames[NOAUTHENTICATION]);
+
+        for (int tNo = 0; tNo < encryptionTests.length; tNo++) {
+            noauthSuite.addTest(
+                TestConfiguration.singleUseDatabaseDecoratorNoShutdown(
+                    new DboPowersTest(encryptionTests[tNo], NOAUTHENTICATION,
+                                      "foo", "bar")));
+        }
+
+        tests[NOAUTHENTICATION] = noauthSuite;
+
+        /* Tests with authentication and sql authorization
+         */
+        for (int autLev = AUTHENTICATION;
+             autLev <= SQLAUTHORIZATION ; autLev++) {
+
+            tests[autLev] = wrapEncryptionUserTests(autLev);
+        }
+
+        TestSuite suite = new TestSuite("dboPowers:"+framework);
+
+        /* run tests with no authentication enabled */
+        suite.addTest(tests[NOAUTHENTICATION]);
+
+        /* run test for all users with only authentication enabled */
+        suite.addTest(tests[AUTHENTICATION]);
+
+        /* run test for all users with authentication and
+         * sqlAuthorization enabled
+         */
+        suite.addTest(tests[SQLAUTHORIZATION]);
+
+        return suite;
+    }
+
+    /**
+     * Wraps the encryption fixtures in decorators to run with data
+     * base owner and other valid user.
+     *
+     * @param autLev security context to use
+     */
+
+    private static Test wrapEncryptionUserTests(int autLev)
+    {
+        // add decorator for different users authenticated
+        TestSuite usersSuite =
+            new TestSuite("usersSuite: security level=" +
+                          secLevelNames[autLev]);
+
+        // First decorate with users, then with authentication.  Note
+        // use of no teardown / no shutdown decorator variants:
+        // Necessary since framework doesnt know bootPassword
+        for (int userNo = 0; userNo < users.length; userNo++) {
+            for (int tNo = 0; tNo < encryptionTests.length; tNo++) {
+                Test test = TestConfiguration.changeUserDecorator
+                    (new DboPowersTest(encryptionTests[tNo],
+                                       autLev,
+                                       users[autLev-1][0], // dbo
+                                       users[autLev-1][0].concat(pwSuffix)),
+                     users[autLev-1][userNo],
+                     users[autLev-1][userNo].concat(pwSuffix));
+                test = DatabasePropertyTestSetup.builtinAuthenticationNoTeardown
+                    (test, users[autLev-1], pwSuffix);
+                if (autLev == AUTHENTICATION) {
+                    test = TestConfiguration.
+                        singleUseDatabaseDecoratorNoShutdown(test);
+                } else {
+                    test = TestConfiguration.
+                        sqlAuthorizationDecoratorSingleUse(test);
+                }
+                usersSuite.addTest(test);
+            }
+        }
+        return usersSuite;
+    }
+
+    /**
+     * Enumerates the encryption tests
+     */
+    final static String[] encryptionTests = { "testEncrypt", "testReEncrypt" };
+
+    /**
+     * Test database encryption for an already created
+     * database. Note: The test needs to shut down the database for
+     * the single use decorators to work.
+     *
+     * @throws SQLException
+     */
+    public void testEncrypt() throws SQLException
+    {
+        println("testEncrypt: auth=" + this._authLevel +
+                " user="+getTestConfiguration().getUserName());
+
+        // make sure db is created
+        getConnection().close();
+
+        // shut down database in preparation for encryption
+        bringDbDown();
+
+        // make encryption attempt
+        String user = getTestConfiguration().getUserName();
+        String password = getTestConfiguration().getUserPassword();
+        String bootPassword="12345678";
+        DataSource ds = JDBCDataSource.getDataSource();
+
+        JDBCDataSource.setBeanProperty(ds, "connectionAttributes",
+                                       "dataEncryption=true;bootPassword=" +
+                                           bootPassword);
+        JDBCDataSource.setBeanProperty(ds, "user", user);
+        JDBCDataSource.setBeanProperty(ds, "password", password);
+
+        try {
+            ds.getConnection();
+            vetEncryptionAttempt(user, null);
+        } catch (SQLException e) {
+            vetEncryptionAttempt(user, e);
+            bringDbDown();
+            return;
+        }
+
+        // we managed to encrypt: bring db down and up again to verify
+        bringDbDown();
+        bringDbUp(bootPassword);
+        bringDbDown();
+    }
+
+
+    /**
+     * Test database re-encryption for an already encrypted
+     * database. Note: The test needs to shut down database for the
+     * single use decorators to work.
+     *
+     * @throws SQLException
+     */
+    public void testReEncrypt() throws SQLException
+    {
+        println("testReEncrypt: auth=" + this._authLevel +
+                " user="+getTestConfiguration().getUserName());
+
+        // make sure db is created
+        getConnection().close();
+
+        // shut down database in preparation for encryption
+        bringDbDown();
+
+        String bootPassword="12345678";
+        doEncrypt(bootPassword);
+        bringDbDown();
+
+        // make re-encryption attempt
+        String user = getTestConfiguration().getUserName();
+        String password = getTestConfiguration().getUserPassword();
+        String newBootPassword="87654321";
+        DataSource ds = JDBCDataSource.getDataSource();
+
+        JDBCDataSource.setBeanProperty(ds, "connectionAttributes",
+                                       "bootPassword=" + bootPassword +
+                                       ";newBootPassword=" + newBootPassword);
+        JDBCDataSource.setBeanProperty(ds, "user", user);
+        JDBCDataSource.setBeanProperty(ds, "password", password);
+
+        try {
+            ds.getConnection();
+            vetEncryptionAttempt(user, null);
+        } catch (SQLException e) {
+            vetEncryptionAttempt(user, e);
+            bringDbDown();
+            return;
+        }
+
+        // we managed to encrypt: bring db down and up again to verify
+        bringDbDown();
+        bringDbUp(newBootPassword);
+        bringDbDown();
+    }
+
+
+    /**
+     * Encrypt database, as owner (not testing encryption power here)
+     * @param bootPassword
+     * @throws SQLException
+     */
+    private void doEncrypt(String bootPassword) throws SQLException
+    {
+        DataSource ds = JDBCDataSource.getDataSource();
+        JDBCDataSource.setBeanProperty(ds, "connectionAttributes",
+                                       "dataEncryption=true;bootPassword=" +
+                                       bootPassword);
+        JDBCDataSource.setBeanProperty(ds, "user", _dbo);
+        JDBCDataSource.setBeanProperty(ds, "password", _dboPassword);
+        ds.getConnection();
+    }
+
+
+    /**
+     * Shut down database, as db owner (not testing that power here)
+     */
+    private void bringDbDown()
+    {
+        DataSource ds = JDBCDataSource.getDataSource();
+        JDBCDataSource.setBeanProperty(
+            ds, "connectionAttributes", "shutdown=true");
+        JDBCDataSource.setBeanProperty(ds, "user", _dbo);
+        JDBCDataSource.setBeanProperty(ds, "password", _dboPassword);
+        try {
+            ds.getConnection();
+            fail("shutdown failed: expected exception");
+        } catch (SQLException e) {
+            assertSQLState("database shutdown", "08006", e);
+        }
+    }
+
+
+    /**
+     * Boot database back up after encryption using current user,
+     * should succeed
+     *
+     * @param bootPassword Boot using this bootPassword
+     * @throws SQLException
+     */
+    private void bringDbUp(String bootPassword) throws SQLException
+    {
+        String user = getTestConfiguration().getUserName();
+        String password = getTestConfiguration().getUserPassword();
+        DataSource ds = JDBCDataSource.getDataSource();
+        JDBCDataSource.setBeanProperty(
+            ds, "connectionAttributes", "bootPassword=" + bootPassword);
+        JDBCDataSource.setBeanProperty(ds, "user", user);
+        JDBCDataSource.setBeanProperty(ds, "password", password);
+        ds.getConnection().close();
+    }
+
+    /**
+     * Decide if the result of trying to (re)encrypt the database is
+     * compliant with the semantics introduced by DERBY-2264.
+     *
+     * @param user The db user under which we tried to encrypt
+     * @param e    Exception caught during attempt, if any
+     */
+    private void vetEncryptionAttempt (String user, SQLException e)
+    {
+        switch (_authLevel) {
+        case NOAUTHENTICATION:
+            assertEquals("encryption, no authentication", null, e);
+            break;
+        case AUTHENTICATION:
+            if ("APP".equals(user)) {
+                assertEquals("encryption, authentication, db owner", null, e);
+            } else {
+                assertSQLState("database encryption restriction, " +
+                               "authentication, not db owner", "2850I", e);
+            }
+            break;
+        case SQLAUTHORIZATION:
+            if ("TEST_DBO".equals(user)) {
+                assertEquals("encryption, SQL authorization, db owner",
+                             null, e);
+            } else {
+                assertSQLState("encryption restriction, " +
+                               "SQL authorization, not db owner",
+                               "2850I", e);
+            }
+            break;
+        default:
+            fail("test error: invalid authLevel: " + _authLevel);
             break;
         }
     }

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabasePropertyTestSetup.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabasePropertyTestSetup.java?view=diff&rev=518214&r1=518213&r2=518214
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabasePropertyTestSetup.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DatabasePropertyTestSetup.java Wed Mar 14 09:49:35 2007
@@ -132,6 +132,60 @@
     }
     
     /**
+     * Decorate a test so that the database has authentication enabled
+     * using the BUILTIN provider and the set of users passed in.
+     * The password for each user is set to the user's name with
+     * the value of passwordToken appended.
+     * <BR>
+     * The decorated test can use BaseJDBCTestCase.openUserConnection(String user)
+     * method to simplify using authentication.
+     * <P>
+     * Assumption is that no authentication was enabled upon entry.
+     * <P>
+     * Current user is set to the first user in the list users[0].
+     * <P>
+     * In contrast to plain builtinAuthentication, here the
+     * authentication nor users are *NOT* removed by the decorator's
+     * tearDown method.
+     * @param test Test to be decorated.
+     * @param users Set of users for authentication.
+     * @return Decorated test.
+     */
+    public static Test builtinAuthenticationNoTeardown(Test test, String[] users,
+            String passwordToken)
+    {
+        final Properties userProps = new Properties();
+        final Properties authProps = new Properties();
+
+        authProps.setProperty("derby.connection.requireAuthentication", "true");
+        authProps.setProperty("derby.authentication.provider", "BUILTIN");
+
+        for (int i = 0; i < users.length; i++)
+        {
+            String user = users[i];
+            userProps.setProperty("derby.user." + user,
+                    TestConfiguration.getPassword(user, passwordToken));
+        }
+
+        test = getNoTeardownInstance(test, authProps, true);
+        test = new ChangeUserSetup(test, users[0],
+                TestConfiguration.getPassword(users[0], passwordToken),
+                passwordToken);
+        test = getNoTeardownInstance(test, userProps, false);
+        return test;
+    }
+
+    private static DatabasePropertyTestSetup getNoTeardownInstance(
+        Test test, Properties p, boolean staticp)
+    {
+        return new DatabasePropertyTestSetup(test, p, staticp) {
+                protected void tearDown()
+                        throws java.lang.Exception {
+                }
+            };
+    }
+
+    /**
      * Decorate a test so that it sets a single database property
      * at setUp and resets it at tearDown. Shorthand for
      * using DatabasePropertyTestSetup when only a single property is needed.

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java?view=diff&rev=518214&r1=518213&r2=518214
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/DropDatabaseSetup.java Wed Mar 14 09:49:35 2007
@@ -37,7 +37,7 @@
  */
 class DropDatabaseSetup extends TestSetup {
 
-    private final String logicalDBName;
+    final String logicalDBName;
     DropDatabaseSetup(Test test, String logicalDBName) {
         super(test);
         this.logicalDBName = logicalDBName;
@@ -56,18 +56,25 @@
         String dbName = config.getPhysicalDatabaseName(logicalDBName);
         DataSource ds = JDBCDataSource.getDataSource(dbName);
         JDBCDataSource.shutdownDatabase(ds);
-             
+
+        removeDatabase();
+    }
+
+    void removeDatabase()
+    {
+        TestConfiguration config = TestConfiguration.getCurrent();
+        String dbName = config.getPhysicalDatabaseName(logicalDBName);
         dbName = dbName.replace('/', File.separatorChar);
-        
         String dsh = BaseTestCase.getSystemProperty("derby.system.home");
-        if (dsh == null)
+        if (dsh == null) {
             fail("not implemented");
-        else
+        } else {
             dbName = dsh + File.separator + dbName;
-        
+        }
         removeDirectory(dbName);
-    } 
-    
+    }
+
+
     static void removeDirectory(String path)
     {
         final File dir = new File(path);

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java?view=diff&rev=518214&r1=518213&r2=518214
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/TestConfiguration.java Wed Mar 14 09:49:35 2007
@@ -314,7 +314,37 @@
 
         return new DatabaseChangeSetup(new DropDatabaseSetup(test, dbName), dbName, dbName, true);
     }
-    
+
+    /**
+     * Decorate a test to use a new database that is created upon the
+     * first connection request to the database and deleted at
+     * tearDown. In contrast to plain singleUseDatabaseDecorator, the
+     * database is expected to be shutdown by the test.  The
+     * configuration differs only from the current configuration by
+     * the list of used databases. The new database name is generated
+     * automatically as 'singleUse/oneuseXX' where 'XX' is the unique
+     * number. The generated database name is added at the end of
+     * <code>usedDbNames</code> and assigned as a default database
+     * name.  This decorator expects the database file to be local so
+     * it can be removed.
+     * @param test Test to be decorated
+     * @return decorated test.
+     */
+    public static TestSetup singleUseDatabaseDecoratorNoShutdown(Test test)
+    {
+        String dbName = generateUniqueDatabaseName();
+
+        return new DatabaseChangeSetup(
+            new DropDatabaseSetup(test, dbName)
+            {
+                protected void tearDown() throws Exception {
+                    // test responsible for shutdown
+                    removeDatabase();
+                }
+            },
+            dbName, dbName, true);
+    }
+
     /**
      * Decorate a test to use a new database that is created upon the
      * first connection request to the database and shutdown & deleted at
@@ -404,7 +434,45 @@
             new DatabaseChangeSetup(setSQLAuthMode, DEFAULT_DBNAME_SQL, DEFAULT_DBNAME_SQL, true),
             "TEST_DBO", "dummy"); // DRDA doesn't like empty pw
     }
+
+
+    /**
+     * Same as sqlAuthorizationDecorator, except that the database is dropped
+     * at teardown and the test is responsible for shutting down the database.
+     *
+     * @param test Test to be decorated
+     * @return decorated test.
+     *
+     * @see TestConfiguration#sqlAuthorizationDecorator(Test test)
+     */
+    public static Test sqlAuthorizationDecoratorSingleUse(Test test)
+    {
+        // Set the SQL authorization mode as a database property
+        // with a modified DatabasePropertyTestSetup that does not
+        // reset it.
+        final Properties sqlAuth = new Properties();
+        sqlAuth.setProperty("derby.database.sqlAuthorization", "true");
+        Test setSQLAuthMode = new DatabasePropertyTestSetup(test,
+                                                            sqlAuth, true) {
+                protected void tearDown() { }
+            };
+
+
+        setSQLAuthMode = new DatabaseChangeSetup(
+            new DropDatabaseSetup(setSQLAuthMode, DEFAULT_DBNAME_SQL) {
+                protected void tearDown() throws Exception {
+                    // test responsible for shutdown
+                    removeDatabase();
+                }
+            },
+            DEFAULT_DBNAME_SQL, DEFAULT_DBNAME_SQL, true);
+
+        return changeUserDecorator(setSQLAuthMode,
+                                   "TEST_DBO",
+                                   "dummy"); // DRDA doesn't like empty pw
+    }
     
+
     /**
      * Utility version of sqlAuthorizationDecorator that also sets
      * up authentication. A combination of