Merge branch 'master' of github.com:mongodb/mongo-python-driver
This commit is contained in:
commit
7eafd5dd72
@ -11,7 +11,7 @@ The PyMongo distribution contains tools for interacting with MongoDB
|
||||
database from Python. The `bson` package is an implementation of the
|
||||
[BSON format](http://bsonspec.org) for Python. The `pymongo` package is
|
||||
a native Python driver for MongoDB. The `gridfs` package is a
|
||||
[gridfs](https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.rst/)
|
||||
[gridfs](https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.md/)
|
||||
implementation on top of `pymongo`.
|
||||
|
||||
PyMongo supports MongoDB 4.0, 4.2, 4.4, 5.0, 6.0, 7.0, and 8.0.
|
||||
|
||||
@ -22,7 +22,7 @@ is emitted and parsed, with the default being the Relaxed Extended JSON format.
|
||||
when :const:`CANONICAL_JSON_OPTIONS` or :const:`LEGACY_JSON_OPTIONS` is
|
||||
provided, respectively.
|
||||
|
||||
.. _Extended JSON: https://github.com/mongodb/specifications/blob/master/source/extended-json.rst
|
||||
.. _Extended JSON: https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md
|
||||
|
||||
Example usage (deserialization):
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ interacting with MongoDB. :mod:`bson` is an implementation of the
|
||||
`BSON format <http://bsonspec.org>`_, :mod:`pymongo` is a
|
||||
full-featured driver for MongoDB, and :mod:`gridfs` is a set of tools
|
||||
for working with the `GridFS
|
||||
<https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.rst/>`_ storage
|
||||
<https://github.com/mongodb/specifications/blob/master/source/gridfs/gridfs-spec.md/>`_ storage
|
||||
specification.
|
||||
|
||||
.. toctree::
|
||||
|
||||
@ -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 <https://docs.python.org/3.13/whatsnew/3.13.html#whatsnew313-free-threaded-cpython>`_.
|
||||
- :attr:`~pymongo.asynchronous.mongo_client.AsyncMongoClient.address` and
|
||||
@ -23,6 +24,10 @@ PyMongo 4.11 brings a number of changes including:
|
||||
:meth:`~pymongo.collection.Collection.update_one`, :meth:`~pymongo.collection.Collection.replace_one`,
|
||||
:class:`~pymongo.operations.UpdateOne`, and
|
||||
:class:`~pymongo.operations.UpdateMany`,
|
||||
- :meth:`~pymongo.mongo_client.MongoClient.bulk_write` and
|
||||
:meth:`~pymongo.asynchronous.mongo_client.AsyncMongoClient.bulk_write` now throw an error
|
||||
when ``ordered=True`` or ``verboseResults=True`` are used with unacknowledged writes.
|
||||
These are unavoidable breaking changes.
|
||||
|
||||
Issues Resolved
|
||||
...............
|
||||
@ -1022,7 +1027,7 @@ See the `PyMongo 4.0 release notes in JIRA`_ for the list of resolved issues
|
||||
in this release.
|
||||
|
||||
.. _PyMongo 4.0 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=18463
|
||||
.. _DBRef specification: https://github.com/mongodb/specifications/blob/5a8c8d7/source/dbref.rst
|
||||
.. _DBRef specification: https://github.com/mongodb/specifications/blob/master/source/dbref/dbref.md
|
||||
|
||||
Changes in Version 3.13.0 (2022/11/01)
|
||||
--------------------------------------
|
||||
@ -1557,7 +1562,7 @@ Unavoidable breaking changes:
|
||||
bumped to 1.16.0. This is a breaking change for applications that use
|
||||
PyMongo's SRV support with a version of ``dnspython`` older than 1.16.0.
|
||||
|
||||
.. _URI options specification: https://github.com/mongodb/specifications/blob/master/source/uri-options/uri-options.rst
|
||||
.. _URI options specification: https://github.com/mongodb/specifications/blob/master/source/uri-options/uri-options.md
|
||||
|
||||
|
||||
Issues Resolved
|
||||
@ -1581,7 +1586,7 @@ Changes in Version 3.8.0 (2019/04/22)
|
||||
must upgrade to PyPy3.5+.
|
||||
|
||||
- :class:`~bson.objectid.ObjectId` now implements the `ObjectID specification
|
||||
version 0.2 <https://github.com/mongodb/specifications/blob/master/source/objectid.rst>`_.
|
||||
version 0.2 <https://github.com/mongodb/specifications/blob/master/source/bson-objectid/objectid.md>`_.
|
||||
- For better performance and to better follow the GridFS spec,
|
||||
:class:`~gridfs.grid_file.GridOut` now uses a single cursor to read all the
|
||||
chunks in the file. Previously, each chunk in the file was queried
|
||||
@ -1943,7 +1948,7 @@ Highlights include:
|
||||
:class:`~pymongo.operations.UpdateOne`, and
|
||||
:class:`~pymongo.operations.UpdateMany`.
|
||||
- Implemented the `MongoDB Extended JSON
|
||||
<https://github.com/mongodb/specifications/blob/master/source/extended-json.rst>`_
|
||||
<https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md>`_
|
||||
specification.
|
||||
- :class:`~bson.decimal128.Decimal128` now works when cdecimal is installed.
|
||||
- PyMongo is now tested against a wider array of operating systems and CPU
|
||||
|
||||
@ -85,7 +85,7 @@ pygments_style = "sphinx"
|
||||
# wiki.centos.org has been flakey.
|
||||
# sourceforge.net is giving a 403 error, but is still accessible from the browser.
|
||||
linkcheck_ignore = [
|
||||
"https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-monitoring.rst#requesting-an-immediate-check",
|
||||
"https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-monitoring.md#requesting-an-immediate-check",
|
||||
"https://github.com/mongodb/libmongocrypt/blob/master/bindings/python/README.rst#installing-from-source",
|
||||
r"https://wiki.centos.org/[\w/]*",
|
||||
r"http://sourceforge.net/",
|
||||
|
||||
@ -106,7 +106,7 @@ Thus the current design of periodic executors is surprisingly simple: they
|
||||
do a simple ``time.sleep`` for a half-second, check if it is time to wake or
|
||||
terminate, and sleep again.
|
||||
|
||||
.. _Server Discovery And Monitoring Spec: https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-monitoring.rst#requesting-an-immediate-check
|
||||
.. _Server Discovery And Monitoring Spec: https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-monitoring.md#requesting-an-immediate-check
|
||||
|
||||
.. _PYTHON-863: https://jira.mongodb.org/browse/PYTHON-863
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -84,7 +84,7 @@ Finally, the same UUID would historically be serialized by the Java driver as::
|
||||
.. note:: For in-depth information about the the byte-order historically
|
||||
used by different drivers, see the `Handling of Native UUID Types
|
||||
Specification
|
||||
<https://github.com/mongodb/specifications/blob/master/source/uuid.rst>`_.
|
||||
<https://github.com/mongodb/specifications/blob/master/source/bson-binary-uuid/uuid.md>`_.
|
||||
|
||||
This difference in the byte-order of UUIDs encoded by different drivers can
|
||||
result in highly unintuitive behavior in some scenarios. We detail two such
|
||||
|
||||
@ -118,7 +118,7 @@ Renamed URI options
|
||||
|
||||
Several deprecated URI options have been renamed to the standardized
|
||||
option names defined in the
|
||||
`URI options specification <https://github.com/mongodb/specifications/blob/master/source/uri-options/uri-options.rst>`_.
|
||||
`URI options specification <https://github.com/mongodb/specifications/blob/master/source/uri-options/uri-options.md>`_.
|
||||
The old option names and their renamed equivalents are summarized in the table
|
||||
below. Some renamed options have different semantics from the option being
|
||||
replaced as noted in the 'Migration Notes' column.
|
||||
@ -965,7 +965,7 @@ correct type. Otherwise the document is returned as normal. Previously, any
|
||||
subdocument containing a ``$ref`` field would be decoded as a
|
||||
:class:`~bson.dbref.DBRef`.
|
||||
|
||||
.. _DBRef specification: https://github.com/mongodb/specifications/blob/5a8c8d7/source/dbref.rst
|
||||
.. _DBRef specification: https://github.com/mongodb/specifications/blob/master/source/dbref/dbref.md
|
||||
|
||||
Encoding a UUID raises an error by default
|
||||
..........................................
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -681,11 +681,11 @@ class _AsyncClientBulk:
|
||||
_throw_client_bulk_write_exception(full_result, self.verbose_results)
|
||||
return full_result
|
||||
|
||||
async def execute_command_unack_unordered(
|
||||
async def execute_command_unack(
|
||||
self,
|
||||
conn: AsyncConnection,
|
||||
) -> None:
|
||||
"""Execute commands with OP_MSG and w=0 writeConcern, unordered."""
|
||||
"""Execute commands with OP_MSG and w=0 writeConcern. Always unordered."""
|
||||
db_name = "admin"
|
||||
cmd_name = "bulkWrite"
|
||||
listeners = self.client._event_listeners
|
||||
@ -704,8 +704,8 @@ class _AsyncClientBulk:
|
||||
while self.idx_offset < self.total_ops:
|
||||
# Construct the server command, specifying the relevant options.
|
||||
cmd = {"bulkWrite": 1}
|
||||
cmd["errorsOnly"] = not self.verbose_results
|
||||
cmd["ordered"] = self.ordered # type: ignore[assignment]
|
||||
cmd["errorsOnly"] = True
|
||||
cmd["ordered"] = False
|
||||
if self.bypass_doc_val is not None:
|
||||
cmd["bypassDocumentValidation"] = self.bypass_doc_val
|
||||
cmd["writeConcern"] = {"w": 0} # type: ignore[assignment]
|
||||
@ -723,43 +723,6 @@ class _AsyncClientBulk:
|
||||
|
||||
self.idx_offset += len(to_send_ops)
|
||||
|
||||
async def execute_command_unack_ordered(
|
||||
self,
|
||||
conn: AsyncConnection,
|
||||
) -> None:
|
||||
"""Execute commands with OP_MSG and w=0 WriteConcern, ordered."""
|
||||
full_result: MutableMapping[str, Any] = {
|
||||
"anySuccessful": False,
|
||||
"error": None,
|
||||
"writeErrors": [],
|
||||
"writeConcernErrors": [],
|
||||
"nInserted": 0,
|
||||
"nUpserted": 0,
|
||||
"nMatched": 0,
|
||||
"nModified": 0,
|
||||
"nDeleted": 0,
|
||||
"insertResults": {},
|
||||
"updateResults": {},
|
||||
"deleteResults": {},
|
||||
}
|
||||
# Ordered bulk writes have to be acknowledged so that we stop
|
||||
# processing at the first error, even when the application
|
||||
# specified unacknowledged writeConcern.
|
||||
initial_write_concern = WriteConcern()
|
||||
op_id = _randint()
|
||||
try:
|
||||
await self._execute_command(
|
||||
initial_write_concern,
|
||||
None,
|
||||
conn,
|
||||
op_id,
|
||||
False,
|
||||
full_result,
|
||||
self.write_concern,
|
||||
)
|
||||
except OperationFailure:
|
||||
pass
|
||||
|
||||
async def execute_no_results(
|
||||
self,
|
||||
conn: AsyncConnection,
|
||||
@ -775,9 +738,7 @@ class _AsyncClientBulk:
|
||||
"Cannot set bypass_document_validation with unacknowledged write concern"
|
||||
)
|
||||
|
||||
if self.ordered:
|
||||
return await self.execute_command_unack_ordered(conn)
|
||||
return await self.execute_command_unack_unordered(conn)
|
||||
return await self.execute_command_unack(conn)
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
|
||||
@ -221,7 +221,7 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
<https://en.wikipedia.org/wiki/TXT_record>`_. See the
|
||||
`Initial DNS Seedlist Discovery spec
|
||||
<https://github.com/mongodb/specifications/blob/master/source/
|
||||
initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst>`_
|
||||
initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md>`_
|
||||
for more details. Note that the use of SRV URIs implicitly enables
|
||||
TLS support. Pass tls=false in the URI to override.
|
||||
|
||||
@ -367,7 +367,7 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
:meth:`~pymongo.asynchronous.collection.AsyncCollection.aggregate` using the ``$out``
|
||||
pipeline operator and any operation with an unacknowledged write
|
||||
concern (e.g. {w: 0})). See
|
||||
https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst
|
||||
https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.md
|
||||
- `retryReads`: (boolean) Whether supported read operations
|
||||
executed within this AsyncMongoClient will be retried once after a
|
||||
network error. Defaults to ``True``.
|
||||
@ -394,7 +394,7 @@ class AsyncMongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
transient errors such as network failures, database upgrades, and
|
||||
replica set failovers. For an exact definition of which errors
|
||||
trigger a retry, see the `retryable reads specification
|
||||
<https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.rst>`_.
|
||||
<https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.md>`_.
|
||||
|
||||
- `compressors`: Comma separated list of compressors for wire
|
||||
protocol compression. The list is used to negotiate a compressor
|
||||
|
||||
@ -34,7 +34,6 @@ from pymongo.errors import ConfigurationError
|
||||
MECHANISMS = frozenset(
|
||||
[
|
||||
"GSSAPI",
|
||||
"MONGODB-CR",
|
||||
"MONGODB-OIDC",
|
||||
"MONGODB-X509",
|
||||
"MONGODB-AWS",
|
||||
@ -78,7 +77,7 @@ MongoCredential = namedtuple(
|
||||
|
||||
|
||||
GSSAPIProperties = namedtuple(
|
||||
"GSSAPIProperties", ["service_name", "canonicalize_host_name", "service_realm"]
|
||||
"GSSAPIProperties", ["service_name", "canonicalize_host_name", "service_realm", "service_host"]
|
||||
)
|
||||
"""Mechanism properties for GSSAPI authentication."""
|
||||
|
||||
@ -87,6 +86,16 @@ _AWSProperties = namedtuple("_AWSProperties", ["aws_session_token"])
|
||||
"""Mechanism properties for MONGODB-AWS authentication."""
|
||||
|
||||
|
||||
def _validate_canonicalize_host_name(value: str | bool) -> str | bool:
|
||||
valid_names = [False, True, "none", "forward", "forwardAndReverse"]
|
||||
if value in ["true", "false", True, False]:
|
||||
return value in ["true", True]
|
||||
|
||||
if value not in valid_names:
|
||||
raise ValueError(f"CANONICALIZE_HOST_NAME '{value}' not in valid options: {valid_names}")
|
||||
return value
|
||||
|
||||
|
||||
def _build_credentials_tuple(
|
||||
mech: str,
|
||||
source: Optional[str],
|
||||
@ -103,12 +112,15 @@ def _build_credentials_tuple(
|
||||
raise ValueError("authentication source must be $external or None for GSSAPI")
|
||||
properties = extra.get("authmechanismproperties", {})
|
||||
service_name = properties.get("SERVICE_NAME", "mongodb")
|
||||
canonicalize = bool(properties.get("CANONICALIZE_HOST_NAME", False))
|
||||
service_host = properties.get("SERVICE_HOST", None)
|
||||
canonicalize = properties.get("CANONICALIZE_HOST_NAME", "false")
|
||||
canonicalize = _validate_canonicalize_host_name(canonicalize)
|
||||
service_realm = properties.get("SERVICE_REALM")
|
||||
props = GSSAPIProperties(
|
||||
service_name=service_name,
|
||||
canonicalize_host_name=canonicalize,
|
||||
service_realm=service_realm,
|
||||
service_host=service_host,
|
||||
)
|
||||
# Source is always $external.
|
||||
return MongoCredential(mech, "$external", user, passwd, props, None)
|
||||
|
||||
@ -139,6 +139,9 @@ SRV_SERVICE_NAME = "mongodb"
|
||||
# Default value for serverMonitoringMode
|
||||
SERVER_MONITORING_MODE = "auto" # poll/stream/auto
|
||||
|
||||
# Auth mechanism properties that must raise an error instead of warning if they invalidate.
|
||||
_MECH_PROP_MUST_RAISE = ["CANONICALIZE_HOST_NAME"]
|
||||
|
||||
|
||||
def partition_node(node: str) -> tuple[str, int]:
|
||||
"""Split a host:port string into (host, int(port)) pair."""
|
||||
@ -423,6 +426,7 @@ def validate_read_preference_tags(name: str, value: Any) -> list[dict[str, str]]
|
||||
_MECHANISM_PROPS = frozenset(
|
||||
[
|
||||
"SERVICE_NAME",
|
||||
"SERVICE_HOST",
|
||||
"CANONICALIZE_HOST_NAME",
|
||||
"SERVICE_REALM",
|
||||
"AWS_SESSION_TOKEN",
|
||||
@ -476,7 +480,9 @@ 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)
|
||||
from pymongo.auth_shared import _validate_canonicalize_host_name
|
||||
|
||||
props[key] = _validate_canonicalize_host_name(val)
|
||||
else:
|
||||
props[key] = val
|
||||
|
||||
@ -867,6 +873,10 @@ def get_validated_options(
|
||||
validator = _get_validator(opt, URI_OPTIONS_VALIDATOR_MAP, normed_key=normed_key)
|
||||
validated = validator(opt, value)
|
||||
except (ValueError, TypeError, ConfigurationError) as exc:
|
||||
if normed_key == "authmechanismproperties" and any(
|
||||
p in str(exc) for p in _MECH_PROP_MUST_RAISE
|
||||
):
|
||||
raise
|
||||
if warn:
|
||||
warnings.warn(str(exc), stacklevel=2)
|
||||
else:
|
||||
|
||||
@ -216,7 +216,7 @@ def _metadata_env() -> dict[str, Any]:
|
||||
_MAX_METADATA_SIZE = 512
|
||||
|
||||
|
||||
# See: https://github.com/mongodb/specifications/blob/5112bcc/source/mongodb-handshake/handshake.rst#limitations
|
||||
# See: https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.md#limitations
|
||||
def _truncate_metadata(metadata: MutableMapping[str, Any]) -> None:
|
||||
"""Perform metadata truncation."""
|
||||
if len(bson.encode(metadata)) <= _MAX_METADATA_SIZE:
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -679,11 +679,11 @@ class _ClientBulk:
|
||||
_throw_client_bulk_write_exception(full_result, self.verbose_results)
|
||||
return full_result
|
||||
|
||||
def execute_command_unack_unordered(
|
||||
def execute_command_unack(
|
||||
self,
|
||||
conn: Connection,
|
||||
) -> None:
|
||||
"""Execute commands with OP_MSG and w=0 writeConcern, unordered."""
|
||||
"""Execute commands with OP_MSG and w=0 writeConcern. Always unordered."""
|
||||
db_name = "admin"
|
||||
cmd_name = "bulkWrite"
|
||||
listeners = self.client._event_listeners
|
||||
@ -702,8 +702,8 @@ class _ClientBulk:
|
||||
while self.idx_offset < self.total_ops:
|
||||
# Construct the server command, specifying the relevant options.
|
||||
cmd = {"bulkWrite": 1}
|
||||
cmd["errorsOnly"] = not self.verbose_results
|
||||
cmd["ordered"] = self.ordered # type: ignore[assignment]
|
||||
cmd["errorsOnly"] = True
|
||||
cmd["ordered"] = False
|
||||
if self.bypass_doc_val is not None:
|
||||
cmd["bypassDocumentValidation"] = self.bypass_doc_val
|
||||
cmd["writeConcern"] = {"w": 0} # type: ignore[assignment]
|
||||
@ -721,43 +721,6 @@ class _ClientBulk:
|
||||
|
||||
self.idx_offset += len(to_send_ops)
|
||||
|
||||
def execute_command_unack_ordered(
|
||||
self,
|
||||
conn: Connection,
|
||||
) -> None:
|
||||
"""Execute commands with OP_MSG and w=0 WriteConcern, ordered."""
|
||||
full_result: MutableMapping[str, Any] = {
|
||||
"anySuccessful": False,
|
||||
"error": None,
|
||||
"writeErrors": [],
|
||||
"writeConcernErrors": [],
|
||||
"nInserted": 0,
|
||||
"nUpserted": 0,
|
||||
"nMatched": 0,
|
||||
"nModified": 0,
|
||||
"nDeleted": 0,
|
||||
"insertResults": {},
|
||||
"updateResults": {},
|
||||
"deleteResults": {},
|
||||
}
|
||||
# Ordered bulk writes have to be acknowledged so that we stop
|
||||
# processing at the first error, even when the application
|
||||
# specified unacknowledged writeConcern.
|
||||
initial_write_concern = WriteConcern()
|
||||
op_id = _randint()
|
||||
try:
|
||||
self._execute_command(
|
||||
initial_write_concern,
|
||||
None,
|
||||
conn,
|
||||
op_id,
|
||||
False,
|
||||
full_result,
|
||||
self.write_concern,
|
||||
)
|
||||
except OperationFailure:
|
||||
pass
|
||||
|
||||
def execute_no_results(
|
||||
self,
|
||||
conn: Connection,
|
||||
@ -773,9 +736,7 @@ class _ClientBulk:
|
||||
"Cannot set bypass_document_validation with unacknowledged write concern"
|
||||
)
|
||||
|
||||
if self.ordered:
|
||||
return self.execute_command_unack_ordered(conn)
|
||||
return self.execute_command_unack_unordered(conn)
|
||||
return self.execute_command_unack(conn)
|
||||
|
||||
def execute(
|
||||
self,
|
||||
|
||||
@ -216,7 +216,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
<https://en.wikipedia.org/wiki/TXT_record>`_. See the
|
||||
`Initial DNS Seedlist Discovery spec
|
||||
<https://github.com/mongodb/specifications/blob/master/source/
|
||||
initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst>`_
|
||||
initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md>`_
|
||||
for more details. Note that the use of SRV URIs implicitly enables
|
||||
TLS support. Pass tls=false in the URI to override.
|
||||
|
||||
@ -365,7 +365,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
:meth:`~pymongo.collection.Collection.aggregate` using the ``$out``
|
||||
pipeline operator and any operation with an unacknowledged write
|
||||
concern (e.g. {w: 0})). See
|
||||
https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst
|
||||
https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.md
|
||||
- `retryReads`: (boolean) Whether supported read operations
|
||||
executed within this MongoClient will be retried once after a
|
||||
network error. Defaults to ``True``.
|
||||
@ -392,7 +392,7 @@ class MongoClient(common.BaseObject, Generic[_DocumentType]):
|
||||
transient errors such as network failures, database upgrades, and
|
||||
replica set failovers. For an exact definition of which errors
|
||||
trigger a retry, see the `retryable reads specification
|
||||
<https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.rst>`_.
|
||||
<https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.md>`_.
|
||||
|
||||
- `compressors`: Comma separated list of compressors for wire
|
||||
protocol compression. The list is used to negotiate a compressor
|
||||
|
||||
@ -375,7 +375,7 @@ class TestSCRAMSHA1(AsyncIntegrationTest):
|
||||
await db.command("dbstats")
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst#scram-sha-256-and-mechanism-negotiation
|
||||
# https://github.com/mongodb/specifications/blob/master/source/auth/auth.md#scram-sha-256-and-mechanism-negotiation
|
||||
class TestSCRAM(AsyncIntegrationTest):
|
||||
@async_client_context.require_auth
|
||||
@async_client_context.require_version_min(3, 7, 2)
|
||||
|
||||
@ -1610,7 +1610,7 @@ class TestGCPEncryption(AzureGCPEncryptionTestMixin, AsyncEncryptionIntegrationT
|
||||
return await self._test_automatic(expected_document_extjson, {"secret_gcp": "string0"})
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#deadlock-tests
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#deadlock-tests
|
||||
class TestDeadlockProse(AsyncEncryptionIntegrationTest):
|
||||
async def asyncSetUp(self):
|
||||
self.client_test = await self.async_rs_or_single_client(
|
||||
@ -1837,7 +1837,7 @@ class TestDeadlockProse(AsyncEncryptionIntegrationTest):
|
||||
self.assertEqual(len(self.topology_listener.results["opened"]), 1)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#14-decryption-events
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#14-decryption-events
|
||||
class TestDecryptProse(AsyncEncryptionIntegrationTest):
|
||||
async def asyncSetUp(self):
|
||||
self.client = async_client_context.client
|
||||
@ -1909,7 +1909,7 @@ class TestDecryptProse(AsyncEncryptionIntegrationTest):
|
||||
self.assertEqual(event.reply["cursor"]["firstBatch"][0]["encrypted"], self.cipher_text)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#bypass-spawning-mongocryptd
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#bypass-spawning-mongocryptd
|
||||
class TestBypassSpawningMongocryptdProse(AsyncEncryptionIntegrationTest):
|
||||
@unittest.skipIf(
|
||||
os.environ.get("TEST_CRYPT_SHARED"),
|
||||
@ -1990,7 +1990,7 @@ class TestBypassSpawningMongocryptdProse(AsyncEncryptionIntegrationTest):
|
||||
with self.assertRaises(ServerSelectionTimeoutError):
|
||||
await no_mongocryptd_client.db.command("ping")
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#20-bypass-creating-mongocryptd-client-when-shared-library-is-loaded
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#20-bypass-creating-mongocryptd-client-when-shared-library-is-loaded
|
||||
@unittest.skipUnless(os.environ.get("TEST_CRYPT_SHARED"), "crypt_shared lib is not installed")
|
||||
async def test_client_via_loading_shared_library(self):
|
||||
connection_established = False
|
||||
@ -2066,7 +2066,7 @@ class TestKmsTLSProse(AsyncEncryptionIntegrationTest):
|
||||
await self.client_encrypted.create_data_key("aws", master_key=key)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#kms-tls-options-tests
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#kms-tls-options-tests
|
||||
class TestKmsTLSOptions(AsyncEncryptionIntegrationTest):
|
||||
@unittest.skipUnless(any(AWS_CREDS.values()), "AWS environment credentials are not set")
|
||||
async def asyncSetUp(self):
|
||||
@ -2272,7 +2272,7 @@ class TestKmsTLSOptions(AsyncEncryptionIntegrationTest):
|
||||
await self.client_encryption_with_names.create_data_key("kmip:with_tls")
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/50e26fe/source/client-side-encryption/tests/README.rst#unique-index-on-keyaltnames
|
||||
# https://github.com/mongodb/specifications/blob/50e26fe/source/client-side-encryption/tests/README.md#unique-index-on-keyaltnames
|
||||
class TestUniqueIndexOnKeyAltNamesProse(AsyncEncryptionIntegrationTest):
|
||||
async def asyncSetUp(self):
|
||||
self.client = async_client_context.client
|
||||
@ -2303,7 +2303,7 @@ class TestUniqueIndexOnKeyAltNamesProse(AsyncEncryptionIntegrationTest):
|
||||
assert key_doc["keyAltNames"] == ["def"]
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/d4c9432/source/client-side-encryption/tests/README.rst#explicit-encryption
|
||||
# https://github.com/mongodb/specifications/blob/d4c9432/source/client-side-encryption/tests/README.md#explicit-encryption
|
||||
class TestExplicitQueryableEncryption(AsyncEncryptionIntegrationTest):
|
||||
@async_client_context.require_no_standalone
|
||||
@async_client_context.require_version_min(7, 0, -1)
|
||||
@ -2423,7 +2423,7 @@ class TestExplicitQueryableEncryption(AsyncEncryptionIntegrationTest):
|
||||
self.assertEqual(decrypted, val)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/072601/source/client-side-encryption/tests/README.rst#rewrap
|
||||
# https://github.com/mongodb/specifications/blob/072601/source/client-side-encryption/tests/README.md#rewrap
|
||||
class TestRewrapWithSeparateClientEncryption(AsyncEncryptionIntegrationTest):
|
||||
MASTER_KEYS: Mapping[str, Mapping[str, Any]] = {
|
||||
"aws": {
|
||||
@ -2505,7 +2505,7 @@ class TestRewrapWithSeparateClientEncryption(AsyncEncryptionIntegrationTest):
|
||||
)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/5cf3ed/source/client-side-encryption/tests/README.rst#on-demand-aws-credentials
|
||||
# https://github.com/mongodb/specifications/blob/5cf3ed/source/client-side-encryption/tests/README.md#on-demand-aws-credentials
|
||||
class TestOnDemandAWSCredentials(AsyncEncryptionIntegrationTest):
|
||||
async def asyncSetUp(self):
|
||||
await super().asyncSetUp()
|
||||
@ -2869,7 +2869,7 @@ class TestRangeQueryDefaultsProse(AsyncEncryptionIntegrationTest):
|
||||
assert len(payload) > len(self.payload_defaults)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#automatic-data-encryption-keys
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#automatic-data-encryption-keys
|
||||
class TestAutomaticDecryptionKeys(AsyncEncryptionIntegrationTest):
|
||||
@async_client_context.require_no_standalone
|
||||
@async_client_context.require_version_min(7, 0, -1)
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
"""Unified test format runner.
|
||||
|
||||
https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst
|
||||
https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@ -431,7 +431,7 @@ class UnifiedSpecTestMixinV1(AsyncIntegrationTest):
|
||||
"""Mixin class to run test cases from test specification files.
|
||||
|
||||
Assumes that tests conform to the `unified test format
|
||||
<https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst>`_.
|
||||
<https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md>`_.
|
||||
|
||||
Specification of the test suite being currently run is available as
|
||||
a class attribute ``TEST_SPEC``.
|
||||
|
||||
@ -80,7 +80,7 @@
|
||||
},
|
||||
{
|
||||
"description": "should accept generic mechanism property (GSSAPI)",
|
||||
"uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true",
|
||||
"uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forward,SERVICE_HOST:example.com",
|
||||
"valid": true,
|
||||
"credential": {
|
||||
"username": "user@DOMAIN.COM",
|
||||
@ -89,10 +89,46 @@
|
||||
"mechanism": "GSSAPI",
|
||||
"mechanism_properties": {
|
||||
"SERVICE_NAME": "other",
|
||||
"CANONICALIZE_HOST_NAME": true
|
||||
"SERVICE_HOST": "example.com",
|
||||
"CANONICALIZE_HOST_NAME": "forward"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "should accept forwardAndReverse hostname canonicalization (GSSAPI)",
|
||||
"uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:forwardAndReverse",
|
||||
"valid": true,
|
||||
"credential": {
|
||||
"username": "user@DOMAIN.COM",
|
||||
"password": null,
|
||||
"source": "$external",
|
||||
"mechanism": "GSSAPI",
|
||||
"mechanism_properties": {
|
||||
"SERVICE_NAME": "other",
|
||||
"CANONICALIZE_HOST_NAME": "forwardAndReverse"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "should accept no hostname canonicalization (GSSAPI)",
|
||||
"uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:none",
|
||||
"valid": true,
|
||||
"credential": {
|
||||
"username": "user@DOMAIN.COM",
|
||||
"password": null,
|
||||
"source": "$external",
|
||||
"mechanism": "GSSAPI",
|
||||
"mechanism_properties": {
|
||||
"SERVICE_NAME": "other",
|
||||
"CANONICALIZE_HOST_NAME": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "must raise an error when the hostname canonicalization is invalid",
|
||||
"uri": "mongodb://user%40DOMAIN.COM@localhost/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:invalid",
|
||||
"valid": false
|
||||
},
|
||||
{
|
||||
"description": "should accept the password (GSSAPI)",
|
||||
"uri": "mongodb://user%40DOMAIN.COM:password@localhost/?authMechanism=GSSAPI&authSource=$external",
|
||||
@ -127,47 +163,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",
|
||||
@ -474,14 +469,14 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "should throw an exception if username and password is specified for test environment (MONGODB-OIDC)",
|
||||
"description": "should throw an exception if supplied a password (MONGODB-OIDC)",
|
||||
"uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test",
|
||||
"valid": false,
|
||||
"credential": null
|
||||
},
|
||||
{
|
||||
"description": "should throw an exception if username is specified for test environment (MONGODB-OIDC)",
|
||||
"uri": "mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&ENVIRONMENT:test",
|
||||
"description": "should throw an exception if username is specified for test (MONGODB-OIDC)",
|
||||
"uri": "mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test",
|
||||
"valid": false,
|
||||
"credential": null
|
||||
},
|
||||
@ -492,11 +487,17 @@
|
||||
"credential": null
|
||||
},
|
||||
{
|
||||
"description": "should throw an exception if neither provider nor callbacks specified (MONGODB-OIDC)",
|
||||
"description": "should throw an exception if neither environment nor callbacks specified (MONGODB-OIDC)",
|
||||
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC",
|
||||
"valid": false,
|
||||
"credential": null
|
||||
},
|
||||
{
|
||||
"description": "should throw an exception when unsupported auth property is specified (MONGODB-OIDC)",
|
||||
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=UnsupportedProperty:unexisted",
|
||||
"valid": false,
|
||||
"credential": null
|
||||
},
|
||||
{
|
||||
"description": "should recognise the mechanism with azure provider (MONGODB-OIDC)",
|
||||
"uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:azure,TOKEN_RESOURCE:foo",
|
||||
@ -627,4 +628,4 @@
|
||||
"credential": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -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"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -218,50 +218,19 @@ class TestHandshake(unittest.TestCase):
|
||||
request.ok(
|
||||
"ismaster",
|
||||
True,
|
||||
saslSupportedMechs=["SCRAM-SHA-256"],
|
||||
# Unsupported auth mech should be ignored.
|
||||
saslSupportedMechs=["SCRAM-SHA-256", "does_not_exist"],
|
||||
speculativeAuthenticate=auth,
|
||||
minWireVersion=2,
|
||||
maxWireVersion=MIN_SUPPORTED_WIRE_VERSION,
|
||||
)
|
||||
# Authentication should immediately fail with:
|
||||
# OperationFailure: Server returned an invalid nonce.
|
||||
with self.assertRaises(OperationFailure):
|
||||
with self.assertRaises(OperationFailure) as cm:
|
||||
future()
|
||||
self.assertEqual(str(cm.exception), "Server returned an invalid nonce.")
|
||||
return
|
||||
|
||||
def test_client_handshake_saslSupportedMechs_unknown(self):
|
||||
server = MockupDB()
|
||||
server.run()
|
||||
self.addCleanup(server.stop)
|
||||
|
||||
primary_response = OpReply(
|
||||
"ismaster",
|
||||
True,
|
||||
minWireVersion=2,
|
||||
maxWireVersion=MIN_SUPPORTED_WIRE_VERSION,
|
||||
saslSupportedMechs=["SCRAM-SHA-256", "does_not_exist"],
|
||||
)
|
||||
client = MongoClient(
|
||||
server.uri, authmechanism="PLAIN", username="username", password="password"
|
||||
)
|
||||
|
||||
self.addCleanup(client.close)
|
||||
|
||||
# New monitoring connections send data during handshake.
|
||||
heartbeat = server.receives("ismaster")
|
||||
heartbeat.ok(primary_response)
|
||||
|
||||
future = go(client.db.command, "whatever")
|
||||
for request in server:
|
||||
if request.matches("ismaster"):
|
||||
request.ok(primary_response)
|
||||
elif request.matches("saslStart"):
|
||||
request.ok("saslStart", True, conversationId=1, payload=b"", done=True, ok=1)
|
||||
else:
|
||||
request.ok()
|
||||
future()
|
||||
return
|
||||
|
||||
def test_handshake_load_balanced(self):
|
||||
self.hello_with_option_helper(OpMsg, loadBalanced=True)
|
||||
with self.assertRaisesRegex(AssertionError, "does not match"):
|
||||
|
||||
@ -373,7 +373,7 @@ class TestSCRAMSHA1(IntegrationTest):
|
||||
db.command("dbstats")
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst#scram-sha-256-and-mechanism-negotiation
|
||||
# https://github.com/mongodb/specifications/blob/master/source/auth/auth.md#scram-sha-256-and-mechanism-negotiation
|
||||
class TestSCRAM(IntegrationTest):
|
||||
@client_context.require_auth
|
||||
@client_context.require_version_min(3, 7, 2)
|
||||
|
||||
@ -128,7 +128,7 @@ class TestDBRef(unittest.TestCase):
|
||||
self.assertNotEqual(hash(dbref_1a), hash(dbref_2a))
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/dbref.rst#test-plan
|
||||
# https://github.com/mongodb/specifications/blob/master/source/dbref/dbref.md#test-plan
|
||||
class TestDBRefSpec(unittest.TestCase):
|
||||
def test_decoding_1_2_3(self):
|
||||
doc: Any
|
||||
|
||||
@ -1604,7 +1604,7 @@ class TestGCPEncryption(AzureGCPEncryptionTestMixin, EncryptionIntegrationTest):
|
||||
return self._test_automatic(expected_document_extjson, {"secret_gcp": "string0"})
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#deadlock-tests
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#deadlock-tests
|
||||
class TestDeadlockProse(EncryptionIntegrationTest):
|
||||
def setUp(self):
|
||||
self.client_test = self.rs_or_single_client(
|
||||
@ -1829,7 +1829,7 @@ class TestDeadlockProse(EncryptionIntegrationTest):
|
||||
self.assertEqual(len(self.topology_listener.results["opened"]), 1)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#14-decryption-events
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#14-decryption-events
|
||||
class TestDecryptProse(EncryptionIntegrationTest):
|
||||
def setUp(self):
|
||||
self.client = client_context.client
|
||||
@ -1901,7 +1901,7 @@ class TestDecryptProse(EncryptionIntegrationTest):
|
||||
self.assertEqual(event.reply["cursor"]["firstBatch"][0]["encrypted"], self.cipher_text)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#bypass-spawning-mongocryptd
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#bypass-spawning-mongocryptd
|
||||
class TestBypassSpawningMongocryptdProse(EncryptionIntegrationTest):
|
||||
@unittest.skipIf(
|
||||
os.environ.get("TEST_CRYPT_SHARED"),
|
||||
@ -1982,7 +1982,7 @@ class TestBypassSpawningMongocryptdProse(EncryptionIntegrationTest):
|
||||
with self.assertRaises(ServerSelectionTimeoutError):
|
||||
no_mongocryptd_client.db.command("ping")
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#20-bypass-creating-mongocryptd-client-when-shared-library-is-loaded
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#20-bypass-creating-mongocryptd-client-when-shared-library-is-loaded
|
||||
@unittest.skipUnless(os.environ.get("TEST_CRYPT_SHARED"), "crypt_shared lib is not installed")
|
||||
def test_client_via_loading_shared_library(self):
|
||||
connection_established = False
|
||||
@ -2058,7 +2058,7 @@ class TestKmsTLSProse(EncryptionIntegrationTest):
|
||||
self.client_encrypted.create_data_key("aws", master_key=key)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#kms-tls-options-tests
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#kms-tls-options-tests
|
||||
class TestKmsTLSOptions(EncryptionIntegrationTest):
|
||||
@unittest.skipUnless(any(AWS_CREDS.values()), "AWS environment credentials are not set")
|
||||
def setUp(self):
|
||||
@ -2264,7 +2264,7 @@ class TestKmsTLSOptions(EncryptionIntegrationTest):
|
||||
self.client_encryption_with_names.create_data_key("kmip:with_tls")
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/50e26fe/source/client-side-encryption/tests/README.rst#unique-index-on-keyaltnames
|
||||
# https://github.com/mongodb/specifications/blob/50e26fe/source/client-side-encryption/tests/README.md#unique-index-on-keyaltnames
|
||||
class TestUniqueIndexOnKeyAltNamesProse(EncryptionIntegrationTest):
|
||||
def setUp(self):
|
||||
self.client = client_context.client
|
||||
@ -2293,7 +2293,7 @@ class TestUniqueIndexOnKeyAltNamesProse(EncryptionIntegrationTest):
|
||||
assert key_doc["keyAltNames"] == ["def"]
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/d4c9432/source/client-side-encryption/tests/README.rst#explicit-encryption
|
||||
# https://github.com/mongodb/specifications/blob/d4c9432/source/client-side-encryption/tests/README.md#explicit-encryption
|
||||
class TestExplicitQueryableEncryption(EncryptionIntegrationTest):
|
||||
@client_context.require_no_standalone
|
||||
@client_context.require_version_min(7, 0, -1)
|
||||
@ -2407,7 +2407,7 @@ class TestExplicitQueryableEncryption(EncryptionIntegrationTest):
|
||||
self.assertEqual(decrypted, val)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/072601/source/client-side-encryption/tests/README.rst#rewrap
|
||||
# https://github.com/mongodb/specifications/blob/072601/source/client-side-encryption/tests/README.md#rewrap
|
||||
class TestRewrapWithSeparateClientEncryption(EncryptionIntegrationTest):
|
||||
MASTER_KEYS: Mapping[str, Mapping[str, Any]] = {
|
||||
"aws": {
|
||||
@ -2489,7 +2489,7 @@ class TestRewrapWithSeparateClientEncryption(EncryptionIntegrationTest):
|
||||
)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/5cf3ed/source/client-side-encryption/tests/README.rst#on-demand-aws-credentials
|
||||
# https://github.com/mongodb/specifications/blob/5cf3ed/source/client-side-encryption/tests/README.md#on-demand-aws-credentials
|
||||
class TestOnDemandAWSCredentials(EncryptionIntegrationTest):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
@ -2851,7 +2851,7 @@ class TestRangeQueryDefaultsProse(EncryptionIntegrationTest):
|
||||
assert len(payload) > len(self.payload_defaults)
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#automatic-data-encryption-keys
|
||||
# https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#automatic-data-encryption-keys
|
||||
class TestAutomaticDecryptionKeys(EncryptionIntegrationTest):
|
||||
@client_context.require_no_standalone
|
||||
@client_context.require_version_min(7, 0, -1)
|
||||
|
||||
@ -142,7 +142,7 @@ class TestStreamingProtocol(IntegrationTest):
|
||||
@client_context.require_failCommand_appName
|
||||
def test_monitor_waits_after_server_check_error(self):
|
||||
# This test implements:
|
||||
# https://github.com/mongodb/specifications/blob/6c5b2ac/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.rst#monitors-sleep-at-least-minheartbeatfreqencyms-between-checks
|
||||
# https://github.com/mongodb/specifications/blob/master/source/server-discovery-and-monitoring/server-discovery-and-monitoring-tests.md#monitors-sleep-at-least-minheartbeatfreqencyms-between-checks
|
||||
fail_hello = {
|
||||
"mode": {"times": 5},
|
||||
"data": {
|
||||
|
||||
@ -142,7 +142,6 @@ class TestURI(unittest.TestCase):
|
||||
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")
|
||||
)
|
||||
@ -295,30 +294,30 @@ class TestURI(unittest.TestCase):
|
||||
|
||||
# Various authentication tests
|
||||
res = copy.deepcopy(orig)
|
||||
res["options"] = {"authmechanism": "MONGODB-CR"}
|
||||
res["options"] = {"authmechanism": "SCRAM-SHA-256"}
|
||||
res["username"] = "user"
|
||||
res["password"] = "password"
|
||||
self.assertEqual(
|
||||
res, parse_uri("mongodb://user:password@localhost/?authMechanism=MONGODB-CR")
|
||||
res, parse_uri("mongodb://user:password@localhost/?authMechanism=SCRAM-SHA-256")
|
||||
)
|
||||
|
||||
res = copy.deepcopy(orig)
|
||||
res["options"] = {"authmechanism": "MONGODB-CR", "authsource": "bar"}
|
||||
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=MONGODB-CR"
|
||||
"mongodb://user:password@localhost/foo?authSource=bar;authMechanism=SCRAM-SHA-256"
|
||||
),
|
||||
)
|
||||
|
||||
res = copy.deepcopy(orig)
|
||||
res["options"] = {"authmechanism": "MONGODB-CR"}
|
||||
res["options"] = {"authmechanism": "SCRAM-SHA-256"}
|
||||
res["username"] = "user"
|
||||
res["password"] = ""
|
||||
self.assertEqual(res, parse_uri("mongodb://user:@localhost/?authMechanism=MONGODB-CR"))
|
||||
self.assertEqual(res, parse_uri("mongodb://user:@localhost/?authMechanism=SCRAM-SHA-256"))
|
||||
|
||||
res = copy.deepcopy(orig)
|
||||
res["username"] = "user@domain.com"
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
"""Unified test format runner.
|
||||
|
||||
https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst
|
||||
https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@ -431,7 +431,7 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
|
||||
"""Mixin class to run test cases from test specification files.
|
||||
|
||||
Assumes that tests conform to the `unified test format
|
||||
<https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst>`_.
|
||||
<https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md>`_.
|
||||
|
||||
Specification of the test suite being currently run is available as
|
||||
a class attribute ``TEST_SPEC``.
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
"""Shared utility functions and constants for the unified test format runner.
|
||||
|
||||
https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst
|
||||
https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user