Skip to content

Commit

Permalink
fix: Make default DB compulsory in URI string format (#15881)
Browse files Browse the repository at this point in the history
* fix: Use admin as default database when no Mongo DB is specified

* fix: Make default db compulsary instead

* Added back check during execution per review

* Json field spec fix

Co-authored-by: Aishwarya UR <[email protected]>
  • Loading branch information
nidhi-nair and Aishwarya-U-R authored Aug 11, 2022
1 parent e255593 commit 10c9f77
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@
import static com.external.plugins.constants.FieldName.SUCCESS;
import static com.external.plugins.constants.FieldName.UPDATE_OPERATION;
import static com.external.plugins.constants.FieldName.UPDATE_QUERY;
import static com.external.plugins.utils.DatasourceUtils.KEY_PASSWORD;
import static com.external.plugins.utils.DatasourceUtils.KEY_URI_DEFAULT_DBNAME;
import static com.external.plugins.utils.DatasourceUtils.KEY_USERNAME;
import static com.external.plugins.utils.DatasourceUtils.buildClientURI;
import static com.external.plugins.utils.DatasourceUtils.buildURIFromExtractedInfo;
import static com.external.plugins.utils.DatasourceUtils.extractInfoFromConnectionStringURI;
Expand Down Expand Up @@ -157,24 +160,18 @@ public class MongoPlugin extends BasePlugin {
* We use this regex to find usage of special Mongo data types like ObjectId(...) wrapped inside double quotes
* e.g. "ObjectId(...)". Case for single quotes e.g. 'ObjectId(...)' is not added because the way client sends
* back the data to the API server it would be extremely uncommon to encounter this case.
*
* <p>
* In the given regex E is replaced by the name of special data types before doing the match / find operation.
* e.g. E will be replaced with ObjectId to find the occurrence of "ObjectId(...)" pattern.
*
* <p>
* Example: for "[\"ObjectId('xyz')\"]":
* o group 1 will match "ObjectId(...)"
* o group 2 will match ObjectId(...)
* o group 3 will match 'xyz'
* o group 4 will match xyz
* o group 1 will match "ObjectId(...)"
* o group 2 will match ObjectId(...)
* o group 3 will match 'xyz'
* o group 4 will match xyz
*/
private static final String MONGODB_SPECIAL_TYPE_INSIDE_QUOTES_REGEX_TEMPLATE = "(\\\"(E\\((.*?((\\w|-|:|\\.|,|\\s)+).*?)?\\))\\\")";

private static final String KEY_USERNAME = "username";

private static final String KEY_PASSWORD = "password";

private static final String KEY_URI_DBNAME = "dbName";

private static final int DATASOURCE_CONFIG_MONGO_URI_PROPERTY_INDEX = 1;

private static final Integer MONGO_COMMAND_EXCEPTION_UNAUTHORIZED_ERROR_CODE = 13;
Expand Down Expand Up @@ -640,7 +637,6 @@ public Mono<MongoClient> datasourceCreate(DatasourceConfiguration datasourceConf
}



@Override
public void datasourceDestroy(MongoClient mongoClient) {
if (mongoClient != null) {
Expand Down Expand Up @@ -668,18 +664,23 @@ public Set<String> validateDatasource(DatasourceConfiguration datasourceConfigur
if (extractedInfo == null) {
invalids.add("Mongo Connection String URI does not seem to be in the correct format. " +
"Please check the URI once.");
} else if (!isAuthenticated(authentication, mongoUri)) {
String mongoUriWithHiddenPassword = buildURIFromExtractedInfo(extractedInfo, "****");
properties.get(DATASOURCE_CONFIG_MONGO_URI_PROPERTY_INDEX).setValue(mongoUriWithHiddenPassword);
authentication = (authentication == null) ? new DBAuth() : authentication;
authentication.setUsername((String) extractedInfo.get(KEY_USERNAME));
authentication.setPassword((String) extractedInfo.get(KEY_PASSWORD));
authentication.setDatabaseName((String) extractedInfo.get(KEY_URI_DBNAME));
datasourceConfiguration.setAuthentication(authentication);

// remove any default db set via form auto-fill via browser
if (datasourceConfiguration.getConnection() != null) {
datasourceConfiguration.getConnection().setDefaultDatabaseName(null);
} else {
if (extractedInfo.get(KEY_URI_DEFAULT_DBNAME) == null) {
invalids.add("Missing default database name.");
}
if (!isAuthenticated(authentication, mongoUri)) {
String mongoUriWithHiddenPassword = buildURIFromExtractedInfo(extractedInfo, "****");
properties.get(DATASOURCE_CONFIG_MONGO_URI_PROPERTY_INDEX).setValue(mongoUriWithHiddenPassword);
authentication = (authentication == null) ? new DBAuth() : authentication;
authentication.setUsername((String) extractedInfo.get(KEY_USERNAME));
authentication.setPassword((String) extractedInfo.get(KEY_PASSWORD));
authentication.setDatabaseName((String) extractedInfo.get(KEY_URI_DEFAULT_DBNAME));
datasourceConfiguration.setAuthentication(authentication);

// remove any default db set via form auto-fill via browser
if (datasourceConfiguration.getConnection() != null) {
datasourceConfiguration.getConnection().setDefaultDatabaseName(null);
}
}
}
}
Expand Down Expand Up @@ -720,7 +721,7 @@ public Set<String> validateDatasource(DatasourceConfiguration datasourceConfigur
}

if (!StringUtils.hasLength(authentication.getDatabaseName())) {
invalids.add("Missing database name.");
invalids.add("Missing default database name.");
}

}
Expand Down Expand Up @@ -860,7 +861,7 @@ public Object substituteValueInInput(int index,
Object... args) {
String jsonBody = (String) input;
DataType dataType = stringToKnownMongoDBDataTypeConverter(value);
return DataTypeStringUtils.jsonSmartReplacementPlaceholderWithValue(jsonBody, value,dataType, insertedParams, this);
return DataTypeStringUtils.jsonSmartReplacementPlaceholderWithValue(jsonBody, value, dataType, insertedParams, this);
}

/**
Expand Down Expand Up @@ -904,6 +905,7 @@ public Mono<ActionExecutionResult> execute(MongoClient mongoClient,
* This method coverts Mongo plugin's form data to Mongo's native query. Currently, it is meant to help users
* switch easily from form based input to raw input mode by providing a readily available translation of the
* form data to raw query.
*
* @return Mongo's native/raw query set at path `formData.formToNativeQuery.data`
*/
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ public class DatasourceUtils {

private static final int REGEX_GROUP_TAIL = 6;

private static final String KEY_USERNAME = "username";
public static final String KEY_USERNAME = "username";

private static final String KEY_PASSWORD = "password";
public static final String KEY_PASSWORD = "password";

private static final String KEY_HOST_PORT = "hostPort";
public static final String KEY_HOST_PORT = "hostPort";

private static final String KEY_URI_HEAD = "uriHead";
public static final String KEY_URI_HEAD = "uriHead";

private static final String KEY_URI_TAIL = "uriTail";
public static final String KEY_URI_TAIL = "uriTail";

private static final String KEY_URI_DBNAME = "dbName";
public static final String KEY_URI_DEFAULT_DBNAME = "defaultDbName";

private static final String YES = "Yes";

Expand Down Expand Up @@ -94,7 +94,7 @@ public static Map extractInfoFromConnectionStringURI(String uri, String regex) {
extractedInfoMap.put(KEY_USERNAME, matcher.group(REGEX_GROUP_USERNAME));
extractedInfoMap.put(KEY_PASSWORD, matcher.group(REGEX_GROUP_PASSWORD));
extractedInfoMap.put(KEY_HOST_PORT, matcher.group(REGEX_HOST_PORT));
extractedInfoMap.put(KEY_URI_DBNAME, matcher.group(REGEX_GROUP_DBNAME));
extractedInfoMap.put(KEY_URI_DEFAULT_DBNAME, matcher.group(REGEX_GROUP_DBNAME));
extractedInfoMap.put(KEY_URI_TAIL, matcher.group(REGEX_GROUP_TAIL));
return extractedInfoMap;
}
Expand All @@ -113,7 +113,7 @@ public static String buildURIFromExtractedInfo(Map extractedInfo, String passwor
userInfo += "@";
}

final String dbInfo = "/" + (extractedInfo.get(KEY_URI_DBNAME) == null ? "" : extractedInfo.get(KEY_URI_DBNAME));
final String dbInfo = "/" + (extractedInfo.get(KEY_URI_DEFAULT_DBNAME) == null ? "" : extractedInfo.get(KEY_URI_DEFAULT_DBNAME));

String tailInfo = (String) (extractedInfo.get(KEY_URI_TAIL) == null ? "" : extractedInfo.get(KEY_URI_TAIL));
tailInfo = "?" + buildURITail(tailInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,23 @@ private static MongoCommand getMongoCommand(ActionConfiguration actionConfigurat
}

public static String getDatabaseName(DatasourceConfiguration datasourceConfiguration) {
String databaseName = null;

// Explicitly set default database.
String databaseName = datasourceConfiguration.getConnection().getDefaultDatabaseName();
if (datasourceConfiguration.getConnection() != null) {
databaseName = datasourceConfiguration.getConnection().getDefaultDatabaseName();
}

// If that's not available, pick the authentication database.
final DBAuth authentication = (DBAuth) datasourceConfiguration.getAuthentication();
if (StringUtils.isEmpty(databaseName) && authentication != null) {
if (!StringUtils.hasLength(databaseName) && authentication != null) {
databaseName = authentication.getDatabaseName();
}

if (databaseName == null) {
throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, "Missing default database name.");
}

return databaseName;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2835,4 +2835,18 @@ public void testStaleConnectionOnMongoSocketWriteExceptionOnGetStructure() {
.expectErrorMatches(throwable -> throwable instanceof StaleConnectionException)
.verify();
}

@Test
public void testValidateDatasource_withoutDefaultDBInURIString_returnsInvalid() {
final DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration();
List<Property> properties = new ArrayList<>();
properties.add(new Property("isUriString", "Yes"));
properties.add(new Property("uriString", "mongodb://user:[email protected]/"));
datasourceConfiguration.setProperties(properties);

final Set<String> strings = pluginExecutor.validateDatasource(datasourceConfiguration);

assertEquals(1, strings.size());
assertTrue(strings.contains("Missing default database name."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.external.plugins.utils;

import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
import com.appsmith.external.models.DatasourceConfiguration;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

public class MongoPluginUtilsTest {

@Test
public void testGetDatabaseName_withoutDatabaseName_throwsDatasourceError() {
final AppsmithPluginException exception = assertThrows(
AppsmithPluginException.class,
() -> MongoPluginUtils.getDatabaseName(new DatasourceConfiguration())
);

assertEquals("Missing default database name.", exception.getMessage());

}
}

0 comments on commit 10c9f77

Please sign in to comment.