diff --git a/README.md b/README.md index 35a088d..7637624 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,11 @@ To connect using TNS, you need to have the ORACLE_HOME environment variable set. The file tnsnames.ora must exist in path %ORACLE_HOME%/network/admin The file tnsnames.ora must contain valid TNS entries. +In case you use a username containing `/` or a password containing `@` you should encapsulate it with double quotes `"`: +``` +utplsql run "my/Username"/"myP@ssword"@connectstring +``` + ### run `utplsql run []` @@ -227,6 +232,16 @@ UT_XUNIT_REPORTER: Provides outcomes in a format conforming with JUnit 4 and above as defined in: https://gist.github.com/kuzuha/232902acab1344d6b578 ``` +## Using utPLSQL-cli as sysdba + +Since 3.1.3 it is possible to run utPLSQL-cli as sysdba by running + +``` +utplsql run "sys as sysdba"/pw@connectstring +``` + +It is, however, __not recommended__ to run utPLSQL with sysdba privileges. + ## Enabling Color Outputs on Windows To enable color outputs on Windows cmd you need to install an open-source utility called [ANSICON](http://adoxa.altervista.org/ansicon/). diff --git a/src/main/java/org/utplsql/cli/ConnectionConfig.java b/src/main/java/org/utplsql/cli/ConnectionConfig.java index b98c6a5..e5d70de 100644 --- a/src/main/java/org/utplsql/cli/ConnectionConfig.java +++ b/src/main/java/org/utplsql/cli/ConnectionConfig.java @@ -10,16 +10,26 @@ public class ConnectionConfig { private final String connect; public ConnectionConfig( String connectString ) { - Matcher m = Pattern.compile("^([^/]+)/([^@]+)@(.*)$").matcher(connectString); + Matcher m = Pattern.compile("^(\".+\"|[^/]+)/(\".+\"|[^@]+)@(.*)$").matcher(connectString); if ( m.find() ) { - user = m.group(1); - password = m.group(2); + user = stripEnclosingQuotes(m.group(1)); + password = stripEnclosingQuotes(m.group(2)); connect = m.group(3); } else throw new IllegalArgumentException("Not a valid connectString: '" + connectString + "'"); } + private String stripEnclosingQuotes( String value ) { + if ( value.length() > 1 + && value.startsWith("\"") + && value.endsWith("\"")) { + return value.substring(1, value.length()-1); + } else { + return value; + } + } + public String getConnect() { return connect; } @@ -35,4 +45,10 @@ public String getPassword() { public String getConnectString() { return user + "/" + password + "@" + connect; } + + public boolean isSysDba() { + return user != null && + (user.toLowerCase().endsWith(" as sysdba") + || user.toLowerCase().endsWith(" as sysoper")); + } } diff --git a/src/main/java/org/utplsql/cli/DataSourceProvider.java b/src/main/java/org/utplsql/cli/DataSourceProvider.java index 7fee23e..c9eb9b6 100644 --- a/src/main/java/org/utplsql/cli/DataSourceProvider.java +++ b/src/main/java/org/utplsql/cli/DataSourceProvider.java @@ -26,6 +26,7 @@ public static DataSource getDataSource(ConnectionInfo info, int maxConnections ) requireOjdbc(); ConnectionConfig config = new ConnectionConfig(info.getConnectionString()); + warnIfSysDba(config); HikariDataSource pds = new TestedDataSourceProvider(config).getDataSource(); pds.setAutoCommit(false); @@ -43,4 +44,10 @@ private static void requireOjdbc() { throw new RuntimeException("Can't run utPLSQL-cli without Oracle JDBC driver"); } } + + private static void warnIfSysDba(ConnectionConfig config) { + if ( config.isSysDba() ) { + System.out.println("WARNING: You are connecting to the database as SYSDBA or SYSOPER, which is NOT RECOMMENDED and can put your database at risk!"); + } + } } diff --git a/src/main/java/org/utplsql/cli/RunCommand.java b/src/main/java/org/utplsql/cli/RunCommand.java index cbfca36..65ae15c 100644 --- a/src/main/java/org/utplsql/cli/RunCommand.java +++ b/src/main/java/org/utplsql/cli/RunCommand.java @@ -124,7 +124,7 @@ public class RunCommand implements ICommand { private ReporterFactory reporterFactory; private ReporterManager reporterManager; - private ConnectionInfo getConnectionInfo() { + ConnectionInfo getConnectionInfo() { return connectionInfoList.get(0); } diff --git a/src/test/java/org/utplsql/cli/ConnectionConfigTest.java b/src/test/java/org/utplsql/cli/ConnectionConfigTest.java new file mode 100644 index 0000000..ee1e0b9 --- /dev/null +++ b/src/test/java/org/utplsql/cli/ConnectionConfigTest.java @@ -0,0 +1,57 @@ +package org.utplsql.cli; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class ConnectionConfigTest { + + @Test + void parse() { + ConnectionConfig info = new ConnectionConfig("test/pw@my.local.host/service"); + + assertEquals("test", info.getUser()); + assertEquals("pw", info.getPassword()); + assertEquals("my.local.host/service", info.getConnect()); + assertFalse(info.isSysDba()); + } + + @Test + void parseSysDba() { + ConnectionConfig info = new ConnectionConfig("sys as sysdba/pw@my.local.host/service"); + + assertEquals("sys as sysdba", info.getUser()); + assertEquals("pw", info.getPassword()); + assertEquals("my.local.host/service", info.getConnect()); + assertTrue(info.isSysDba()); + } + @Test + void parseSysOper() { + ConnectionConfig info = new ConnectionConfig("myOperUser as sysoper/passw0rd@my.local.host/service"); + + assertEquals("myOperUser as sysoper", info.getUser()); + assertEquals("passw0rd", info.getPassword()); + assertEquals("my.local.host/service", info.getConnect()); + assertTrue(info.isSysDba()); + } + + @Test + void parseSpecialCharsPW() { + ConnectionConfig info = new ConnectionConfig("test/\"p@ssw0rd=\"@my.local.host/service"); + + assertEquals("test", info.getUser()); + assertEquals("p@ssw0rd=", info.getPassword()); + assertEquals("my.local.host/service", info.getConnect()); + assertFalse(info.isSysDba()); + } + + @Test + void parseSpecialCharsUser() { + ConnectionConfig info = new ConnectionConfig("\"User/Mine@=\"/pw@my.local.host/service"); + + assertEquals("User/Mine@=", info.getUser()); + assertEquals("pw", info.getPassword()); + assertEquals("my.local.host/service", info.getConnect()); + assertFalse(info.isSysDba()); + } +} diff --git a/src/test/java/org/utplsql/cli/DataSourceProviderIT.java b/src/test/java/org/utplsql/cli/DataSourceProviderIT.java index cfff041..0bef99a 100644 --- a/src/test/java/org/utplsql/cli/DataSourceProviderIT.java +++ b/src/test/java/org/utplsql/cli/DataSourceProviderIT.java @@ -20,6 +20,14 @@ void connectToDatabase() throws SQLException { assertNotNull(dataSource); } + //@Test + void connectAsSysdba() throws SQLException { + ConnectionConfig config = new ConnectionConfig("sys as sysdba/oracle@localhost:1522/ORCLPDB1"); + DataSource dataSource = new TestedDataSourceProvider(config).getDataSource(); + + assertNotNull(dataSource); + } + @Test void initNlsLang() throws SQLException { System.setProperty("NLS_LANG", "BRAZILIAN PORTUGUESE_BRAZIL.WE8ISO8859P1"); diff --git a/src/test/java/org/utplsql/cli/RunCommandIT.java b/src/test/java/org/utplsql/cli/RunCommandIT.java index a6373fb..ae14b5a 100644 --- a/src/test/java/org/utplsql/cli/RunCommandIT.java +++ b/src/test/java/org/utplsql/cli/RunCommandIT.java @@ -5,6 +5,7 @@ import org.utplsql.api.reporter.CoreReporters; import java.nio.file.Paths; +import java.sql.SQLException; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -13,6 +14,14 @@ */ class RunCommandIT extends AbstractFileOutputTest { + private void assertValidReturnCode(int returnCode) throws SQLException { + // Only expect failure-exit-code to work on several framework versions + if (OptionalFeatures.FAIL_ON_ERROR.isAvailableFor(TestHelper.getFrameworkVersion())) + assertEquals(2, returnCode); + else + assertEquals(0, returnCode); + } + @Test void run_Default() throws Exception { @@ -23,11 +32,7 @@ void run_Default() throws Exception { "-c", "--failure-exit-code=2"); - // Only expect failure-exit-code to work on several framework versions - if (OptionalFeatures.FAIL_ON_ERROR.isAvailableFor(TestHelper.getFrameworkVersion())) - assertEquals(2, result); - else - assertEquals(0, result); + assertValidReturnCode(result); } @Test @@ -35,9 +40,10 @@ void run_Debug() throws Exception { int result = TestHelper.runApp("run", TestHelper.getConnectionString(), - "--debug"); + "--debug", + "--failure-exit-code=2"); - assertEquals(1, result); + assertValidReturnCode(result); } @Test @@ -55,11 +61,7 @@ void run_MultipleReporters() throws Exception { "-c", "--failure-exit-code=2"); - // Only expect failure-exit-code to work on several framework versions - if (OptionalFeatures.FAIL_ON_ERROR.isAvailableFor(TestHelper.getFrameworkVersion())) - assertEquals(2, result); - else - assertEquals(0, result); + assertValidReturnCode(result); } diff --git a/src/test/java/org/utplsql/cli/RunCommandTest.java b/src/test/java/org/utplsql/cli/RunCommandTest.java index 84eccdc..941a0e1 100644 --- a/src/test/java/org/utplsql/cli/RunCommandTest.java +++ b/src/test/java/org/utplsql/cli/RunCommandTest.java @@ -85,4 +85,11 @@ void reporterOptions_TwoReporters() { assertTrue(reporterOptions2.outputToScreen()); } + @Test + void connectionString_asSysdba() { + RunCommand runCmd = TestHelper.createRunCommand("sys as sysdba/mypass@connectstring/service"); + + assertEquals("sys as sysdba/mypass@connectstring/service", + runCmd.getConnectionInfo().getConnectionString()); + } } diff --git a/src/test/java/org/utplsql/cli/TestHelper.java b/src/test/java/org/utplsql/cli/TestHelper.java index ca1abc9..77375bd 100644 --- a/src/test/java/org/utplsql/cli/TestHelper.java +++ b/src/test/java/org/utplsql/cli/TestHelper.java @@ -45,4 +45,16 @@ static Version getFrameworkVersion() throws SQLException { static String getConnectionString() { return sUser + "/" + sPass + "@" + sUrl; } + + static String getUser() { + return sUser; + } + + static String getPass() { + return sPass; + } + + static String getUrl() { + return sUrl; + } }