| /* |
| * Copyright (c) 1998, 2021 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 v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // 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 jakarta.persistence.EntityManager; |
| import jakarta.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 { |
| |
| private static final String PU_NAME = "proxyauthentication"; |
| |
| // 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 { |
| @Override |
| 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); |
| } |
| |
| @Override |
| public String getPersistenceUnitName() { |
| return PU_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; |
| } |
| |
| @Override |
| 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(PU_NAME).getPlatform().isOracle()) { |
| setupErrorMsg = "ProxyAuthentication test has not run, it runs only on Oracle and Oracle9Platform or higher required."; |
| } else if (! (getServerSession(PU_NAME).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(PU_NAME)); |
| } |
| } |
| 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(PU_NAME); |
| initializeFactoryProperties(); |
| } |
| } |
| |
| @Override |
| 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 = 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"; |
| } |
| } |
| } |