| /* |
| * 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.proxyauthentication.thin; |
| |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.eclipse.persistence.config.PersistenceUnitProperties; |
| import org.eclipse.persistence.internal.sessions.ExclusiveIsolatedClientSession; |
| import org.eclipse.persistence.queries.DataModifyQuery; |
| import org.eclipse.persistence.queries.ValueReadQuery; |
| import org.eclipse.persistence.sessions.DatabaseSession; |
| import org.eclipse.persistence.sessions.Session; |
| import org.eclipse.persistence.sessions.SessionEvent; |
| import org.eclipse.persistence.sessions.SessionEventAdapter; |
| import org.eclipse.persistence.sessions.SessionEventListener; |
| import org.eclipse.persistence.sessions.server.ClientSession; |
| import org.eclipse.persistence.sessions.server.ConnectionPolicy; |
| import org.eclipse.persistence.sessions.server.ServerSession; |
| import org.eclipse.persistence.testing.framework.TestCase; |
| import org.eclipse.persistence.testing.framework.TestErrorException; |
| import org.eclipse.persistence.testing.framework.TestProblemException; |
| import org.eclipse.persistence.testing.framework.TestWarningException; |
| import org.eclipse.persistence.testing.framework.oracle.SessionExchanger; |
| |
| import oracle.jdbc.OracleConnection; |
| |
| // Test verifying that connectionUser and proxyUser are used as expected. |
| public class ProxyAuthenticationConnectionCustomizerTestCase extends TestCase { |
| // true -> DatabaseSession; false -> ServerSession. |
| boolean shouldUseDatabaseSession; |
| // indicates whether external connection pooling should be used. |
| boolean shouldUseExternalConnectionPooling; |
| |
| Map mainSessionProxyProperties; |
| String expectedMainSessionUser; |
| |
| // (valid on ServerSession only) indicates whether EclusiveIsolatedClientSession should be used. |
| boolean shoulUseExclusiveIsolatedSession; |
| |
| Map clientSessionProxyProperties; |
| String expectedClientSessionUser; |
| |
| // Substitutes the original session with the one required for the test; restores the original one after the test. |
| SessionExchanger exchanger = new SessionExchanger(); |
| |
| // Set to true to test for Bug 267880: JPA/ProxyAuthentication tests failed with "java.sql.SQLException: Closed Statement": |
| // the bug caused tests using internal connection pools to fail. |
| boolean shouldEnableStatementCaching = true; |
| |
| private String writeUser; |
| class Listener extends SessionEventAdapter { |
| @Override |
| public void outputParametersDetected(SessionEvent event) { |
| writeUser = (String)((Map)event.getResult()).get("OUT"); |
| } |
| } |
| SessionEventListener listener = new Listener(); |
| |
| static ValueReadQuery readQuery; |
| static DataModifyQuery writeQuery; |
| static { |
| // Used to return the schema name used by reading connection. |
| readQuery = new ValueReadQuery(); |
| readQuery.setSQLString("SELECT SYS_CONTEXT ('USERENV', 'CURRENT_SCHEMA') FROM DUAL"); |
| |
| // Used to return (through event) the schema name used by writing connection. |
| writeQuery = new DataModifyQuery(); |
| writeQuery.setSQLString("BEGIN SELECT SYS_CONTEXT ('USERENV', 'CURRENT_SCHEMA') INTO ###OUT FROM DUAL; END;"); |
| writeQuery.setIsUserDefined(true); |
| } |
| |
| String errorMsg = ""; |
| |
| public static ProxyAuthenticationConnectionCustomizerTestCase createDatabaseSessionTest(boolean shouldUseExternalConnectionPooling, Map databaseSessionProxyProperties) { |
| return new ProxyAuthenticationConnectionCustomizerTestCase(shouldUseExternalConnectionPooling, databaseSessionProxyProperties); |
| } |
| |
| public static ProxyAuthenticationConnectionCustomizerTestCase createServerSessionTest(boolean shouldUseExternalConnectionPooling, Map serverSessionProxyProperties, boolean shoulUseExclusiveIsolatedSession, Map clientSessionProxyProperties) { |
| return new ProxyAuthenticationConnectionCustomizerTestCase(shouldUseExternalConnectionPooling, serverSessionProxyProperties, shoulUseExclusiveIsolatedSession, clientSessionProxyProperties); |
| } |
| |
| protected ProxyAuthenticationConnectionCustomizerTestCase(boolean shouldUseExternalConnectionPooling, Map databaseSessionProxyProperties) { |
| this(true, shouldUseExternalConnectionPooling, databaseSessionProxyProperties, false, null); |
| } |
| |
| protected ProxyAuthenticationConnectionCustomizerTestCase(boolean shouldUseExternalConnectionPooling, Map serverSessionProxyProperties, boolean shoulUseExclusiveIsolatedSession, Map clientSessionProxyProperties) { |
| this(false, shouldUseExternalConnectionPooling, serverSessionProxyProperties, shoulUseExclusiveIsolatedSession, clientSessionProxyProperties); |
| } |
| |
| protected ProxyAuthenticationConnectionCustomizerTestCase(boolean shouldUseDatabaseSession, boolean shouldUseExternalConnectionPooling, Map mainSessionProxyProperties, boolean shoulUseExclusiveIsolatedSession, Map clientSessionProxyProperties) { |
| this.shouldUseDatabaseSession = shouldUseDatabaseSession; |
| this.shouldUseExternalConnectionPooling = shouldUseExternalConnectionPooling; |
| this.mainSessionProxyProperties = mainSessionProxyProperties; |
| // in case no proxy properties specified on the main session it uses connectionUser. |
| expectedMainSessionUser = ProxyAuthenticationUsersAndProperties.connectionUser; |
| if(mainSessionProxyProperties != null) { |
| expectedMainSessionUser = getExpectedUserName(mainSessionProxyProperties); |
| } |
| this.shoulUseExclusiveIsolatedSession = shoulUseExclusiveIsolatedSession; |
| if(!shouldUseDatabaseSession) { |
| this.clientSessionProxyProperties = clientSessionProxyProperties; |
| // in case no proxy properties specified on the ClientSession it uses the same use as the ServerSession. |
| expectedClientSessionUser = expectedMainSessionUser; |
| if(clientSessionProxyProperties != null) { |
| expectedClientSessionUser = getExpectedUserName(clientSessionProxyProperties); |
| } |
| } |
| |
| // generate the test name |
| String mainPropsStr = mainSessionProxyProperties != null ? " Proxy properties on" : "No proxy properties on"; |
| String sessionStr = shouldUseDatabaseSession ? " DatabaseSession" : " ServerSession"; |
| String poolingStr = shouldUseExternalConnectionPooling ? " usingExternalConnectionPooling" : ""; |
| if(shouldUseDatabaseSession) { |
| this.setName(mainPropsStr + sessionStr + poolingStr); |
| } else { |
| String clientPropsStr = "; no proxy properties on"; |
| if(clientSessionProxyProperties != null) { |
| if(mainSessionProxyProperties == null) { |
| clientPropsStr = "; proxy properties on"; |
| } else { |
| clientPropsStr = expectedClientSessionUser.equals(ProxyAuthenticationUsersAndProperties.connectionUser) ? "; canceled on" : "; overridden on"; |
| } |
| } |
| String clientSessionStr = shoulUseExclusiveIsolatedSession ? " ExclusiveIsolatedClientSession" : " ClientSession"; |
| this.setName(mainPropsStr + sessionStr + poolingStr + clientPropsStr + clientSessionStr); |
| } |
| } |
| |
| // 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. |
| static 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); |
| } |
| } |
| |
| @Override |
| public void setup() { |
| if (!getSession().getPlatform().isOracle()) { |
| throw new TestWarningException("Supports Oracle platform only"); |
| } |
| Properties loginProperties = ProxyAuthenticationUsersAndProperties.connectionProperties; |
| // In case of ServerSession with internal connection pools each pool will use just one connection. |
| getExecutor().setSession(exchanger.createNewSession((DatabaseSession)getSession(), shouldUseDatabaseSession, shouldUseExternalConnectionPooling, loginProperties, mainSessionProxyProperties, 1, 1, 1, 1)); |
| getSession().getEventManager().addListener(listener); |
| if(!shouldUseDatabaseSession) { |
| if(shoulUseExclusiveIsolatedSession) { |
| ((ServerSession)getSession()).getDefaultConnectionPolicy().setExclusiveMode(ConnectionPolicy.ExclusiveMode.Always); |
| |
| } |
| } |
| } |
| |
| String getReadUser(Session session) { |
| return (String)session.executeQuery(readQuery); |
| } |
| |
| String getWriteUser(Session session) { |
| session.executeQuery(writeQuery); |
| String user = writeUser; |
| writeUser = null; |
| return user; |
| } |
| |
| @Override |
| public void test() { |
| if(this.shouldUseDatabaseSession) { |
| testDatabaseSession(); |
| } else { |
| testServerSession(); |
| } |
| } |
| |
| void testDatabaseSession() { |
| // DatabaseSession was created with proxy properties - should always use proxyUser. |
| verifyUser("DatabaseSession read", getReadUser(getSession()), ProxyAuthenticationUsersAndProperties.proxyUser); |
| verifyUser("DatabaseSession wrote", getWriteUser(getSession()), ProxyAuthenticationUsersAndProperties.proxyUser); |
| } |
| |
| void testServerSession() { |
| ServerSession ss = (ServerSession)getSession(); |
| if(shouldEnableStatementCaching) { |
| ss.getPlatform().setShouldCacheAllStatements(true); |
| } |
| verifyUser("ServerSession first read", getReadUser(ss), expectedMainSessionUser); |
| |
| // The first ClientSession created without proxy properties - should always use the same user as ServerSession. |
| ClientSession cs1 = ss.acquireClientSession(); |
| if(shoulUseExclusiveIsolatedSession) { |
| // verify that IsolatedSession is used if required. |
| if(!(cs1 instanceof ExclusiveIsolatedClientSession)) { |
| throw new TestProblemException("The ClientSession must be ExclusiveIsolatedClientSession"); |
| } |
| } |
| verifyUser("ClientSession1 before transaction read", getReadUser(cs1), expectedMainSessionUser); |
| cs1.beginTransaction(); |
| verifyUser("ClientSession1in transaction read", getReadUser(cs1), expectedMainSessionUser); |
| verifyUser("ClientSession1 wrote", getWriteUser(cs1), expectedMainSessionUser); |
| cs1.rollbackTransaction(); |
| verifyUser("ClientSession1 after transaction read", getReadUser(cs1), expectedMainSessionUser); |
| cs1.release(); |
| |
| // The second ClientSession created with proxy properties. |
| ClientSession cs2 = ss.acquireClientSession(clientSessionProxyProperties); |
| if(shoulUseExclusiveIsolatedSession) { |
| verifyUser("ExclusiveIsolatedClientSession2 before transaction read", getReadUser(cs2), expectedClientSessionUser); |
| } else { |
| verifyUser("ClientSession2 before transaction read", getReadUser(cs2), expectedMainSessionUser); |
| } |
| cs2.beginTransaction(); |
| verifyUser("ClientSession2 in transaction read", getReadUser(cs2), expectedClientSessionUser); |
| verifyUser("ClientSession2 wrote", getWriteUser(cs2), expectedClientSessionUser); |
| cs2.rollbackTransaction(); |
| if(shoulUseExclusiveIsolatedSession) { |
| verifyUser("ExclusiveIsolatedClientSession2 after transaction read", getReadUser(cs2), expectedClientSessionUser); |
| } else { |
| verifyUser("ClientSession2 after transaction read", getReadUser(cs2), expectedMainSessionUser); |
| } |
| cs2.release(); |
| |
| // verify that ServerSession still uses the correct user. |
| // 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. |
| verifyUser("ServerSession second read", getReadUser(ss), expectedMainSessionUser); |
| |
| // 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. |
| ClientSession cs3 = ss.acquireClientSession(); |
| verifyUser("ClientSession3 before transaction read", getReadUser(cs3), expectedMainSessionUser); |
| cs3.beginTransaction(); |
| verifyUser("ClientSession3 in transaction read", getReadUser(cs3), expectedMainSessionUser); |
| verifyUser("ClientSession3 wrote", getWriteUser(cs3), expectedMainSessionUser); |
| cs3.rollbackTransaction(); |
| verifyUser("ClientSession3 after transaction read", getReadUser(cs3), expectedMainSessionUser); |
| cs3.release(); |
| |
| } |
| |
| void verifyUser(String msg, String user, String expectedUser) { |
| if(!user.equalsIgnoreCase(expectedUser)) { |
| errorMsg += msg + " through wrong user " + user + " - " + expectedUser + " was expected \n"; |
| } |
| } |
| |
| @Override |
| public void verify() { |
| if(errorMsg.length() > 0) { |
| throw new TestErrorException(errorMsg); |
| } |
| } |
| |
| @Override |
| public void reset() { |
| getSession().getEventManager().removeListener(listener); |
| getExecutor().setSession(exchanger.returnOriginalSession()); |
| errorMsg = ""; |
| } |
| } |