diff --git a/doc/examples/authentication.rst b/doc/examples/authentication.rst index 993d544b7..d58a45547 100644 --- a/doc/examples/authentication.rst +++ b/doc/examples/authentication.rst @@ -5,19 +5,22 @@ MongoDB supports several different authentication mechanisms. These examples cover all authentication methods currently supported by PyMongo, documenting Python module and MongoDB version dependencies. -Support For Special Characters In Usernames And Passwords ---------------------------------------------------------- +Percent-Escaping Username and Password +-------------------------------------- -If your username or password contains special characters (e.g. '/', ' ', -or '@') you must ``%xx`` escape them for use in the MongoDB URI. PyMongo -uses :meth:`~urllib.unquote_plus` to decode them. For example:: +Username and password must be percent-escaped with :meth:`~urllib.quote_plus` +to be used in a MongoDB URI. PyMongo uses :meth:`~urllib.unquote_plus` +internally to decode them. For example:: >>> from pymongo import MongoClient >>> import urllib + >>> username = urllib.quote_plus('user') + >>> username + 'user' >>> password = urllib.quote_plus('pass/word') >>> password 'pass%2Fword' - >>> MongoClient('mongodb://user:' + password + '@127.0.0.1') + >>> MongoClient('mongodb://%s:%s@127.0.0.1' % (username, password) MongoClient('127.0.0.1', 27017) SCRAM-SHA-1 (RFC 5802) diff --git a/pymongo/uri_parser.py b/pymongo/uri_parser.py index 96be1a1d0..708e6a6c5 100644 --- a/pymongo/uri_parser.py +++ b/pymongo/uri_parser.py @@ -14,6 +14,7 @@ """Tools to parse and validate a MongoDB URI.""" +import re import warnings from bson.py3compat import PY3, iteritems, string_type @@ -68,7 +69,7 @@ def _rpartition(entity, sep): def parse_userinfo(userinfo): """Validates the format of user information in a MongoDB URI. Reserved characters like ':', '/', '+' and '@' must be escaped - following RFC 2396. + following RFC 3986. Returns a 2-tuple containing the unescaped username followed by the unescaped password. @@ -80,16 +81,13 @@ def parse_userinfo(userinfo): Now uses `urllib.unquote_plus` so `+` characters must be escaped. """ if '@' in userinfo or userinfo.count(':') > 1: - raise InvalidURI("':' or '@' characters in a username or password " - "must be escaped according to RFC 2396.") + raise InvalidURI("Username and password must be escaped according to " + "RFC 3986, use urllib.quote_plus().") user, _, passwd = _partition(userinfo, ":") # No password is expected with GSSAPI authentication. if not user: raise InvalidURI("The empty string is not valid username.") - user = unquote_plus(user) - passwd = unquote_plus(passwd) - - return user, passwd + return unquote_plus(user), unquote_plus(passwd) def parse_ipv6_literal_host(entity, default_port): @@ -251,6 +249,11 @@ def split_hosts(hosts, default_port=DEFAULT_PORT): return nodes +# Prohibited characters in database name. DB names also can't have ".", but for +# backward-compat we allow "db.collection" in URI. +_BAD_DB_CHARS = re.compile('[' + re.escape(r'/ "$') + ']') + + def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False): """Parse and validate a MongoDB URI. @@ -294,19 +297,10 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False): collection = None options = {} - # Check for unix domain sockets in the uri - if '.sock' in scheme_free: - host_part, _, path_part = _rpartition(scheme_free, '/') - if not host_part: - host_part = path_part - path_part = "" - if '/' in host_part: - raise InvalidURI("Any '/' in a unix domain socket must be" - " URL encoded: %s" % host_part) - host_part = unquote_plus(host_part) - path_part = unquote_plus(path_part) - else: - host_part, _, path_part = _partition(scheme_free, '/') + host_part, _, path_part = _partition(scheme_free, '/') + if not host_part: + host_part = path_part + path_part = "" if not path_part and '?' in host_part: raise InvalidURI("A '/' is required between " @@ -318,17 +312,24 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False): else: hosts = host_part + if '/' in hosts: + raise InvalidURI("Any '/' in a unix domain socket must be" + " URL encoded: %s" % host_part) + + hosts = unquote_plus(hosts) nodes = split_hosts(hosts, default_port=default_port) if path_part: - if path_part[0] == '?': - opts = path_part[1:] + opts = unquote_plus(path_part[1:]) else: - dbase, _, opts = _partition(path_part, '?') + dbase, _, opts = map(unquote_plus, _partition(path_part, '?')) if '.' in dbase: dbase, collection = dbase.split('.', 1) + if _BAD_DB_CHARS.search(dbase): + raise InvalidURI('Bad database name "%s"' % dbase) + if opts: options = split_options(opts, validate, warn) diff --git a/test/connection_string/test/invalid-uris.json b/test/connection_string/test/invalid-uris.json index e47a52a92..51a843427 100644 --- a/test/connection_string/test/invalid-uris.json +++ b/test/connection_string/test/invalid-uris.json @@ -189,6 +189,15 @@ "valid": false, "warning": null }, + { + "auth": null, + "description": "Username with password containing an unescaped colon", + "hosts": null, + "options": null, + "uri": "mongodb://alice:foo:bar@127.0.0.1", + "valid": false, + "warning": null + }, { "auth": null, "description": "Username containing an unescaped at-sign", @@ -207,6 +216,33 @@ "valid": false, "warning": null }, + { + "auth": null, + "description": "Username containing an unescaped slash", + "hosts": null, + "options": null, + "uri": "mongodb://alice/@localhost/db", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username containing unescaped slash with password", + "hosts": null, + "options": null, + "uri": "mongodb://alice/bob:foo@localhost/db", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username with password containing an unescaped slash", + "hosts": null, + "options": null, + "uri": "mongodb://alice:foo/bar@localhost/db", + "valid": false, + "warning": null + }, { "auth": null, "description": "Host with unescaped slash", diff --git a/test/connection_string/test/valid-auth.json b/test/connection_string/test/valid-auth.json index b3aa7f9cf..9cb90c7d0 100644 --- a/test/connection_string/test/valid-auth.json +++ b/test/connection_string/test/valid-auth.json @@ -2,328 +2,309 @@ "tests": [ { "auth": { - "db": null, - "password": "foo", + "db": null, + "password": "foo", "username": "alice" - }, - "description": "User info for single IPv4 host without database", + }, + "description": "User info for single IPv4 host without database", "hosts": [ { - "host": "127.0.0.1", - "port": null, + "host": "127.0.0.1", + "port": null, "type": "ipv4" } - ], - "options": null, - "uri": "mongodb://alice:foo@127.0.0.1", - "valid": true, + ], + "options": null, + "uri": "mongodb://alice:foo@127.0.0.1", + "valid": true, "warning": false - }, + }, { "auth": { - "db": "test", - "password": "foo", + "db": "test", + "password": "foo", "username": "alice" - }, - "description": "User info for single IPv4 host with database", + }, + "description": "User info for single IPv4 host with database", "hosts": [ { - "host": "127.0.0.1", - "port": null, + "host": "127.0.0.1", + "port": null, "type": "ipv4" } - ], - "options": null, - "uri": "mongodb://alice:foo@127.0.0.1/test", - "valid": true, + ], + "options": null, + "uri": "mongodb://alice:foo@127.0.0.1/test", + "valid": true, "warning": false - }, + }, { "auth": { - "db": "t\u0000est", - "password": "f\u0000oo", - "username": "a\u0000lice" - }, - "description": "User info for single IPv4 host with database (escaped null bytes)", - "hosts": [ - { - "host": "127.0.0.1", - "port": null, - "type": "ipv4" - } - ], - "options": null, - "uri": "mongodb://a%00lice:f%00oo@127.0.0.1/t%00est", - "valid": true, - "warning": false - }, - { - "auth": { - "db": null, - "password": "bar", + "db": null, + "password": "bar", "username": "bob" - }, - "description": "User info for single IP literal host without database", + }, + "description": "User info for single IP literal host without database", "hosts": [ { - "host": "::1", - "port": 27018, + "host": "::1", + "port": 27018, "type": "ip_literal" } - ], - "options": null, - "uri": "mongodb://bob:bar@[::1]:27018", - "valid": true, + ], + "options": null, + "uri": "mongodb://bob:bar@[::1]:27018", + "valid": true, "warning": false - }, + }, { "auth": { - "db": "admin", - "password": "bar", + "db": "admin", + "password": "bar", "username": "bob" - }, - "description": "User info for single IP literal host with database", + }, + "description": "User info for single IP literal host with database", "hosts": [ { - "host": "::1", - "port": 27018, + "host": "::1", + "port": 27018, "type": "ip_literal" } - ], - "options": null, - "uri": "mongodb://bob:bar@[::1]:27018/admin", - "valid": true, + ], + "options": null, + "uri": "mongodb://bob:bar@[::1]:27018/admin", + "valid": true, "warning": false - }, + }, { "auth": { - "db": null, - "password": "baz", + "db": null, + "password": "baz", "username": "eve" - }, - "description": "User info for single hostname without database", + }, + "description": "User info for single hostname without database", "hosts": [ { - "host": "example.com", - "port": null, + "host": "example.com", + "port": null, "type": "hostname" } - ], - "options": null, - "uri": "mongodb://eve:baz@example.com", - "valid": true, + ], + "options": null, + "uri": "mongodb://eve:baz@example.com", + "valid": true, "warning": false - }, + }, { "auth": { - "db": "db2", - "password": "baz", + "db": "db2", + "password": "baz", "username": "eve" - }, - "description": "User info for single hostname with database", + }, + "description": "User info for single hostname with database", "hosts": [ { - "host": "example.com", - "port": null, + "host": "example.com", + "port": null, "type": "hostname" } - ], - "options": null, - "uri": "mongodb://eve:baz@example.com/db2", - "valid": true, + ], + "options": null, + "uri": "mongodb://eve:baz@example.com/db2", + "valid": true, "warning": false - }, + }, { "auth": { - "db": null, - "password": "secret", + "db": null, + "password": "secret", "username": "alice" - }, - "description": "User info for multiple hosts without database", + }, + "description": "User info for multiple hosts without database", "hosts": [ { - "host": "127.0.0.1", - "port": null, + "host": "127.0.0.1", + "port": null, "type": "ipv4" - }, + }, { - "host": "example.com", - "port": 27018, + "host": "example.com", + "port": 27018, "type": "hostname" } - ], - "options": null, - "uri": "mongodb://alice:secret@127.0.0.1,example.com:27018", - "valid": true, + ], + "options": null, + "uri": "mongodb://alice:secret@127.0.0.1,example.com:27018", + "valid": true, "warning": false - }, + }, { "auth": { - "db": "admin", - "password": "secret", + "db": "admin", + "password": "secret", "username": "alice" - }, - "description": "User info for multiple hosts with database", + }, + "description": "User info for multiple hosts with database", "hosts": [ { - "host": "example.com", - "port": null, + "host": "example.com", + "port": null, "type": "hostname" - }, + }, { - "host": "::1", - "port": 27019, + "host": "::1", + "port": 27019, "type": "ip_literal" } - ], - "options": null, - "uri": "mongodb://alice:secret@example.com,[::1]:27019/admin", - "valid": true, + ], + "options": null, + "uri": "mongodb://alice:secret@example.com,[::1]:27019/admin", + "valid": true, "warning": false - }, + }, { "auth": { - "db": null, - "password": null, + "db": null, + "password": null, "username": "alice" - }, - "description": "Username without password", + }, + "description": "Username without password", "hosts": [ { - "host": "127.0.0.1", - "port": null, + "host": "127.0.0.1", + "port": null, "type": "ipv4" } - ], - "options": null, - "uri": "mongodb://alice@127.0.0.1", - "valid": true, + ], + "options": null, + "uri": "mongodb://alice@127.0.0.1", + "valid": true, "warning": false - }, + }, { "auth": { - "db": null, - "password": "", + "db": null, + "password": "", "username": "alice" - }, - "description": "Username with empty password", + }, + "description": "Username with empty password", "hosts": [ { - "host": "127.0.0.1", - "port": null, + "host": "127.0.0.1", + "port": null, "type": "ipv4" } - ], - "options": null, - "uri": "mongodb://alice:@127.0.0.1", - "valid": true, + ], + "options": null, + "uri": "mongodb://alice:@127.0.0.1", + "valid": true, "warning": false - }, + }, { "auth": { - "db": "my=db", - "password": null, - "username": "@l:ce" - }, - "description": "Escaped username and database without password", + "db": "my=db", + "password": null, + "username": "@l:ce/=" + }, + "description": "Escaped username and database without password", "hosts": [ { - "host": "example.com", - "port": null, + "host": "example.com", + "port": null, "type": "hostname" } - ], - "options": null, - "uri": "mongodb://%40l%3Ace@example.com/my%3Ddb", - "valid": true, + ], + "options": null, + "uri": "mongodb://%40l%3Ace%2F%3D@example.com/my%3Ddb", + "valid": true, "warning": false - }, + }, { "auth": { - "db": "admin?", - "password": "f:zzb@zz", + "db": "admin?", + "password": "f:zzb@z/z=", "username": "$am" - }, - "description": "Escaped user info and database (MONGODB-CR)", + }, + "description": "Escaped user info and database (MONGODB-CR)", "hosts": [ { - "host": "127.0.0.1", - "port": null, + "host": "127.0.0.1", + "port": null, "type": "ipv4" } - ], + ], "options": { "authmechanism": "MONGODB-CR" - }, - "uri": "mongodb://%24am:f%3Azzb%40zz@127.0.0.1/admin%3F?authMechanism=MONGODB-CR", - "valid": true, + }, + "uri": "mongodb://%24am:f%3Azzb%40z%2Fz%3D@127.0.0.1/admin%3F?authMechanism=MONGODB-CR", + "valid": true, "warning": false - }, + }, { "auth": { - "db": null, - "password": null, + "db": null, + "password": null, "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" - }, - "description": "Escaped username (MONGODB-X509)", + }, + "description": "Escaped username (MONGODB-X509)", "hosts": [ { - "host": "localhost", - "port": null, + "host": "localhost", + "port": null, "type": "hostname" } - ], + ], "options": { "authmechanism": "MONGODB-X509" - }, - "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509", - "valid": true, + }, + "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509", + "valid": true, "warning": false - }, + }, { "auth": { - "db": null, - "password": "secret", + "db": null, + "password": "secret", "username": "user@EXAMPLE.COM" - }, - "description": "Escaped username (GSSAPI)", + }, + "description": "Escaped username (GSSAPI)", "hosts": [ { - "host": "localhost", - "port": null, + "host": "localhost", + "port": null, "type": "hostname" } - ], + ], "options": { - "authmechanism": "GSSAPI", + "authmechanism": "GSSAPI", "authmechanismproperties": { - "CANONICALIZE_HOST_NAME": true, + "CANONICALIZE_HOST_NAME": true, "SERVICE_NAME": "other" } - }, - "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", - "valid": true, + }, + "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", + "valid": true, "warning": false - }, + }, { "auth": { - "db": "admin", - "password": "secret", + "db": "admin", + "password": "secret", "username": "alice" - }, - "description": "At-signs in options aren't part of the userinfo", + }, + "description": "At-signs in options aren't part of the userinfo", "hosts": [ { - "host": "example.com", - "port": null, + "host": "example.com", + "port": null, "type": "hostname" } - ], + ], "options": { "replicaset": "my@replicaset" - }, - "uri": "mongodb://alice:secret@example.com/admin?replicaset=my@replicaset", - "valid": true, + }, + "uri": "mongodb://alice:secret@example.com/admin?replicaset=my@replicaset", + "valid": true, "warning": false } ] diff --git a/test/connection_string/test/valid-db-with-dotted-name.json b/test/connection_string/test/valid-db-with-dotted-name.json new file mode 100644 index 000000000..c3349859e --- /dev/null +++ b/test/connection_string/test/valid-db-with-dotted-name.json @@ -0,0 +1,100 @@ +{ + "tests": [ + { + "auth": { + "db": "admin.sock", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets and auth DB resembling a socket (relative path)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.sock", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin.shoe", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets with auth DB resembling a path (relative path)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.shoe", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin.sock", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets and auth DB resembling a socket (absolute path)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin.sock", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin.shoe", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets with auth DB resembling a path (absolute path)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin.shoe", + "valid": true, + "warning": false + } + ] +} diff --git a/test/connection_string/test/valid-host_identifiers.json b/test/connection_string/test/valid-host_identifiers.json index 0a69dd97d..f33358725 100644 --- a/test/connection_string/test/valid-host_identifiers.json +++ b/test/connection_string/test/valid-host_identifiers.json @@ -1,154 +1,154 @@ { - "tests": [ + "tests": [ + { + "description": "Single IPv4 host without port", + "uri": "mongodb://127.0.0.1", + "valid": true, + "warning": false, + "hosts": [ { - "auth": null, - "description": "Single IPv4 host without port", - "hosts": [ - { - "host": "127.0.0.1", - "port": null, - "type": "ipv4" - } - ], - "options": null, - "uri": "mongodb://127.0.0.1", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "Single IPv4 host with port", - "hosts": [ - { - "host": "127.0.0.1", - "port": 27018, - "type": "ipv4" - } - ], - "options": null, - "uri": "mongodb://127.0.0.1:27018", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "Single IP literal host without port", - "hosts": [ - { - "host": "::1", - "port": null, - "type": "ip_literal" - } - ], - "options": null, - "uri": "mongodb://[::1]", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "Single IP literal host with port", - "hosts": [ - { - "host": "::1", - "port": 27019, - "type": "ip_literal" - } - ], - "options": null, - "uri": "mongodb://[::1]:27019", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "Single hostname without port", - "hosts": [ - { - "host": "example.com", - "port": null, - "type": "hostname" - } - ], - "options": null, - "uri": "mongodb://example.com", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "Single hostname with port", - "hosts": [ - { - "host": "example.com", - "port": 27020, - "type": "hostname" - } - ], - "options": null, - "uri": "mongodb://example.com:27020", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "Single hostname (resembling IPv4) without port", - "hosts": [ - { - "host": "256.0.0.1", - "port": null, - "type": "hostname" - } - ], - "options": null, - "uri": "mongodb://256.0.0.1", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "Multiple hosts (mixed formats)", - "hosts": [ - { - "host": "127.0.0.1", - "port": null, - "type": "ipv4" - }, - { - "host": "::1", - "port": 27018, - "type": "ip_literal" - }, - { - "host": "example.com", - "port": 27019, - "type": "hostname" - } - ], - "options": null, - "uri": "mongodb://127.0.0.1,[::1]:27018,example.com:27019", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "UTF-8 hosts", - "hosts": [ - { - "host": "b\u00fccher.example.com", - "port": null, - "type": "hostname" - }, - { - "host": "uml\u00e4ut.example.com", - "port": null, - "type": "hostname" - } - ], - "options": null, - "uri": "mongodb://b\u00fccher.example.com,uml\u00e4ut.example.com/", - "valid": true, - "warning": false + "type": "ipv4", + "host": "127.0.0.1", + "port": null } - ] + ], + "auth": null, + "options": null + }, + { + "description": "Single IPv4 host with port", + "uri": "mongodb://127.0.0.1:27018", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ipv4", + "host": "127.0.0.1", + "port": 27018 + } + ], + "auth": null, + "options": null + }, + { + "description": "Single IP literal host without port", + "uri": "mongodb://[::1]", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ip_literal", + "host": "::1", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Single IP literal host with port", + "uri": "mongodb://[::1]:27019", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ip_literal", + "host": "::1", + "port": 27019 + } + ], + "auth": null, + "options": null + }, + { + "description": "Single hostname without port", + "uri": "mongodb://example.com", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Single hostname with port", + "uri": "mongodb://example.com:27020", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": 27020 + } + ], + "auth": null, + "options": null + }, + { + "description": "Single hostname (resembling IPv4) without port", + "uri": "mongodb://256.0.0.1", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "256.0.0.1", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Multiple hosts (mixed formats)", + "uri": "mongodb://127.0.0.1,[::1]:27018,example.com:27019", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ipv4", + "host": "127.0.0.1", + "port": null + }, + { + "type": "ip_literal", + "host": "::1", + "port": 27018 + }, + { + "type": "hostname", + "host": "example.com", + "port": 27019 + } + ], + "auth": null, + "options": null + }, + { + "description": "UTF-8 hosts", + "uri": "mongodb://b\u00fccher.example.com,uml\u00e4ut.example.com/", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "b\u00fccher.example.com", + "port": null + }, + { + "type": "hostname", + "host": "uml\u00e4ut.example.com", + "port": null + } + ], + "auth": null, + "options": null + } + ] } diff --git a/test/connection_string/test/valid-options.json b/test/connection_string/test/valid-options.json index 01f44bce5..4c2bded9e 100644 --- a/test/connection_string/test/valid-options.json +++ b/test/connection_string/test/valid-options.json @@ -1,42 +1,25 @@ { - "tests": [ + "tests": [ + { + "description": "Option names are normalized to lowercase", + "uri": "mongodb://alice:secret@example.com/admin?AUTHMechanism=MONGODB-CR", + "valid": true, + "warning": false, + "hosts": [ { - "auth": { - "db": "admin", - "password": "secret", - "username": "alice" - }, - "description": "Option names are normalized to lowercase", - "hosts": [ - { - "host": "example.com", - "port": null, - "type": "hostname" - } - ], - "options": { - "authmechanism": "MONGODB-CR" - }, - "uri": "mongodb://alice:secret@example.com/admin?AUTHMechanism=MONGODB-CR", - "valid": true, - "warning": false - }, - { - "auth": null, - "description": "Option key and value (escaped null bytes)", - "hosts": [ - { - "host": "example.com", - "port": null, - "type": "hostname" - } - ], - "options": { - "replicaset": "my\u0000rs" - }, - "uri": "mongodb://example.com/?replicaSet=my%00rs", - "valid": true, - "warning": false + "type": "hostname", + "host": "example.com", + "port": null } - ] + ], + "auth": { + "username": "alice", + "password": "secret", + "db": "admin" + }, + "options": { + "authmechanism": "MONGODB-CR" + } + } + ] } diff --git a/test/connection_string/test/valid-unix_socket-absolute.json b/test/connection_string/test/valid-unix_socket-absolute.json index dbf5f59eb..6c60369f4 100644 --- a/test/connection_string/test/valid-unix_socket-absolute.json +++ b/test/connection_string/test/valid-unix_socket-absolute.json @@ -175,11 +175,11 @@ }, { "auth": { - "db": "admin.sock", + "db": "admin", "password": null, "username": null }, - "description": "Multiple Unix domain sockets and auth DB resembling a socket (absolute path)", + "description": "Multiple Unix domain sockets and auth DB (absolute path)", "hosts": [ { "host": "/tmp/mongodb-27017.sock", @@ -193,17 +193,17 @@ } ], "options": null, - "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin.sock", + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin", "valid": true, "warning": false }, { "auth": { - "db": "admin.shoe", + "db": "admin", "password": null, "username": null }, - "description": "Multiple Unix domain sockets with auth DB resembling a path (absolute path)", + "description": "Multiple Unix domain sockets with auth DB (absolute path)", "hosts": [ { "host": "/tmp/mongodb-27017.sock", @@ -217,7 +217,7 @@ } ], "options": null, - "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin.shoe", + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin", "valid": true, "warning": false }, diff --git a/test/connection_string/test/valid-unix_socket-relative.json b/test/connection_string/test/valid-unix_socket-relative.json index 00b699eab..9282bfde4 100644 --- a/test/connection_string/test/valid-unix_socket-relative.json +++ b/test/connection_string/test/valid-unix_socket-relative.json @@ -195,7 +195,7 @@ }, { "auth": { - "db": "admin.sock", + "db": "admin", "password": null, "username": null }, @@ -213,13 +213,13 @@ } ], "options": null, - "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.sock", + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin", "valid": true, "warning": false }, { "auth": { - "db": "admin.shoe", + "db": "admin", "password": null, "username": null }, @@ -237,7 +237,7 @@ } ], "options": null, - "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.shoe", + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin", "valid": true, "warning": false }, diff --git a/test/connection_string/test/valid-warnings.json b/test/connection_string/test/valid-warnings.json index 05ab214cc..6775fae98 100644 --- a/test/connection_string/test/valid-warnings.json +++ b/test/connection_string/test/valid-warnings.json @@ -1,68 +1,68 @@ { - "tests": [ + "tests": [ + { + "description": "Unrecognized option keys are ignored", + "uri": "mongodb://example.com/?foo=bar", + "valid": true, + "warning": true, + "hosts": [ { - "auth": null, - "description": "Unrecognized option keys are ignored", - "hosts": [ - { - "host": "example.com", - "port": null, - "type": "hostname" - } - ], - "options": null, - "uri": "mongodb://example.com/?foo=bar", - "valid": true, - "warning": true - }, - { - "auth": null, - "description": "Unsupported option values are ignored", - "hosts": [ - { - "host": "example.com", - "port": null, - "type": "hostname" - } - ], - "options": null, - "uri": "mongodb://example.com/?fsync=ifPossible", - "valid": true, - "warning": true - }, - { - "auth": null, - "description": "Repeated option keys", - "hosts": [ - { - "host": "example.com", - "port": null, - "type": "hostname" - } - ], - "options": { - "replicaset": "test" - }, - "uri": "mongodb://example.com/?replicaSet=test&replicaSet=test", - "valid": true, - "warning": true - }, - { - "auth": null, - "description": "Deprecated (or unknown) options are ignored if replacement exists", - "hosts": [ - { - "host": "example.com", - "port": null, - "type": "hostname" - } - ], - "options": { - "wtimeoutms": 10 - }, - "uri": "mongodb://example.com/?wtimeout=5&wtimeoutMS=10", - "valid": true, - "warning": true + "type": "hostname", + "host": "example.com", + "port": null } - ] -} + ], + "auth": null, + "options": null + }, + { + "description": "Unsupported option values are ignored", + "uri": "mongodb://example.com/?fsync=ifPossible", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Repeated option keys", + "uri": "mongodb://example.com/?replicaSet=test&replicaSet=test", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": { + "replicaset": "test" + } + }, + { + "description": "Deprecated (or unknown) options are ignored if replacement exists", + "uri": "mongodb://example.com/?wtimeout=5&wtimeoutMS=10", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": { + "wtimeoutms": 10 + } + } + ] +} \ No newline at end of file diff --git a/test/test_uri_parser.py b/test/test_uri_parser.py index 3862b0a1c..2cb58f012 100644 --- a/test/test_uri_parser.py +++ b/test/test_uri_parser.py @@ -331,6 +331,12 @@ class TestURI(unittest.TestCase): parse_uri("mongodb://fred:foobar@localhost/" "test.yield_historical.in")) + res = copy.deepcopy(orig) + res['database'] = 'test' + res['collection'] = 'name/with "delimiters' + self.assertEqual( + res, parse_uri("mongodb://localhost/test.name/with \"delimiters")) + res = copy.deepcopy(orig) res['options'] = {'readpreference': ReadPreference.SECONDARY.mode} self.assertEqual(res, parse_uri( @@ -446,6 +452,30 @@ class TestURI(unittest.TestCase): for key in res['options']: self.assertTrue(isinstance(key, str)) + def test_parse_ssl_paths(self): + # Turn off "validate" since these paths don't exist on filesystem. + self.assertEqual( + {'collection': None, + 'database': None, + 'nodelist': [('/MongoDB.sock', None)], + 'options': {'ssl_certfile': '/a/b'}, + 'password': 'foo/bar', + 'username': 'jesse'}, + parse_uri( + 'mongodb://jesse:foo%2Fbar@%2FMongoDB.sock/?ssl_certfile=/a/b', + validate=False)) + + self.assertEqual( + {'collection': None, + 'database': None, + 'nodelist': [('/MongoDB.sock', None)], + 'options': {'ssl_certfile': 'a/b'}, + 'password': 'foo/bar', + 'username': 'jesse'}, + parse_uri( + 'mongodb://jesse:foo%2Fbar@%2FMongoDB.sock/?ssl_certfile=a/b', + validate=False)) + if __name__ == "__main__": unittest.main()