/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * 05/28/2008-1.0M8 Andrei Ilitchev. * - New file introduced for bug 224964: Provide support for Proxy Authentication through JPA. ******************************************************************************/ package org.eclipse.persistence.testing.tests.jpa.proxyauthentication; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import junit.framework.Test; import junit.framework.TestSuite; import oracle.jdbc.OracleConnection; import oracle.jdbc.pool.OracleDataSource; import org.eclipse.persistence.config.PersistenceUnitProperties; import org.eclipse.persistence.config.ExclusiveConnectionMode; import org.eclipse.persistence.internal.sessions.ExclusiveIsolatedClientSession; import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; import org.eclipse.persistence.jpa.JpaEntityManagerFactory; import org.eclipse.persistence.platform.database.oracle.Oracle9Platform; import org.eclipse.persistence.sessions.SessionEvent; import org.eclipse.persistence.sessions.SessionEventAdapter; import org.eclipse.persistence.sessions.server.ConnectionPolicy; import org.eclipse.persistence.sessions.server.ServerSession; import org.eclipse.persistence.testing.framework.junit.JUnitTestCase; import org.eclipse.persistence.testing.framework.junit.JUnitTestCaseHelper; import org.eclipse.persistence.testing.tests.proxyauthentication.thin.ProxyAuthenticationUsersAndProperties; /** * TestSuite to verifying that connectionUser and proxyUser are used as expected. * To run this test suite several users should be setup in the Oracle database, * see comment in ProxyAuthenticationUsersAndProperties. */ public class ProxyAuthenticationTestSuite extends JUnitTestCase { // the map passed to creteEMFactory method. Map factoryProperties; // indicates whether external connection pooling should be used. boolean shouldUseExternalConnectionPooling; // indicates whether EclusiveIsolatedClientSession should be used. boolean shoulUseExclusiveIsolatedSession; // datasource created in external connection pooling case. OracleDataSource dataSource; // writeUser is set by an event risen by ModifyQuery. private static String writeUser; public static class Listener extends SessionEventAdapter { public void outputParametersDetected(SessionEvent event) { writeUser = (String)((Map)event.getResult()).get("OUT"); } } // custom sql strings used to query for read and write users. static String readQueryString; static String writeQueryString; static { // Used to return the schema name used by reading connection. readQueryString = "SELECT SYS_CONTEXT ('USERENV', 'CURRENT_SCHEMA') FROM DUAL"; // Used to return (through event) the schema name used by writing connection. writeQueryString = "BEGIN SELECT SYS_CONTEXT ('USERENV', 'CURRENT_SCHEMA') INTO ###OUT FROM DUAL; END;"; } // Contains a non-empty string in case of an error after the test is complete. String errorMsg = ""; // Before setup is attempted contains null; after that contains an empty string in case of success, error message otherwise. // If setup has failed then all the tests fail with this message. static String setupErrorMsg; public ProxyAuthenticationTestSuite() { super(); } public ProxyAuthenticationTestSuite(String name) { super(name); } public static Test suite() { TestSuite suite = new TestSuite("Proxy Authentication JPA Test Suite"); suite.addTest(new ProxyAuthenticationTestSuite("testInternalPool_EMFProxyProperties")); suite.addTest(new ProxyAuthenticationTestSuite("testInternalPool_EMFProxyProperties_ExclusiveIsolated")); suite.addTest(new ProxyAuthenticationTestSuite("testExternalPool_EMFProxyProperties")); suite.addTest(new ProxyAuthenticationTestSuite("testExternalPool_EMFProxyProperties_ExclusiveIsolated")); suite.addTest(new ProxyAuthenticationTestSuite("testInternalPool_EMProxyProperties")); suite.addTest(new ProxyAuthenticationTestSuite("testInternalPool_EMProxyProperties_ExclusiveIsolated")); suite.addTest(new ProxyAuthenticationTestSuite("testExternalPool_EMProxyProperties")); suite.addTest(new ProxyAuthenticationTestSuite("testExternalPool_EMProxyProperties_ExclusiveIsolated")); suite.addTest(new ProxyAuthenticationTestSuite("testInternalPool_EMFProxyProperties_EMProxyProperties")); suite.addTest(new ProxyAuthenticationTestSuite("testInternalPool_EMFProxyProperties_EMProxyProperties_ExclusiveIsolated")); suite.addTest(new ProxyAuthenticationTestSuite("testExternalPool_EMFProxyProperties_EMProxyProperties")); suite.addTest(new ProxyAuthenticationTestSuite("testExternalPool_EMFProxyProperties_EMProxyProperties_ExclusiveIsolated")); suite.addTest(new ProxyAuthenticationTestSuite("testInternalPool_EMFProxyProperties_EMCancelProxyProperties")); suite.addTest(new ProxyAuthenticationTestSuite("testInternalPool_EMFProxyProperties_EMCancelProxyProperties_ExclusiveIsolated")); suite.addTest(new ProxyAuthenticationTestSuite("testExternalPool_EMFProxyProperties_EMCancelProxyProperties")); suite.addTest(new ProxyAuthenticationTestSuite("testExternalPool_EMFProxyProperties_EMCancelProxyProperties_ExclusiveIsolated")); return suite; } public void setUp() { // runs for the first time - setup user names and properties used by the tests. if(setupErrorMsg == null) { if (isOnServer()) { setupErrorMsg = "ProxyAuthentication tests currently can't run on server."; } else if(!getServerSession().getPlatform().isOracle()) { setupErrorMsg = "ProxyAuthentication test has not run, it runs only on Oracle and Oracle9Platform or higher required."; } else if (! (getServerSession().getPlatform() instanceof Oracle9Platform)) { setupErrorMsg = "ProxyAuthentication test has not run, Oracle9Platform or higher required."; } else { // sets up all user names and properties used by the tests. ProxyAuthenticationUsersAndProperties.initialize(); // verifies that all the users correctly setup in the db. // returns empty string in case of success, error message otherwise. setupErrorMsg = ProxyAuthenticationUsersAndProperties.verify(getServerSession()); } } if(setupErrorMsg.length() > 0) { // setup has failed - can't run the test. fail("Setup Failure:\n" + setupErrorMsg); } else { // The test will re-create EMFactory using the necessary properties, close the original factory first. // This does nothing if the factory already closed. closeEntityManagerFactory(); initializeFactoryProperties(); } } public void tearDown() { // clean-up if(setupErrorMsg.length() > 0) { // setup failed, the test didn't run, nothing to clean up. return; } // reset errorMsg errorMsg = ""; // the test has customized the factory - it should be closed. closeEntityManagerFactory(); // close the data source if it has been created if(dataSource != null) { try { dataSource.close(); } catch (SQLException ex) { throw new RuntimeException("Exception thrown while closing OracleDataSource:\n", ex); } finally { dataSource = null; } } } // create a data source using the supplied connection string void createDataSource(String connectionString) { try { dataSource = new OracleDataSource(); Properties props = new Properties(); // the pool using just one connection would cause deadlock in case of a connection leak - good for the test. props.setProperty("MinLimit", "1"); props.setProperty("MaxLimit", "1"); props.setProperty("InitialLimit", "1"); dataSource.setConnectionCacheProperties(props); dataSource.setURL(connectionString); } catch (SQLException ex) { throw new RuntimeException("Failed to create OracleDataSource with " + connectionString + ".\n", ex); } } /** * */ public void testInternalPool_EMFProxyProperties() { internalTest(false, ProxyAuthenticationUsersAndProperties.proxyProperties, false, null); } public void testInternalPool_EMFProxyProperties_ExclusiveIsolated() { internalTest(false, ProxyAuthenticationUsersAndProperties.proxyProperties, true, null); } public void testExternalPool_EMFProxyProperties() { internalTest(true, ProxyAuthenticationUsersAndProperties.proxyProperties, false, null); } public void testExternalPool_EMFProxyProperties_ExclusiveIsolated() { internalTest(true, ProxyAuthenticationUsersAndProperties.proxyProperties, true, null); } /** * */ public void testInternalPool_EMProxyProperties() { internalTest(false, null, false, ProxyAuthenticationUsersAndProperties.proxyProperties); } public void testInternalPool_EMProxyProperties_ExclusiveIsolated() { internalTest(false, null, true, ProxyAuthenticationUsersAndProperties.proxyProperties); } public void testExternalPool_EMProxyProperties() { internalTest(true, null, false, ProxyAuthenticationUsersAndProperties.proxyProperties); } public void testExternalPool_EMProxyProperties_ExclusiveIsolated() { internalTest(true, null, true, ProxyAuthenticationUsersAndProperties.proxyProperties); } /** * */ public void testInternalPool_EMFProxyProperties_EMProxyProperties() { internalTest(false, ProxyAuthenticationUsersAndProperties.proxyProperties, false, ProxyAuthenticationUsersAndProperties.proxyProperties2); } public void testInternalPool_EMFProxyProperties_EMProxyProperties_ExclusiveIsolated() { internalTest(false, ProxyAuthenticationUsersAndProperties.proxyProperties, true, ProxyAuthenticationUsersAndProperties.proxyProperties2); } public void testExternalPool_EMFProxyProperties_EMProxyProperties() { internalTest(true, ProxyAuthenticationUsersAndProperties.proxyProperties, false, ProxyAuthenticationUsersAndProperties.proxyProperties2); } public void testExternalPool_EMFProxyProperties_EMProxyProperties_ExclusiveIsolated() { internalTest(true, ProxyAuthenticationUsersAndProperties.proxyProperties, true, ProxyAuthenticationUsersAndProperties.proxyProperties2); } /** * */ public void testInternalPool_EMFProxyProperties_EMCancelProxyProperties() { internalTest(false, ProxyAuthenticationUsersAndProperties.proxyProperties, false, ProxyAuthenticationUsersAndProperties.cancelProxyProperties); } public void testInternalPool_EMFProxyProperties_EMCancelProxyProperties_ExclusiveIsolated() { internalTest(false, ProxyAuthenticationUsersAndProperties.proxyProperties, true, ProxyAuthenticationUsersAndProperties.cancelProxyProperties); } public void testExternalPool_EMFProxyProperties_EMCancelProxyProperties() { internalTest(true, ProxyAuthenticationUsersAndProperties.proxyProperties, false, ProxyAuthenticationUsersAndProperties.cancelProxyProperties); } public void testExternalPool_EMFProxyProperties_EMCancelProxyProperties_ExclusiveIsolated() { internalTest(true, ProxyAuthenticationUsersAndProperties.proxyProperties, true, ProxyAuthenticationUsersAndProperties.cancelProxyProperties); } void initializeFactoryProperties() { // copy the original factory properties. factoryProperties = new HashMap(JUnitTestCaseHelper.getDatabaseProperties()); // these properties used only in internal connection pool case. // the pool using just one connection would cause deadlock in case of a connection leak - good for the test. factoryProperties.put(PersistenceUnitProperties.JDBC_WRITE_CONNECTIONS_MIN, "1"); factoryProperties.put(PersistenceUnitProperties.JDBC_WRITE_CONNECTIONS_MAX, "1"); factoryProperties.put(PersistenceUnitProperties.JDBC_READ_CONNECTIONS_MIN, "1"); factoryProperties.put(PersistenceUnitProperties.JDBC_READ_CONNECTIONS_MAX, "1"); // set them into factoryProperties. Note that this works in external connection pooling case, too (at least with OracleDataSource). factoryProperties.put(PersistenceUnitProperties.JDBC_USER, ProxyAuthenticationUsersAndProperties.connectionUser); factoryProperties.put(PersistenceUnitProperties.JDBC_PASSWORD, ProxyAuthenticationUsersAndProperties.connectionPassword); // add listener that handles output parameter event through which writeUser is obtained. factoryProperties.put(PersistenceUnitProperties.SESSION_EVENT_LISTENER_CLASS, Listener.class.getName()); } void internalTest(boolean shouldUseExternalConnectionPooling, Map serverSessionProxyProperties, boolean shoulUseExclusiveIsolatedSession, Map clientSessionProxyProperties) { if(shouldUseExternalConnectionPooling) { // create data source and add it to factoryProperties createDataSource((String)factoryProperties.get(PersistenceUnitProperties.JDBC_URL)); factoryProperties.put(PersistenceUnitProperties.NON_JTA_DATASOURCE, dataSource); } if(shoulUseExclusiveIsolatedSession) { factoryProperties.put(PersistenceUnitProperties.EXCLUSIVE_CONNECTION_MODE, ExclusiveConnectionMode.Isolated); } // EMFactory uses proxyProperties if(serverSessionProxyProperties != null) { factoryProperties.putAll(serverSessionProxyProperties); } EntityManagerFactory factory = this.getEntityManagerFactory(factoryProperties); ServerSession ss = ((JpaEntityManagerFactory)factory).getServerSession(); if(shoulUseExclusiveIsolatedSession) { ss.getDefaultConnectionPolicy().setExclusiveMode(ConnectionPolicy.ExclusiveMode.Always); } String expectedMainSessionUser = ProxyAuthenticationUsersAndProperties.connectionUser; if(serverSessionProxyProperties != null) { expectedMainSessionUser = getExpectedUserName(serverSessionProxyProperties); } // in case no proxy properties specified on the ClientSession it uses the same use as the ServerSession. String expectedClientSessionUser = expectedMainSessionUser; if(clientSessionProxyProperties != null) { expectedClientSessionUser = getExpectedUserName(clientSessionProxyProperties); } // The second ClientSession created without proxy properties. EntityManager em1 = factory.createEntityManager(); boolean isExclusiveIsolated = ((UnitOfWorkImpl)((org.eclipse.persistence.internal.jpa.EntityManagerImpl)em1).getActiveSession()).getParent() instanceof ExclusiveIsolatedClientSession; if(isExclusiveIsolated != shoulUseExclusiveIsolatedSession) { if(isExclusiveIsolated) { fail("EntityManager is using ExclusiveIsolatedClientSession, ClientSession was expected"); } else { fail("EntityManager is using ClientSession, ExclusiveIsolatedClientSession was expected"); } } verifyUser("ClientSession1 before transaction read", getReadUser(em1), expectedMainSessionUser); beginTransaction(em1); verifyUser("ClientSession1 in transaction read", getReadUser(em1), expectedMainSessionUser); verifyUser("ClientSession1 wrote", getWriteUser(em1), expectedMainSessionUser); rollbackTransaction(em1); verifyUser("ClientSession1 after transaction read", getReadUser(em1), expectedMainSessionUser); em1.close(); // The second ClientSession created with proxy properties. EntityManager em2 = factory.createEntityManager(clientSessionProxyProperties); if(shoulUseExclusiveIsolatedSession) { verifyUser("ExclusiveIsolatedClientSession2 before transaction read", getReadUser(em2), expectedClientSessionUser); } else { verifyUser("ClientSession2 before transaction read", getReadUser(em2), expectedMainSessionUser); } beginTransaction(em2); verifyUser("ClientSession2 in transaction read", getReadUser(em2), expectedClientSessionUser); verifyUser("ClientSession2 wrote", getWriteUser(em2), expectedClientSessionUser); rollbackTransaction(em2); if(shoulUseExclusiveIsolatedSession) { verifyUser("ExclusiveIsolatedClientSession2 after transaction read", getReadUser(em2), expectedClientSessionUser); } else { verifyUser("ClientSession2 after transaction read", getReadUser(em2), expectedMainSessionUser); } em2.close(); // The third ClientSession created without proxy properties again - should always use the same user as ServerSession. // Because there is one one connection in each connection pool (internal pool case) this would fail in case proxy customizer // wasn't removed by cs2. EntityManager em3 = factory.createEntityManager(); verifyUser("ClientSession3 before transaction read", getReadUser(em3), expectedMainSessionUser); beginTransaction(em3); verifyUser("ClientSession3 in transaction read", getReadUser(em3), expectedMainSessionUser); verifyUser("ClientSession3 wrote", getWriteUser(em3), expectedMainSessionUser); rollbackTransaction(em3); verifyUser("ClientSession3 after transaction read", getReadUser(em3), expectedMainSessionUser); em3.close(); if(errorMsg.length() > 0) { fail(errorMsg); } } String getReadUser(EntityManager em) { return (String)em.createNativeQuery(readQueryString).getSingleResult(); } String getWriteUser(EntityManager em) { em.createNativeQuery(writeQueryString).executeUpdate(); String user = writeUser; // make sure writeUser is not left for the next time writeQuery executed. writeUser = null; return user; } // If the session's proxyProperties PersistenceUnitProperties.ORACLE_PROXY_TYPE property has an empty string value // then the session should use connectionUser; // otherwise it should use proxyUser specified in the proxyProperties. String getExpectedUserName(Map proxyProperties) { Object proxytype = proxyProperties.get(PersistenceUnitProperties.ORACLE_PROXY_TYPE); boolean proxytypeIsAnEmptyString = proxytype instanceof String && ((String)proxytype).length()==0; if(proxytypeIsAnEmptyString) { // PersistenceUnitProperties.ORACLE_PROXY_TYPE -> "" means that proxyProperies are to be ignored and therefore connectionUser should be used. return ProxyAuthenticationUsersAndProperties.connectionUser; } else { return (String)proxyProperties.get(OracleConnection.PROXY_USER_NAME); } } void verifyUser(String msg, String user, String expectedUser) { if(!user.equalsIgnoreCase(expectedUser)) { errorMsg += msg + " through wrong user " + user + " - " + expectedUser + " was expected \n"; } } }