From bebd0fa0e6cc3966c4fb02d5a91f68ca20fed600 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 24 Jul 2015 11:23:46 +0200 Subject: [PATCH] DATAMONGO-1257 - element now supports usernames with a comma. We now allow grouping credentials by enclosing them in single quotes like this: credentials='CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry?uri.authMechanism=MONGODB-X509' We also changed the required argument checks to be more authentication mechanism specific which means the pattern is now username[:password@database][?options]. Original pull request: #310. --- .../config/MongoCredentialPropertyEditor.java | 82 +++++++++++++++++-- ...ongoCredentialPropertyEditorUnitTests.java | 79 ++++++++++++++++++ 2 files changed, 154 insertions(+), 7 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java index 85d576cc7..4e5021585 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditor.java @@ -17,8 +17,11 @@ package org.springframework.data.mongodb.config; import java.beans.PropertyEditorSupport; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.springframework.util.StringUtils; @@ -32,6 +35,8 @@ import com.mongodb.MongoCredential; */ public class MongoCredentialPropertyEditor extends PropertyEditorSupport { + private static final Pattern GROUP_PATTERN = Pattern.compile("(\\\\?')(.*?)\\1"); + private static final String AUTH_MECHANISM_KEY = "uri.authMechanism"; private static final String USERNAME_PASSWORD_DELIMINATOR = ":"; private static final String DATABASE_DELIMINATOR = "@"; @@ -51,11 +56,7 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport { List credentials = new ArrayList(); - for (String credentialString : text.split(",")) { - - if (!text.contains(USERNAME_PASSWORD_DELIMINATOR) || !text.contains(DATABASE_DELIMINATOR)) { - throw new IllegalArgumentException("Credentials need to be in format 'username:password@database'!"); - } + for (String credentialString : extractCredentialsString(text)) { String[] userNameAndPassword = extractUserNameAndPassword(credentialString); String database = extractDB(credentialString); @@ -68,16 +69,29 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport { String authMechanism = options.getProperty(AUTH_MECHANISM_KEY); if (MongoCredential.GSSAPI_MECHANISM.equals(authMechanism)) { + + verifyUserNamePresent(userNameAndPassword); credentials.add(MongoCredential.createGSSAPICredential(userNameAndPassword[0])); } else if (MongoCredential.MONGODB_CR_MECHANISM.equals(authMechanism)) { + + verifyUsernameAndPasswordPresent(userNameAndPassword); + verifyDatabasePresent(database); credentials.add(MongoCredential.createMongoCRCredential(userNameAndPassword[0], database, userNameAndPassword[1].toCharArray())); } else if (MongoCredential.MONGODB_X509_MECHANISM.equals(authMechanism)) { + + verifyUserNamePresent(userNameAndPassword); credentials.add(MongoCredential.createMongoX509Credential(userNameAndPassword[0])); } else if (MongoCredential.PLAIN_MECHANISM.equals(authMechanism)) { + + verifyUsernameAndPasswordPresent(userNameAndPassword); + verifyDatabasePresent(database); credentials.add(MongoCredential.createPlainCredential(userNameAndPassword[0], database, userNameAndPassword[1].toCharArray())); } else if (MongoCredential.SCRAM_SHA_1_MECHANISM.equals(authMechanism)) { + + verifyUsernameAndPasswordPresent(userNameAndPassword); + verifyDatabasePresent(database); credentials.add(MongoCredential.createScramSha1Credential(userNameAndPassword[0], database, userNameAndPassword[1].toCharArray())); } else { @@ -86,6 +100,9 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport { } } } else { + + verifyUsernameAndPasswordPresent(userNameAndPassword); + verifyDatabasePresent(database); credentials.add(MongoCredential.createCredential(userNameAndPassword[0], database, userNameAndPassword[1].toCharArray())); } @@ -94,10 +111,34 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport { setValue(credentials); } + private List extractCredentialsString(String source) { + + Matcher matcher = GROUP_PATTERN.matcher(source); + + List list = new ArrayList(); + while (matcher.find()) { + + String value = StringUtils.trimLeadingCharacter(matcher.group(), '\''); + list.add(StringUtils.trimTrailingCharacter(value, '\'')); + } + + if (!list.isEmpty()) { + return list; + } + return Arrays.asList(source.split(",")); + } + private static String[] extractUserNameAndPassword(String text) { - int dbSeperationIndex = text.lastIndexOf(DATABASE_DELIMINATOR); - String userNameAndPassword = text.substring(0, dbSeperationIndex); + int index = text.lastIndexOf(DATABASE_DELIMINATOR); + + if (index == -1) { + index = text.lastIndexOf(OPTIONS_DELIMINATOR); + } + if (index == -1) { + return new String[] {}; + } + String userNameAndPassword = text.substring(0, index); return userNameAndPassword.split(USERNAME_PASSWORD_DELIMINATOR); } @@ -105,6 +146,10 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport { int dbSeperationIndex = text.lastIndexOf(DATABASE_DELIMINATOR); + if (dbSeperationIndex == -1) { + return ""; + } + String tmp = text.substring(dbSeperationIndex + 1); int optionsSeperationIndex = tmp.lastIndexOf(OPTIONS_DELIMINATOR); @@ -129,4 +174,27 @@ public class MongoCredentialPropertyEditor extends PropertyEditorSupport { return properties; } + + private void verifyUserNamePresent(String[] source) { + + if (source.length == 0 || !StringUtils.hasText(source[0])) { + throw new IllegalArgumentException("Credentials need to specify username!"); + } + } + + private void verifyUsernameAndPasswordPresent(String[] source) { + + verifyUserNamePresent(source); + if (source.length != 2) { + throw new IllegalArgumentException( + "Credentials need to specify username and password like in 'username:password@database'!"); + } + } + + private void verifyDatabasePresent(String source) { + + if (!StringUtils.hasText(source)) { + throw new IllegalArgumentException("Credentials need to specify database like in 'username:password@database'!"); + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java index ededa7045..ba9257cbe 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/config/MongoCredentialPropertyEditorUnitTests.java @@ -43,6 +43,9 @@ public class MongoCredentialPropertyEditorUnitTests { static final String USER_2_PWD = "warg"; static final String USER_2_DB = "snow"; + static final String USER_3_NAME = "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry"; + static final String USER_3_DB = "stark"; + static final String USER_1_AUTH_STRING = USER_1_NAME + ":" + USER_1_PWD + "@" + USER_1_DB; static final String USER_1_AUTH_STRING_WITH_PLAIN_AUTH_MECHANISM = USER_1_AUTH_STRING + "?uri.authMechanism=PLAIN"; @@ -50,6 +53,9 @@ public class MongoCredentialPropertyEditorUnitTests { static final String USER_2_AUTH_STRING_WITH_MONGODB_CR_AUTH_MECHANISM = USER_2_AUTH_STRING + "?uri.authMechanism=MONGODB-CR"; + static final String USER_3_AUTH_STRING_WITH_X509_AUTH_MECHANISM = "'" + USER_3_NAME + "@" + USER_3_DB + + "?uri.authMechanism=MONGODB-X509'"; + static final MongoCredential USER_1_CREDENTIALS = MongoCredential.createCredential(USER_1_NAME, USER_1_DB, USER_1_PWD.toCharArray()); static final MongoCredential USER_1_CREDENTIALS_PLAIN_AUTH = MongoCredential.createPlainCredential(USER_1_NAME, @@ -60,6 +66,8 @@ public class MongoCredentialPropertyEditorUnitTests { static final MongoCredential USER_2_CREDENTIALS_CR_AUTH = MongoCredential.createMongoCRCredential(USER_2_NAME, USER_2_DB, USER_2_PWD.toCharArray()); + static final MongoCredential USER_3_CREDENTIALS_X509_AUTH = MongoCredential.createMongoX509Credential(USER_3_NAME); + MongoCredentialPropertyEditor editor; @Before @@ -168,4 +176,75 @@ public class MongoCredentialPropertyEditorUnitTests { assertThat((List) editor.getValue(), contains(USER_1_CREDENTIALS_PLAIN_AUTH, USER_2_CREDENTIALS)); } + + /** + * @see DATAMONGO-1257 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnCredentialsValueCorrectlyWhenGivenMultipleQuotedUserNamePasswordStringWithDatabaseAndNoOptions() { + + editor.setAsText(StringUtils.collectionToCommaDelimitedString(Arrays.asList("'" + USER_1_AUTH_STRING + "'", "'" + + USER_2_AUTH_STRING + "'"))); + + assertThat((List) editor.getValue(), contains(USER_1_CREDENTIALS, USER_2_CREDENTIALS)); + } + + /** + * @see DATAMONGO-1257 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnCredentialsValueCorrectlyWhenGivenSingleQuotedUserNamePasswordStringWithDatabaseAndNoOptions() { + + editor.setAsText("'" + USER_1_AUTH_STRING + "'"); + + assertThat((List) editor.getValue(), contains(USER_1_CREDENTIALS)); + } + + /** + * @see DATAMONGO-1257 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnX509CredentialsCorrectly() { + + editor.setAsText(USER_3_AUTH_STRING_WITH_X509_AUTH_MECHANISM); + + assertThat((List) editor.getValue(), contains(USER_3_CREDENTIALS_X509_AUTH)); + } + + /** + * @see DATAMONGO-1257 + */ + @Test + @SuppressWarnings("unchecked") + public void shouldReturnX509CredentialsCorrectlyWhenNoDbSpecified() { + + editor.setAsText("tyrion?uri.authMechanism=MONGODB-X509"); + + assertThat((List) editor.getValue(), contains(MongoCredential.createMongoX509Credential("tyrion"))); + } + + /** + * @see DATAMONGO-1257 + */ + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionWhenNoDbSpecifiedForMongodbCR() { + + editor.setAsText("tyrion?uri.authMechanism=MONGODB-CR"); + + editor.getValue(); + } + + /** + * @see DATAMONGO-1257 + */ + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionWhenDbIsEmptyForMongodbCR() { + + editor.setAsText("tyrion@?uri.authMechanism=MONGODB-CR"); + + editor.getValue(); + } }