blob: f769a747593cb51c9215feb3ec9b4cf698621e9a [file] [log] [blame]
/*
* Copyright (c) 1998, 2022 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.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;
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.
PoolDataSource 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
dataSource = null;
}
// create a data source using the supplied connection string
void createDataSource(String connectionString) {
try {
dataSource = PoolDataSourceFactory.getPoolDataSource();
dataSource.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
dataSource.setMinPoolSize(1);
dataSource.setMaxPoolSize(1);
dataSource.setInitialPoolSize(1);
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";
}
}
}