Skip to content

Commit 079a494

Browse files
committed
feat(oracle): add separate system password and withOraclePassword API; fix ORACLE_PASSWORD mapping
Introduce an independent system user password for Oracle containers and map it correctly to the ORACLE_PASSWORD env. - Add withOraclePassword(String) to configure SYSTEM/SYS password independently from the app user password - Use system password in SID connections (getPassword returns system password in SID mode) - Keep backward compatibility: withPassword also sets system password unless withOraclePassword was called - Update Oracle XE and Oracle Free containers to use the new internal oraclePassword - Add OracleContainerTest to verify system vs app password behavior in SID and non-SID modes This resolves the issue where ORACLE_PASSWORD was effectively bound to the app user password, making it impossible to set the system user password correctly.
1 parent 51ad2bc commit 079a494

File tree

4 files changed

+150
-8
lines changed

4 files changed

+150
-8
lines changed

modules/oracle-free/src/main/java/org/testcontainers/oracle/OracleContainer.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ public class OracleContainer extends JdbcDatabaseContainer<OracleContainer> {
5858

5959
private String password = APP_USER_PASSWORD;
6060

61+
/**
62+
* Password for Oracle system user (e.g. SYSTEM/SYS). Defaults to {@link #APP_USER_PASSWORD}
63+
* for backwards compatibility, but can be customized independently via {@link #withOraclePassword(String)}.
64+
*/
65+
private String oraclePassword = APP_USER_PASSWORD;
66+
67+
/**
68+
* Tracks whether {@link #withOraclePassword(String)} was called to avoid overriding
69+
* the system password when {@link #withPassword(String)} is used for the application user only.
70+
*/
71+
private boolean oraclePasswordExplicitlySet = false;
72+
6173
private boolean usingSid = false;
6274

6375
public OracleContainer(String dockerImageName) {
@@ -112,7 +124,8 @@ public String getUsername() {
112124

113125
@Override
114126
public String getPassword() {
115-
return password;
127+
// When connecting via SID we authenticate as SYSTEM. Use the dedicated system password.
128+
return isUsingSid() ? oraclePassword : password;
116129
}
117130

118131
@Override
@@ -142,6 +155,27 @@ public OracleContainer withPassword(String password) {
142155
throw new IllegalArgumentException("Password cannot be null or empty");
143156
}
144157
this.password = password;
158+
// Maintain backwards compatibility: if oracle password wasn't set explicitly,
159+
// align it with the application user's password.
160+
if (!oraclePasswordExplicitlySet) {
161+
this.oraclePassword = password;
162+
}
163+
return self();
164+
}
165+
166+
/**
167+
* Sets the password for the Oracle system user (SYSTEM/SYS). This is independent from the
168+
* application user password set via {@link #withPassword(String)}.
169+
*
170+
* @param oraclePassword password for SYSTEM/SYS users inside the container
171+
* @return this container instance
172+
*/
173+
public OracleContainer withOraclePassword(String oraclePassword) {
174+
if (StringUtils.isEmpty(oraclePassword)) {
175+
throw new IllegalArgumentException("Oracle password cannot be null or empty");
176+
}
177+
this.oraclePassword = oraclePassword;
178+
this.oraclePasswordExplicitlySet = true;
145179
return self();
146180
}
147181

@@ -185,7 +219,8 @@ public String getTestQueryString() {
185219

186220
@Override
187221
protected void configure() {
188-
withEnv("ORACLE_PASSWORD", password);
222+
// Configure system user password independently from application user's password
223+
withEnv("ORACLE_PASSWORD", oraclePassword);
189224

190225
// Only set ORACLE_DATABASE if different than the default.
191226
if (databaseName != DEFAULT_DATABASE_NAME) {

modules/oracle-free/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ private void runTest(OracleContainer container, String databaseName, String user
3131
assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1);
3232
}
3333

34+
private void runTestSystemUser(OracleContainer container, String databaseName, String username, String password)
35+
throws SQLException {
36+
//Test config was honored
37+
assertThat(container.getDatabaseName()).isEqualTo(databaseName);
38+
assertThat(container.getUsername()).isEqualTo(username);
39+
assertThat(container.getPassword()).isEqualTo(password);
40+
41+
//Test we can get a connection and execute a system-level command
42+
container.start();
43+
ResultSet resultSet = performQuery(container, "GRANT DBA TO " + username);
44+
int resultSetInt = resultSet.getInt(1);
45+
assertThat(resultSetInt).as("A basic system user query succeeds").isEqualTo(1);
46+
}
47+
3448
@Test
3549
public void testDefaultSettings() throws SQLException {
3650
try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME)) {
@@ -77,7 +91,7 @@ public void testCustomUser() throws SQLException {
7791
@Test
7892
public void testSID() throws SQLException {
7993
try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME).usingSid()) {
80-
runTest(oracle, "freepdb1", "system", "test");
94+
runTestSystemUser(oracle, "freepdb1", "system", "test");
8195

8296
// Match against the last ':'
8397
String urlSuffix = oracle.getJdbcUrl().split("(\\:)(?!.*\\:)", 2)[1];
@@ -92,7 +106,29 @@ public void testSIDAndCustomPassword() throws SQLException {
92106
.usingSid()
93107
.withPassword("testPassword")
94108
) {
95-
runTest(oracle, "freepdb1", "system", "testPassword");
109+
runTestSystemUser(oracle, "freepdb1", "system", "testPassword");
110+
}
111+
}
112+
113+
@Test
114+
public void testSeparateSystemAndAppPasswords() throws SQLException {
115+
// SID mode should use system password
116+
try (
117+
OracleContainer oracleSid = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME)
118+
.usingSid()
119+
.withOraclePassword("SysP@ss1!")
120+
.withPassword("AppP@ss1!")
121+
) {
122+
runTestSystemUser(oracleSid, "freepdb1", "system", "SysP@ss1!");
123+
}
124+
125+
// Non-SID mode should use application user's password
126+
try (
127+
OracleContainer oraclePdb = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME)
128+
.withOraclePassword("SysP@ss2!")
129+
.withPassword("AppP@ss2!")
130+
) {
131+
runTest(oraclePdb, "freepdb1", "test", "AppP@ss2!");
96132
}
97133
}
98134

modules/oracle-xe/src/main/java/org/testcontainers/containers/OracleContainer.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,18 @@ public class OracleContainer extends JdbcDatabaseContainer<OracleContainer> {
6161

6262
private String password = APP_USER_PASSWORD;
6363

64+
/**
65+
* Password for Oracle system user (e.g. SYSTEM/SYS). Defaults to {@link #APP_USER_PASSWORD}
66+
* for backwards compatibility, but can be customized independently via {@link #withOraclePassword(String)}.
67+
*/
68+
private String oraclePassword = APP_USER_PASSWORD;
69+
70+
/**
71+
* Tracks whether {@link #withOraclePassword(String)} was called to avoid overriding
72+
* the system password when {@link #withPassword(String)} is used for the application user only.
73+
*/
74+
private boolean oraclePasswordExplicitlySet = false;
75+
6476
private boolean usingSid = false;
6577

6678
public OracleContainer(String dockerImageName) {
@@ -125,7 +137,8 @@ public String getUsername() {
125137

126138
@Override
127139
public String getPassword() {
128-
return password;
140+
// When connecting via SID we authenticate as SYSTEM. Use the dedicated system password.
141+
return isUsingSid() ? oraclePassword : password;
129142
}
130143

131144
@Override
@@ -155,6 +168,27 @@ public OracleContainer withPassword(String password) {
155168
throw new IllegalArgumentException("Password cannot be null or empty");
156169
}
157170
this.password = password;
171+
// Maintain backwards compatibility: if oracle password wasn't set explicitly,
172+
// align it with the application user's password.
173+
if (!oraclePasswordExplicitlySet) {
174+
this.oraclePassword = password;
175+
}
176+
return self();
177+
}
178+
179+
/**
180+
* Sets the password for the Oracle system user (SYSTEM/SYS). This is independent from the
181+
* application user password set via {@link #withPassword(String)}.
182+
*
183+
* @param oraclePassword password for SYSTEM/SYS users inside the container
184+
* @return this container instance
185+
*/
186+
public OracleContainer withOraclePassword(String oraclePassword) {
187+
if (StringUtils.isEmpty(oraclePassword)) {
188+
throw new IllegalArgumentException("Oracle password cannot be null or empty");
189+
}
190+
this.oraclePassword = oraclePassword;
191+
this.oraclePasswordExplicitlySet = true;
158192
return self();
159193
}
160194

@@ -203,7 +237,8 @@ public String getTestQueryString() {
203237

204238
@Override
205239
protected void configure() {
206-
withEnv("ORACLE_PASSWORD", password);
240+
// Configure system user password independently from application user's password
241+
withEnv("ORACLE_PASSWORD", oraclePassword);
207242

208243
// Only set ORACLE_DATABASE if different than the default.
209244
if (databaseName != DEFAULT_DATABASE_NAME) {

modules/oracle-xe/src/test/java/org/testcontainers/junit/oracle/SimpleOracleTest.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ private void runTest(OracleContainer container, String databaseName, String user
3131
assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1);
3232
}
3333

34+
private void runTestSystemUser(OracleContainer container, String databaseName, String username, String password)
35+
throws SQLException {
36+
//Test config was honored
37+
assertThat(container.getDatabaseName()).isEqualTo(databaseName);
38+
assertThat(container.getUsername()).isEqualTo(username);
39+
assertThat(container.getPassword()).isEqualTo(password);
40+
41+
//Test we can get a connection and execute a system-level command
42+
container.start();
43+
ResultSet resultSet = performQuery(container, "GRANT DBA TO " + username);
44+
int resultSetInt = resultSet.getInt(1);
45+
assertThat(resultSetInt).as("A basic system user query succeeds").isEqualTo(1);
46+
}
47+
3448
@Test
3549
public void testDefaultSettings() throws SQLException {
3650
try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME);) {
@@ -77,7 +91,7 @@ public void testCustomUser() throws SQLException {
7791
@Test
7892
public void testSID() throws SQLException {
7993
try (OracleContainer oracle = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME).usingSid();) {
80-
runTest(oracle, "xepdb1", "system", "test");
94+
runTestSystemUser(oracle, "xepdb1", "system", "test");
8195

8296
// Match against the last ':'
8397
String urlSuffix = oracle.getJdbcUrl().split("(\\:)(?!.*\\:)", 2)[1];
@@ -92,7 +106,29 @@ public void testSIDAndCustomPassword() throws SQLException {
92106
.usingSid()
93107
.withPassword("testPassword");
94108
) {
95-
runTest(oracle, "xepdb1", "system", "testPassword");
109+
runTestSystemUser(oracle, "xepdb1", "system", "testPassword");
110+
}
111+
}
112+
113+
@Test
114+
public void testSeparateSystemAndAppPasswords() throws SQLException {
115+
// SID mode should use system password
116+
try (
117+
OracleContainer oracleSid = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME)
118+
.usingSid()
119+
.withOraclePassword("SysP@ss1!")
120+
.withPassword("AppP@ss1!")
121+
) {
122+
runTestSystemUser(oracleSid, "xepdb1", "system", "SysP@ss1!");
123+
}
124+
125+
// Non-SID mode should use application user's password
126+
try (
127+
OracleContainer oraclePdb = new OracleContainer(ORACLE_DOCKER_IMAGE_NAME)
128+
.withOraclePassword("SysP@ss2!")
129+
.withPassword("AppP@ss2!")
130+
) {
131+
runTest(oraclePdb, "xepdb1", "test", "AppP@ss2!");
96132
}
97133
}
98134

0 commit comments

Comments
 (0)