PYTHON-2973 Revert back to using quote_plus/unquote_plus (#767)
This commit is contained in:
parent
3c3a85d1bc
commit
42324c69cf
@ -523,7 +523,7 @@ functions:
|
||||
silent: true
|
||||
script: |
|
||||
cat <<'EOF' > "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh"
|
||||
alias urlencode='${python3_binary} -c "import sys, urllib.parse as ulp; sys.stdout.write(ulp.quote(sys.argv[1]))"'
|
||||
alias urlencode='${python3_binary} -c "import sys, urllib.parse as ulp; sys.stdout.write(ulp.quote_plus(sys.argv[1]))"'
|
||||
USER=$(urlencode ${iam_auth_ecs_account})
|
||||
PASS=$(urlencode ${iam_auth_ecs_secret_access_key})
|
||||
MONGODB_URI="mongodb://$USER:$PASS@localhost"
|
||||
@ -554,7 +554,7 @@ functions:
|
||||
script: |
|
||||
# DO NOT ECHO WITH XTRACE (which PREPARE_SHELL does)
|
||||
cat <<'EOF' > "${PROJECT_DIRECTORY}/prepare_mongodb_aws.sh"
|
||||
alias urlencode='${python3_binary} -c "import sys, urllib.parse as ulp; sys.stdout.write(ulp.quote(sys.argv[1]))"'
|
||||
alias urlencode='${python3_binary} -c "import sys, urllib.parse as ulp; sys.stdout.write(ulp.quote_plus(sys.argv[1]))"'
|
||||
alias jsonkey='${python3_binary} -c "import json,sys;sys.stdout.write(json.load(sys.stdin)[sys.argv[1]])" < ${DRIVERS_TOOLS}/.evergreen/auth_aws/creds.json'
|
||||
USER=$(jsonkey AccessKeyId)
|
||||
USER=$(urlencode $USER)
|
||||
|
||||
@ -166,13 +166,6 @@ Breaking Changes in 4.0
|
||||
:exc:`~pymongo.errors.InvalidURI` exception
|
||||
when it encounters unescaped percent signs in username and password when
|
||||
parsing MongoDB URIs.
|
||||
- :class:`~pymongo.mongo_client.MongoClient` now uses
|
||||
:py::func:`urllib.parse.unquote` rather than
|
||||
:py:func:`urllib.parse.unquote_plus`,
|
||||
meaning that plus signs ("+") are no longer converted to spaces (" "). This
|
||||
means that if you were previously quoting your login information using
|
||||
quote_plus, you must now switch to quote. Additionally, be aware that this
|
||||
change only occurs when parsing login information from the URI.
|
||||
|
||||
Notable improvements
|
||||
....................
|
||||
|
||||
@ -15,10 +15,10 @@ Username and password must be percent-escaped with
|
||||
|
||||
>>> from pymongo import MongoClient
|
||||
>>> import urllib.parse
|
||||
>>> username = urllib.parse.quote('user')
|
||||
>>> username = urllib.parse.quote_plus('user')
|
||||
>>> username
|
||||
'user'
|
||||
>>> password = urllib.parse.quote('pass/word')
|
||||
>>> password = urllib.parse.quote_plus('pass/word')
|
||||
>>> password
|
||||
'pass%2Fword'
|
||||
>>> MongoClient('mongodb://%s:%s@127.0.0.1' % (username, password))
|
||||
|
||||
@ -200,16 +200,6 @@ MongoClient raises exception when given unescaped percent sign in login info
|
||||
:exc:`~pymongo.errors.InvalidURI` exception
|
||||
when it encounters unescaped percent signs in username and password.
|
||||
|
||||
MongoClient uses `unquote` rather than `unquote_plus` for login info
|
||||
....................................................................
|
||||
|
||||
:class:`~pymongo.mongo_client.MongoClient` now uses
|
||||
:py:func:`urllib.parse.unquote` rather than
|
||||
:py:func:`urllib.parse.unquote_plus`, meaning that space characters are no
|
||||
longer converted to plus signs. This means that if you were previously
|
||||
quoting your login information using :py:func:`urllib.parse.quote_plus`, you
|
||||
must now switch to :py:func:`urllib.parse.quote`.
|
||||
|
||||
Database
|
||||
--------
|
||||
|
||||
|
||||
@ -319,6 +319,9 @@ def _authenticate_gssapi(credentials, sock_info):
|
||||
|
||||
if password is not None:
|
||||
if _USE_PRINCIPAL:
|
||||
# Note that, though we use unquote_plus for unquoting URI
|
||||
# options, we use quote here. Microsoft's UrlUnescape (used
|
||||
# by WinKerberos) doesn't support +.
|
||||
principal = ":".join((quote(username), quote(password)))
|
||||
result, ctx = kerberos.authGSSClientInit(
|
||||
service, principal, gssflags=kerberos.GSS_C_MUTUAL_FLAG)
|
||||
|
||||
@ -18,7 +18,7 @@ import re
|
||||
import warnings
|
||||
import sys
|
||||
|
||||
from urllib.parse import unquote, unquote_plus
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
from pymongo.common import (
|
||||
SRV_SERVICE_NAME,
|
||||
@ -47,7 +47,7 @@ def _unquoted_percent(s):
|
||||
sub = s[i:i+3]
|
||||
# If unquoting yields the same string this means there was an
|
||||
# unquoted %.
|
||||
if unquote(sub) == sub:
|
||||
if unquote_plus(sub) == sub:
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -65,14 +65,14 @@ def parse_userinfo(userinfo):
|
||||
if ('@' in userinfo or userinfo.count(':') > 1 or
|
||||
_unquoted_percent(userinfo)):
|
||||
raise InvalidURI("Username and password must be escaped according to "
|
||||
"RFC 3986, use urllib.parse.quote")
|
||||
"RFC 3986, use urllib.parse.quote_plus")
|
||||
|
||||
user, _, passwd = userinfo.partition(":")
|
||||
# No password is expected with GSSAPI authentication.
|
||||
if not user:
|
||||
raise InvalidURI("The empty string is not valid username.")
|
||||
|
||||
return unquote(user), unquote(passwd)
|
||||
return unquote_plus(user), unquote_plus(passwd)
|
||||
|
||||
|
||||
def parse_ipv6_literal_host(entity, default_port):
|
||||
@ -430,9 +430,7 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
|
||||
|
||||
.. versionchanged:: 4.0
|
||||
To better follow RFC 3986, unquoted percent signs ("%") are no longer
|
||||
supported and plus signs ("+") are no longer decoded into spaces (" ")
|
||||
when decoding username and password. To avoid these issues, use
|
||||
:py:func:`urllib.parse.quote` when building the URI.
|
||||
supported.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
Added the ``normalize`` parameter.
|
||||
|
||||
@ -20,7 +20,7 @@ import sys
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from urllib.parse import quote
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from pymongo import MongoClient, ssl_support
|
||||
from pymongo.errors import (ConfigurationError,
|
||||
@ -526,7 +526,7 @@ class TestSSL(IntegrationTest):
|
||||
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism='
|
||||
'MONGODB-X509' % (
|
||||
quote(MONGODB_X509_USERNAME), host, port))
|
||||
quote_plus(MONGODB_X509_USERNAME), host, port))
|
||||
client = MongoClient(uri,
|
||||
ssl=True,
|
||||
tlsAllowInvalidCertificates=True,
|
||||
@ -546,7 +546,7 @@ class TestSSL(IntegrationTest):
|
||||
# Auth should fail if username and certificate do not match
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism='
|
||||
'MONGODB-X509' % (
|
||||
quote("not the username"), host, port))
|
||||
quote_plus("not the username"), host, port))
|
||||
|
||||
bad_client = MongoClient(
|
||||
uri, ssl=True, tlsAllowInvalidCertificates=True,
|
||||
@ -571,7 +571,7 @@ class TestSSL(IntegrationTest):
|
||||
# Invalid certificate (using CA certificate as client certificate)
|
||||
uri = ('mongodb://%s@%s:%d/?authMechanism='
|
||||
'MONGODB-X509' % (
|
||||
quote(MONGODB_X509_USERNAME), host, port))
|
||||
quote_plus(MONGODB_X509_USERNAME), host, port))
|
||||
try:
|
||||
connected(MongoClient(uri,
|
||||
ssl=True,
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
import copy
|
||||
import sys
|
||||
import warnings
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
@ -43,7 +44,7 @@ class TestURI(unittest.TestCase):
|
||||
self.assertTrue(parse_userinfo('user:password'))
|
||||
self.assertEqual(('us:r', 'p@ssword'),
|
||||
parse_userinfo('us%3Ar:p%40ssword'))
|
||||
self.assertEqual(('us+er', 'p+ssword'),
|
||||
self.assertEqual(('us er', 'p ssword'),
|
||||
parse_userinfo('us+er:p+ssword'))
|
||||
self.assertEqual(('us er', 'p ssword'),
|
||||
parse_userinfo('us%20er:p%20ssword'))
|
||||
@ -512,6 +513,14 @@ class TestURI(unittest.TestCase):
|
||||
'quote_plus?'):
|
||||
parse_uri(uri)
|
||||
|
||||
def test_special_chars(self):
|
||||
user = "user@ /9+:?~!$&'()*+,;="
|
||||
pwd = "pwd@ /9+:?~!$&'()*+,;="
|
||||
uri = 'mongodb://%s:%s@localhost' % (quote_plus(user), quote_plus(pwd))
|
||||
res = parse_uri(uri)
|
||||
self.assertEqual(user, res['username'])
|
||||
self.assertEqual(pwd, res['password'])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@ -143,6 +143,9 @@ def create_test(test, test_workdir):
|
||||
options['database'] += "." + options['collection']
|
||||
for elm in auth:
|
||||
if auth[elm] is not None:
|
||||
# We have to do this because while the spec requires
|
||||
# "+"->"+", unquote_plus does "+"->" "
|
||||
options[elm] = options[elm].replace(" ", "+")
|
||||
self.assertEqual(auth[elm], options[elm],
|
||||
"Expected %s but got %s"
|
||||
% (auth[elm], options[elm]))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user