From 5729ab2c8e368b4b386504bbaa1044d0050754ee Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 29 Oct 2024 07:03:28 -0500 Subject: [PATCH 1/5] PYTHON-4922 Remove Support for MONGODB-CR Authentication --- doc/changelog.rst | 1 + doc/examples/authentication.rst | 21 +--------- pymongo/asynchronous/auth.py | 16 -------- pymongo/auth_shared.py | 1 - pymongo/synchronous/auth.py | 16 -------- test/auth/legacy/connection-string.json | 41 ------------------- test/connection_string/test/valid-auth.json | 32 +++------------ .../connection_string/test/valid-options.json | 4 +- test/test_uri_parser.py | 27 ------------ 9 files changed, 10 insertions(+), 149 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 29fddb7b5c..94d991868d 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -12,6 +12,7 @@ PyMongo 4.11 brings a number of changes including: - Dropped support for Python 3.8. - Dropped support for MongoDB 3.6. +- Dropped support for the MONGODB-CR authenticate mechanism, which is no longer supported by MongoDB 4.0+. - Added support for free-threaded Python with the GIL disabled. For more information see: `Free-threaded CPython `_. - :attr:`~pymongo.asynchronous.mongo_client.AsyncMongoClient.address` and diff --git a/doc/examples/authentication.rst b/doc/examples/authentication.rst index 6c89910f3c..b319df814c 100644 --- a/doc/examples/authentication.rst +++ b/doc/examples/authentication.rst @@ -76,24 +76,6 @@ For best performance on Python versions older than 2.7.8 install `backports.pbkd .. _backports.pbkdf2: https://pypi.python.org/pypi/backports.pbkdf2/ -MONGODB-CR ----------- - -.. warning:: MONGODB-CR was deprecated with the release of MongoDB 3.6 and - is no longer supported by MongoDB 4.0. - -Before MongoDB 3.0 the default authentication mechanism was MONGODB-CR, -the "MongoDB Challenge-Response" protocol:: - - >>> from pymongo import MongoClient - >>> client = MongoClient('example.com', - ... username='user', - ... password='password', - ... authMechanism='MONGODB-CR') - >>> - >>> uri = "mongodb://user:password@example.com/?authSource=the_database&authMechanism=MONGODB-CR" - >>> client = MongoClient(uri) - Default Authentication Mechanism -------------------------------- @@ -221,8 +203,7 @@ SASL PLAIN (RFC 4616) MongoDB Enterprise Edition version 2.6 and newer support the SASL PLAIN authentication mechanism, initially intended for delegating authentication -to an LDAP server. Using the PLAIN mechanism is very similar to MONGODB-CR. -These examples use the $external virtual database for LDAP support:: +to an LDAP server. These examples use the $external virtual database for LDAP support:: >>> from pymongo import MongoClient >>> uri = "mongodb://user:password@example.com/?authMechanism=PLAIN" diff --git a/pymongo/asynchronous/auth.py b/pymongo/asynchronous/auth.py index 1fb28f6c49..fc563ec48f 100644 --- a/pymongo/asynchronous/auth.py +++ b/pymongo/asynchronous/auth.py @@ -329,21 +329,6 @@ async def _authenticate_x509(credentials: MongoCredential, conn: AsyncConnection await conn.command("$external", cmd) -async def _authenticate_mongo_cr(credentials: MongoCredential, conn: AsyncConnection) -> None: - """Authenticate using MONGODB-CR.""" - source = credentials.source - username = credentials.username - password = credentials.password - # Get a nonce - response = await conn.command(source, {"getnonce": 1}) - nonce = response["nonce"] - key = _auth_key(nonce, username, password) - - # Actually authenticate - query = {"authenticate": 1, "user": username, "nonce": nonce, "key": key} - await conn.command(source, query) - - async def _authenticate_default(credentials: MongoCredential, conn: AsyncConnection) -> None: if conn.max_wire_version >= 7: if conn.negotiated_mechs: @@ -365,7 +350,6 @@ async def _authenticate_default(credentials: MongoCredential, conn: AsyncConnect _AUTH_MAP: Mapping[str, Callable[..., Coroutine[Any, Any, None]]] = { "GSSAPI": _authenticate_gssapi, - "MONGODB-CR": _authenticate_mongo_cr, "MONGODB-X509": _authenticate_x509, "MONGODB-AWS": _authenticate_aws, "MONGODB-OIDC": _authenticate_oidc, # type:ignore[dict-item] diff --git a/pymongo/auth_shared.py b/pymongo/auth_shared.py index 7e3acd9dfb..11d08ffe9c 100644 --- a/pymongo/auth_shared.py +++ b/pymongo/auth_shared.py @@ -34,7 +34,6 @@ MECHANISMS = frozenset( [ "GSSAPI", - "MONGODB-CR", "MONGODB-OIDC", "MONGODB-X509", "MONGODB-AWS", diff --git a/pymongo/synchronous/auth.py b/pymongo/synchronous/auth.py index 9a3477679d..7b370843c5 100644 --- a/pymongo/synchronous/auth.py +++ b/pymongo/synchronous/auth.py @@ -326,21 +326,6 @@ def _authenticate_x509(credentials: MongoCredential, conn: Connection) -> None: conn.command("$external", cmd) -def _authenticate_mongo_cr(credentials: MongoCredential, conn: Connection) -> None: - """Authenticate using MONGODB-CR.""" - source = credentials.source - username = credentials.username - password = credentials.password - # Get a nonce - response = conn.command(source, {"getnonce": 1}) - nonce = response["nonce"] - key = _auth_key(nonce, username, password) - - # Actually authenticate - query = {"authenticate": 1, "user": username, "nonce": nonce, "key": key} - conn.command(source, query) - - def _authenticate_default(credentials: MongoCredential, conn: Connection) -> None: if conn.max_wire_version >= 7: if conn.negotiated_mechs: @@ -360,7 +345,6 @@ def _authenticate_default(credentials: MongoCredential, conn: Connection) -> Non _AUTH_MAP: Mapping[str, Callable[..., None]] = { "GSSAPI": _authenticate_gssapi, - "MONGODB-CR": _authenticate_mongo_cr, "MONGODB-X509": _authenticate_x509, "MONGODB-AWS": _authenticate_aws, "MONGODB-OIDC": _authenticate_oidc, # type:ignore[dict-item] diff --git a/test/auth/legacy/connection-string.json b/test/auth/legacy/connection-string.json index 57fd9d4a11..ab559582ae 100644 --- a/test/auth/legacy/connection-string.json +++ b/test/auth/legacy/connection-string.json @@ -127,47 +127,6 @@ "uri": "mongodb://localhost/?authMechanism=GSSAPI", "valid": false }, - { - "description": "should recognize the mechanism (MONGODB-CR)", - "uri": "mongodb://user:password@localhost/?authMechanism=MONGODB-CR", - "valid": true, - "credential": { - "username": "user", - "password": "password", - "source": "admin", - "mechanism": "MONGODB-CR", - "mechanism_properties": null - } - }, - { - "description": "should use the database when no authSource is specified (MONGODB-CR)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=MONGODB-CR", - "valid": true, - "credential": { - "username": "user", - "password": "password", - "source": "foo", - "mechanism": "MONGODB-CR", - "mechanism_properties": null - } - }, - { - "description": "should use the authSource when specified (MONGODB-CR)", - "uri": "mongodb://user:password@localhost/foo?authMechanism=MONGODB-CR&authSource=bar", - "valid": true, - "credential": { - "username": "user", - "password": "password", - "source": "bar", - "mechanism": "MONGODB-CR", - "mechanism_properties": null - } - }, - { - "description": "should throw an exception if no username is supplied (MONGODB-CR)", - "uri": "mongodb://localhost/?authMechanism=MONGODB-CR", - "valid": false - }, { "description": "should recognize the mechanism (MONGODB-X509)", "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509", diff --git a/test/connection_string/test/valid-auth.json b/test/connection_string/test/valid-auth.json index 4f684ff185..60f63f4e3f 100644 --- a/test/connection_string/test/valid-auth.json +++ b/test/connection_string/test/valid-auth.json @@ -220,29 +220,8 @@ "options": null }, { - "description": "Escaped user info and database (MONGODB-CR)", - "uri": "mongodb://%24am:f%3Azzb%40z%2Fz%3D@127.0.0.1/admin%3F?authMechanism=MONGODB-CR", - "valid": true, - "warning": false, - "hosts": [ - { - "type": "ipv4", - "host": "127.0.0.1", - "port": null - } - ], - "auth": { - "username": "$am", - "password": "f:zzb@z/z=", - "db": "admin?" - }, - "options": { - "authmechanism": "MONGODB-CR" - } - }, - { - "description": "Subdelimiters in user/pass don't need escaping (MONGODB-CR)", - "uri": "mongodb://!$&'()*+,;=:!$&'()*+,;=@127.0.0.1/admin?authMechanism=MONGODB-CR", + "description": "Subdelimiters in user/pass don't need escaping (PLAIN)", + "uri": "mongodb://!$&'()*+,;=:!$&'()*+,;=@127.0.0.1/admin?authMechanism=PLAIN", "valid": true, "warning": false, "hosts": [ @@ -258,7 +237,7 @@ "db": "admin" }, "options": { - "authmechanism": "MONGODB-CR" + "authmechanism": "PLAIN" } }, { @@ -284,7 +263,7 @@ }, { "description": "Escaped username (GSSAPI)", - "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", + "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com&authMechanism=GSSAPI", "valid": true, "warning": false, "hosts": [ @@ -303,7 +282,8 @@ "authmechanism": "GSSAPI", "authmechanismproperties": { "SERVICE_NAME": "other", - "CANONICALIZE_HOST_NAME": true + "SERVICE_HOST": "example.com", + "CANONICALIZE_HOST_NAME": "forward" } } }, diff --git a/test/connection_string/test/valid-options.json b/test/connection_string/test/valid-options.json index 3c79fe7ae5..6c86172d08 100644 --- a/test/connection_string/test/valid-options.json +++ b/test/connection_string/test/valid-options.json @@ -2,7 +2,7 @@ "tests": [ { "description": "Option names are normalized to lowercase", - "uri": "mongodb://alice:secret@example.com/admin?AUTHMechanism=MONGODB-CR", + "uri": "mongodb://alice:secret@example.com/admin?AUTHMechanism=PLAIN", "valid": true, "warning": false, "hosts": [ @@ -18,7 +18,7 @@ "db": "admin" }, "options": { - "authmechanism": "MONGODB-CR" + "authmechanism": "PLAIN" } }, { diff --git a/test/test_uri_parser.py b/test/test_uri_parser.py index 2a68e9a2cd..5672b45f66 100644 --- a/test/test_uri_parser.py +++ b/test/test_uri_parser.py @@ -142,7 +142,6 @@ def test_split_options(self): self.assertEqual({"fsync": True}, split_options("fsync=true")) self.assertEqual({"fsync": False}, split_options("fsync=false")) self.assertEqual({"authmechanism": "GSSAPI"}, split_options("authMechanism=GSSAPI")) - self.assertEqual({"authmechanism": "MONGODB-CR"}, split_options("authMechanism=MONGODB-CR")) self.assertEqual( {"authmechanism": "SCRAM-SHA-1"}, split_options("authMechanism=SCRAM-SHA-1") ) @@ -294,32 +293,6 @@ def test_parse_uri(self): self.assertEqual(res, parse_uri("mongodb://localhost/?readPreference=secondary")) # Various authentication tests - res = copy.deepcopy(orig) - res["options"] = {"authmechanism": "MONGODB-CR"} - res["username"] = "user" - res["password"] = "password" - self.assertEqual( - res, parse_uri("mongodb://user:password@localhost/?authMechanism=MONGODB-CR") - ) - - res = copy.deepcopy(orig) - res["options"] = {"authmechanism": "MONGODB-CR", "authsource": "bar"} - res["username"] = "user" - res["password"] = "password" - res["database"] = "foo" - self.assertEqual( - res, - parse_uri( - "mongodb://user:password@localhost/foo?authSource=bar;authMechanism=MONGODB-CR" - ), - ) - - res = copy.deepcopy(orig) - res["options"] = {"authmechanism": "MONGODB-CR"} - res["username"] = "user" - res["password"] = "" - self.assertEqual(res, parse_uri("mongodb://user:@localhost/?authMechanism=MONGODB-CR")) - res = copy.deepcopy(orig) res["username"] = "user@domain.com" res["password"] = "password" From 65ac229e595a1adfcc563a30472adb7ef7d7d82a Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 29 Oct 2024 07:20:54 -0500 Subject: [PATCH 2/5] fix test --- test/connection_string/test/valid-auth.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/connection_string/test/valid-auth.json b/test/connection_string/test/valid-auth.json index 60f63f4e3f..0d59031db7 100644 --- a/test/connection_string/test/valid-auth.json +++ b/test/connection_string/test/valid-auth.json @@ -263,7 +263,7 @@ }, { "description": "Escaped username (GSSAPI)", - "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com&authMechanism=GSSAPI", + "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", "valid": true, "warning": false, "hosts": [ @@ -282,8 +282,8 @@ "authmechanism": "GSSAPI", "authmechanismproperties": { "SERVICE_NAME": "other", - "SERVICE_HOST": "example.com", - "CANONICALIZE_HOST_NAME": "forward" + "CANONICALIZE_HOST_NAME": true, + "SERVICE_HOST": "example.com" } } }, From 473690047120a88e1eb7e9bc22bf019cb8d09f64 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 29 Oct 2024 08:55:50 -0500 Subject: [PATCH 3/5] fix test --- test/connection_string/test/valid-auth.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/connection_string/test/valid-auth.json b/test/connection_string/test/valid-auth.json index 0d59031db7..3b78991d65 100644 --- a/test/connection_string/test/valid-auth.json +++ b/test/connection_string/test/valid-auth.json @@ -282,7 +282,7 @@ "authmechanism": "GSSAPI", "authmechanismproperties": { "SERVICE_NAME": "other", - "CANONICALIZE_HOST_NAME": true, + "CANONICALIZE_HOST_NAME": "true", "SERVICE_HOST": "example.com" } } From 16033157b19bc7b5c41678a0ba68087c98896873 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 29 Oct 2024 09:52:39 -0500 Subject: [PATCH 4/5] fix test --- test/connection_string/test/valid-auth.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/connection_string/test/valid-auth.json b/test/connection_string/test/valid-auth.json index 3b78991d65..12192fab4c 100644 --- a/test/connection_string/test/valid-auth.json +++ b/test/connection_string/test/valid-auth.json @@ -263,7 +263,7 @@ }, { "description": "Escaped username (GSSAPI)", - "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", + "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", "valid": true, "warning": false, "hosts": [ @@ -282,8 +282,7 @@ "authmechanism": "GSSAPI", "authmechanismproperties": { "SERVICE_NAME": "other", - "CANONICALIZE_HOST_NAME": "true", - "SERVICE_HOST": "example.com" + "CANONICALIZE_HOST_NAME": true } } }, From 7cd553ea17c11420263ef1e4e7a63c8f9b9a9a6f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 29 Oct 2024 18:27:21 -0500 Subject: [PATCH 5/5] address review --- test/test_uri_parser.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/test_uri_parser.py b/test/test_uri_parser.py index 5672b45f66..f95717e95f 100644 --- a/test/test_uri_parser.py +++ b/test/test_uri_parser.py @@ -293,6 +293,32 @@ def test_parse_uri(self): self.assertEqual(res, parse_uri("mongodb://localhost/?readPreference=secondary")) # Various authentication tests + res = copy.deepcopy(orig) + res["options"] = {"authmechanism": "SCRAM-SHA-256"} + res["username"] = "user" + res["password"] = "password" + self.assertEqual( + res, parse_uri("mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-256") + ) + + res = copy.deepcopy(orig) + res["options"] = {"authmechanism": "SCRAM-SHA-256", "authsource": "bar"} + res["username"] = "user" + res["password"] = "password" + res["database"] = "foo" + self.assertEqual( + res, + parse_uri( + "mongodb://user:password@localhost/foo?authSource=bar;authMechanism=SCRAM-SHA-256" + ), + ) + + res = copy.deepcopy(orig) + res["options"] = {"authmechanism": "SCRAM-SHA-256"} + res["username"] = "user" + res["password"] = "" + self.assertEqual(res, parse_uri("mongodb://user:@localhost/?authMechanism=SCRAM-SHA-256")) + res = copy.deepcopy(orig) res["username"] = "user@domain.com" res["password"] = "password"