Skip to content

Commit baa1fb1

Browse files
authored
[329832604] Fix password-related NPEs in Oracle, provide more descriptive errors (#369)
Fix password-related NPEs in Oracle, provide more descriptive errors (#369) - introduce changes to how password prompts are handled by Dumper - handle cases where incomplete authorization is provided to Oracle connectors bug id: 329832604
1 parent e98b159 commit baa1fb1

File tree

12 files changed

+179
-50
lines changed

12 files changed

+179
-50
lines changed

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/ConnectorArguments.java

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
import com.google.edwmigration.dumper.application.dumper.connector.Connector;
3333
import com.google.edwmigration.dumper.application.dumper.connector.ConnectorProperty;
3434
import com.google.edwmigration.dumper.application.dumper.connector.ConnectorPropertyWithDefault;
35+
import com.google.edwmigration.dumper.application.dumper.io.PasswordReader;
3536
import com.google.edwmigration.dumper.plugin.ext.jdk.annotation.Description;
36-
import java.io.Console;
3737
import java.io.File;
3838
import java.io.IOException;
3939
import java.io.PrintStream;
@@ -412,9 +412,7 @@ public class ConnectorArguments extends DefaultArguments {
412412

413413
private ConnectorProperties connectorProperties;
414414

415-
// because of quoting of special characeters on command line... the -password is made optional,
416-
// and if not given, asked at the console.
417-
private String askedPassword;
415+
private final PasswordReader passwordReader = new PasswordReader();
418416

419417
public ConnectorArguments(@Nonnull String... args) throws IOException {
420418
super(args);
@@ -663,31 +661,36 @@ public String getUser() {
663661
return getOptions().valueOf(optionUser);
664662
}
665663

666-
// -password has optional argument, and if not provied
667-
// should be asked from command line
668-
@CheckForNull
669-
public String getPassword() {
670-
if (!getOptions().has(optionPass)) {
671-
return null;
672-
}
673-
String pass = getOptions().valueOf(optionPass);
674-
if (pass != null) {
675-
return pass;
676-
}
677-
// Else need to ask & save.
678-
if (askedPassword != null) {
679-
return askedPassword;
664+
@Nonnull
665+
public String getUserOrFail() {
666+
String user = getOptions().valueOf(optionUser);
667+
if (user == null) {
668+
throw new MetadataDumperUsageException(
669+
"Required username was not provided. Please use the '--"
670+
+ OPT_USER
671+
+ "' flag to provide the username.");
680672
}
673+
return user;
674+
}
681675

682-
Console console = System.console();
683-
if (console == null) {
684-
LOG.info("Cannot prompt for password, Console not available");
685-
return null;
676+
/**
677+
* Get a password depending on the --password flag.
678+
*
679+
* @return An empty optional if the --password flag is not provided. Otherwise, an optional
680+
* containing the result of getPasswordOrPrompt()
681+
*/
682+
@Nonnull
683+
public Optional<String> getPasswordIfFlagProvided() {
684+
return optionallyWhen(getOptions().has(optionPass), this::getPasswordOrPrompt);
685+
}
686+
687+
@Nonnull
688+
public String getPasswordOrPrompt() {
689+
String password = getOptions().valueOf(optionPass);
690+
if (password != null) {
691+
return password;
686692
} else {
687-
console.printf("Password: ");
688-
pass = new String(console.readPassword());
689-
askedPassword = pass;
690-
return askedPassword;
693+
return passwordReader.getOrPrompt();
691694
}
692695
}
693696

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/AbstractJdbcConnector.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ protected SimpleDriverDataSource newSimpleDataSource(
170170
if (!driver.acceptsURL(url))
171171
throw new MetadataDumperUsageException(
172172
"JDBC driver " + driver + " does not accept URL " + url);
173-
return new JdbcDataSource(driver, url, arguments.getUser(), arguments.getPassword());
173+
String password = arguments.getPasswordIfFlagProvided().orElse(null);
174+
return new JdbcDataSource(driver, url, arguments.getUser(), password);
174175
}
175176
}

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/mysql/AbstractMysqlConnector.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ public Handle open(ConnectorArguments arguments) throws Exception {
6666

6767
Driver driver =
6868
newDriver(arguments.getDriverPaths(), "com.mysql.jdbc.Driver", "org.mariadb.jdbc.Driver");
69-
DataSource dataSource =
70-
new SimpleDriverDataSource(driver, url, arguments.getUser(), arguments.getPassword());
69+
String password = arguments.getPasswordIfFlagProvided().orElse(null);
70+
DataSource dataSource = new SimpleDriverDataSource(driver, url, arguments.getUser(), password);
7171
return new JdbcHandle(dataSource);
7272
}
7373
}

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/netezza/NetezzaMetadataConnector.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,8 @@ public Handle open(ConnectorArguments arguments) throws Exception {
317317
}
318318

319319
Driver driver = newDriver(arguments.getDriverPaths(), "org.netezza.Driver");
320-
DataSource dataSource =
321-
new SimpleDriverDataSource(driver, url, arguments.getUser(), arguments.getPassword());
320+
String password = arguments.getPasswordIfFlagProvided().orElse(null);
321+
DataSource dataSource = new SimpleDriverDataSource(driver, url, arguments.getUser(), password);
322322
return new JdbcHandle(dataSource);
323323
}
324324

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/oracle/AbstractOracleConnector.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,18 @@ private boolean isOracleSid(ConnectorArguments arguments) throws MetadataDumperU
139139

140140
@Nonnull
141141
@Override
142-
public Handle open(ConnectorArguments arguments) throws Exception {
142+
public Handle open(@Nonnull ConnectorArguments arguments) throws Exception {
143143
Driver driver = newDriver(arguments.getDriverPaths(), "oracle.jdbc.OracleDriver");
144144
DataSource dataSource =
145145
new SimpleDriverDataSource(driver, buildUrl(arguments), buildProperties(arguments));
146146
return new JdbcHandle(dataSource);
147147
}
148148

149-
private Properties buildProperties(ConnectorArguments arguments) {
149+
@Nonnull
150+
Properties buildProperties(@Nonnull ConnectorArguments arguments) {
150151
Properties properties = new Properties();
151-
properties.setProperty("user", arguments.getUser());
152-
properties.setProperty("password", arguments.getPassword());
152+
properties.setProperty("user", arguments.getUserOrFail());
153+
properties.setProperty("password", arguments.getPasswordOrPrompt());
153154
properties.setProperty(
154155
"useFetchSizeWithLongColumn",
155156
PropertyParser.getString(

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/redshift/AbstractRedshiftConnector.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.time.ZoneOffset;
3535
import java.time.format.DateTimeFormatter;
3636
import java.util.List;
37+
import java.util.Optional;
3738
import javax.annotation.Nonnull;
3839
import javax.sql.DataSource;
3940
import org.slf4j.Logger;
@@ -139,6 +140,7 @@ private static String requireNonNull(String val, String msg) throws MetadataDump
139140
@Nonnull
140141
private String makeJdbcUrlPostgresql(ConnectorArguments arguments)
141142
throws MetadataDumperUsageException, UnsupportedEncodingException {
143+
String password = arguments.getPasswordIfFlagProvided().orElse(null);
142144
return "jdbc:postgresql://"
143145
+ requireNonNull(arguments.getHost(), "--host should be specified")
144146
+ ":"
@@ -147,14 +149,15 @@ private String makeJdbcUrlPostgresql(ConnectorArguments arguments)
147149
+ Iterables.getFirst(arguments.getDatabases(), "") //
148150
+ new JdbcPropBuilder("?=&")
149151
.propOrWarn("user", arguments.getUser(), "--user must be specified")
150-
.propOrWarn("password", arguments.getPassword(), "--password must be specified")
152+
.propOrWarn("password", password, "--password must be specified")
151153
.prop("ssl", "true")
152154
.toJdbcPart();
153155
}
154156

155157
@Nonnull
156158
private String makeJdbcUrlRedshiftSimple(ConnectorArguments arguments)
157159
throws MetadataDumperUsageException, UnsupportedEncodingException {
160+
String password = arguments.getPasswordIfFlagProvided().orElse(null);
158161
return "jdbc:redshift://"
159162
+ requireNonNull(arguments.getHost(), "--host should be specified")
160163
+ ":"
@@ -163,7 +166,7 @@ private String makeJdbcUrlRedshiftSimple(ConnectorArguments arguments)
163166
+ Iterables.getFirst(arguments.getDatabases(), "") //
164167
+ new JdbcPropBuilder("?=&")
165168
.propOrWarn("UID", arguments.getUser(), "--user must be specified")
166-
.propOrWarn("PWD", arguments.getPassword(), "--password must be specified")
169+
.propOrWarn("PWD", password, "--password must be specified")
167170
.toJdbcPart();
168171
}
169172

@@ -207,14 +210,15 @@ public Handle open(ConnectorArguments arguments) throws Exception {
207210
// LOG.debug("DRIVER CAN IAM " + driver.acceptsURL("jdbc:redshift:iam://host/db"));
208211
// LOG.debug("DRIVER CAN PG " + driver.acceptsURL("jdbc:postgresql://host/db"));
209212
String url = arguments.getUri();
213+
Optional<String> password = arguments.getPasswordIfFlagProvided();
210214
if (url == null) {
211215
// driver can be pg or rs ( including rs-iam )
212216
// options can be username/passowrd , or iam secrets.
213217

214218
boolean isDriverRedshift = driver.acceptsURL("jdbc:redshift:iam://host/db");
215219
// there may be no --iam options, in which case default ~/.aws/credentials to be used.
216220
// so this is the only reliable check
217-
boolean isAuthenticationPassword = arguments.getPassword() != null;
221+
boolean isAuthenticationPassword = password.isPresent();
218222

219223
if (!isDriverRedshift) {
220224
if (!isAuthenticationPassword)
@@ -235,7 +239,7 @@ public Handle open(ConnectorArguments arguments) throws Exception {
235239
// LogLevel 0/1
236240
LOG.trace("URI is " + url);
237241
DataSource dataSource =
238-
new SimpleDriverDataSource(driver, url, arguments.getUser(), arguments.getPassword());
242+
new SimpleDriverDataSource(driver, url, arguments.getUser(), password.orElse(null));
239243

240244
return JdbcHandle.newPooledJdbcHandle(dataSource, arguments.getThreadPoolSize());
241245
}

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/snowflake/AbstractSnowflakeConnector.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ public Handle open(@Nonnull ConnectorArguments arguments) throws Exception {
9696

9797
Driver driver =
9898
newDriver(arguments.getDriverPaths(), "net.snowflake.client.jdbc.SnowflakeDriver");
99-
DataSource dataSource =
100-
new SimpleDriverDataSource(driver, url, arguments.getUser(), arguments.getPassword());
99+
String password = arguments.getPasswordIfFlagProvided().orElse(null);
100+
DataSource dataSource = new SimpleDriverDataSource(driver, url, arguments.getUser(), password);
101101
JdbcHandle jdbcHandle = new JdbcHandle(dataSource);
102102
setCurrentDatabase(databaseName, jdbcHandle.getJdbcTemplate());
103103
return jdbcHandle;

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/sqlserver/SqlServerMetadataConnector.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ public Handle open(ConnectorArguments arguments) throws Exception {
117117
// LOG.info("Connecting to URL {}", url);
118118
Driver driver =
119119
newDriver(arguments.getDriverPaths(), "com.microsoft.sqlserver.jdbc.SQLServerDriver");
120-
DataSource dataSource =
121-
new SimpleDriverDataSource(driver, url, arguments.getUser(), arguments.getPassword());
120+
String password = arguments.getPasswordIfFlagProvided().orElse(null);
121+
DataSource dataSource = new SimpleDriverDataSource(driver, url, arguments.getUser(), password);
122122
return new JdbcHandle(dataSource);
123123
}
124124
}

dumper/app/src/main/java/com/google/edwmigration/dumper/application/dumper/connector/vertica/AbstractVerticaConnector.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ public Handle open(ConnectorArguments arguments) throws Exception {
6868
}
6969

7070
Driver driver = newDriver(arguments.getDriverPaths(), "com.vertica.jdbc.Driver");
71-
DataSource dataSource =
72-
new SimpleDriverDataSource(driver, url, arguments.getUser(), arguments.getPassword());
71+
String password = arguments.getPasswordIfFlagProvided().orElse(null);
72+
DataSource dataSource = new SimpleDriverDataSource(driver, url, arguments.getUser(), password);
7373
return new JdbcHandle(dataSource);
7474
}
7575
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2022-2023 Google LLC
3+
* Copyright 2013-2021 CompilerWorks
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package com.google.edwmigration.dumper.application.dumper.io;
18+
19+
import com.google.edwmigration.dumper.application.dumper.MetadataDumperUsageException;
20+
import java.io.Console;
21+
import javax.annotation.Nonnull;
22+
23+
/**
24+
* Provides the functionality of reading a password from prompt.
25+
*
26+
* <p>Needed, because users may not want to provide the password on the command line. This might be
27+
* for security reasons or due to issues caused by special characters.
28+
*/
29+
public class PasswordReader {
30+
31+
private boolean hasCachedValue = false;
32+
@Nonnull private String cachedValue = "";
33+
34+
@Nonnull
35+
public synchronized String getOrPrompt() {
36+
if (hasCachedValue) {
37+
return cachedValue;
38+
}
39+
Console console = System.console();
40+
if (console == null) {
41+
// user requested a prompt, but we can't even get a console, so let's just fail
42+
throw new MetadataDumperUsageException(
43+
"A password prompt was requested, but there is no console available.");
44+
}
45+
console.printf("Password: ");
46+
cachedValue = new String(console.readPassword());
47+
hasCachedValue = true;
48+
return cachedValue;
49+
}
50+
}

0 commit comments

Comments
 (0)