diff --git a/pymongo/common.py b/pymongo/common.py index 217bb3465..57560a7b0 100644 --- a/pymongo/common.py +++ b/pymongo/common.py @@ -454,8 +454,11 @@ def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, Uni return props value = validate_string(option, value) + value = unquote_plus(value) for opt in value.split(","): key, _, val = opt.partition(":") + if not val: + raise ValueError("Malformed auth mechanism properties") if key not in _MECHANISM_PROPS: # Try not to leak the token. if "AWS_SESSION_TOKEN" in key: @@ -473,7 +476,7 @@ def validate_auth_mechanism_properties(option: str, value: Any) -> dict[str, Uni if key == "CANONICALIZE_HOST_NAME": props[key] = validate_boolean_or_string(key, val) else: - props[key] = unquote_plus(val) + props[key] = val return props diff --git a/pymongo/uri_parser.py b/pymongo/uri_parser.py index 8e6e8298d..4ebd3008c 100644 --- a/pymongo/uri_parser.py +++ b/pymongo/uri_parser.py @@ -494,16 +494,11 @@ def parse_uri( collection = None options = _CaseInsensitiveDictionary() - host_part, _, path_part = scheme_free.partition("/") - if not host_part: - host_part = path_part - path_part = "" - - if path_part: - dbase, _, opts = path_part.partition("?") + host_plus_db_part, _, opts = scheme_free.partition("?") + if "/" in host_plus_db_part: + host_part, _, dbase = host_plus_db_part.partition("/") else: - # There was no slash in scheme_free, check for a sole "?". - host_part, _, opts = host_part.partition("?") + host_part = host_plus_db_part if dbase: dbase = unquote_plus(dbase) diff --git a/test/auth/legacy/connection-string.json b/test/auth/legacy/connection-string.json index f8fe0aeb5..57fd9d4a1 100644 --- a/test/auth/legacy/connection-string.json +++ b/test/auth/legacy/connection-string.json @@ -559,7 +559,7 @@ }, { "description": "should handle a complicated url-encoded TOKEN_RESOURCE (MONGODB-OIDC)", - "uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abc%2Cd%25ef%3Ag%26hi", + "uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:abcd%25ef%3Ag%26hi", "valid": true, "credential": { "username": "user", @@ -568,7 +568,7 @@ "mechanism": "MONGODB-OIDC", "mechanism_properties": { "ENVIRONMENT": "azure", - "TOKEN_RESOURCE": "abc,d%ef:g&hi" + "TOKEN_RESOURCE": "abcd%ef:g&hi" } } }, diff --git a/test/connection_string/test/valid-options.json b/test/connection_string/test/valid-options.json index 01bc2264b..3d174c5ab 100644 --- a/test/connection_string/test/valid-options.json +++ b/test/connection_string/test/valid-options.json @@ -37,6 +37,25 @@ "options": { "tls": true } + }, + { + "description": "Colon in a key value pair", + "uri": "mongodb://example.com?authMechanismProperties=TOKEN_RESOURCE:mongodb://test-cluster", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": { + "authmechanismProperties": { + "TOKEN_RESOURCE": "mongodb://test-cluster" + } + } } ] } diff --git a/test/connection_string/test/valid-warnings.json b/test/connection_string/test/valid-warnings.json index 1eacbf8fc..6bedbc6a6 100644 --- a/test/connection_string/test/valid-warnings.json +++ b/test/connection_string/test/valid-warnings.json @@ -93,6 +93,21 @@ ], "auth": null, "options": null + }, + { + "description": "Comma in a key value pair causes a warning", + "uri": "mongodb://example.com?authMechanismProperties=TOKEN_RESOURCE:mongodb://host1%2Chost2", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": null } ] } diff --git a/test/test_uri_parser.py b/test/test_uri_parser.py index a0283eb90..27f5fd2fb 100644 --- a/test/test_uri_parser.py +++ b/test/test_uri_parser.py @@ -474,9 +474,9 @@ class TestURI(unittest.TestCase): res = {"tls": True, "appname": "myapp"} self.assertEqual(res, parse_uri(uri)["options"]) - def test_unquote_after_parsing(self): - quoted_val = "val%21%40%23%24%25%5E%26%2A%28%29_%2B%2C%3A+etc" - unquoted_val = "val!@#$%^&*()_+,: etc" + def test_unquote_during_parsing(self): + quoted_val = "val%21%40%23%24%25%5E%26%2A%28%29_%2B%3A+etc" + unquoted_val = "val!@#$%^&*()_+: etc" uri = ( "mongodb://user:password@localhost/?authMechanism=MONGODB-AWS" "&authMechanismProperties=AWS_SESSION_TOKEN:" + quoted_val @@ -511,7 +511,7 @@ class TestURI(unittest.TestCase): ) with self.assertRaisesRegex( ValueError, - "auth mechanism properties must be key:value pairs like AWS_SESSION_TOKEN:", + "Malformed auth mechanism properties", ): parse_uri(uri)