Inconsistent behavior of auth options in host string and kwarg when connect
ryantd opened this issue · 0 comments
Background
MongoDB version: 4.2.6
mongoengine version: 0.23.1
pymongo: 3.12.0
In my scenario, I need to connect with authentication_source
and authentication_mechanism
and then do some queries.
My code goes like this,
class User(Document):
name = StringField()
# connect
connect(
host='mongodb://{username}:{password}@{host}:{port}/{db}'.format(**MONGO_CONFIG),
authentication_mechanism=MONGO_CONFIG['mechanism'],
authentication_source=MONGO_CONFIG['db']
)
# query
User.objects.count()
Analysis
1. If auth options to be a kwarg
of connect
authentication_mechanism
and authentication_source
will be cleaned when _create_connection,
mongoengine/mongoengine/connection.py
Lines 265 to 275 in 5fe9436
And will get MongoCredential
in pymongo like this when connect,
MongoCredential(mechanism='DEFAULT', source='s', username='u', password='p', mechanism_properties=None, cache=<pymongo.auth._Cache object at 0x7f9aa2db0e50>)
The error will be raised when we do a query or a count op, because the new MongoCredential
is different
Traceback (most recent call last):
File "debug.py", line 27, in <module>
User.objects.count()
File "/Users/.../opt/anaconda3/envs/mongoengine-pr/lib/python3.7/site-packages/mongoengine/queryset/manager.py", line 38, in __get__
queryset = queryset_class(owner, owner._get_collection())
File "/Users/.../opt/anaconda3/envs/mongoengine-pr/lib/python3.7/site-packages/mongoengine/document.py", line 215, in _get_collection
db = cls._get_db()
File "/Users/.../opt/anaconda3/envs/mongoengine-pr/lib/python3.7/site-packages/mongoengine/document.py", line 193, in _get_db
return get_db(cls._meta.get("db_alias", DEFAULT_CONNECTION_NAME))
File "/Users/.../opt/anaconda3/envs/mongoengine-pr/lib/python3.7/site-packages/mongoengine/connection.py", line 368, in get_db
conn_settings["username"], conn_settings["password"], **auth_kwargs
File "/Users/.../opt/anaconda3/envs/mongoengine-pr/lib/python3.7/site-packages/pymongo/database.py", line 1575, in authenticate
connect=True)
File "/Users/.../opt/anaconda3/envs/mongoengine-pr/lib/python3.7/site-packages/pymongo/mongo_client.py", line 806, in _cache_credentials
raise OperationFailure('Another user is already authenticated '
pymongo.errors.OperationFailure: Another user is already authenticated to this database. You must logout first.
MongoCredential(mechanism='SCRAM-SHA-256', source='s', username='u', password='p', mechanism_properties=None, cache=<pymongo.auth._Cache object at 0x7f9fe769b390>)
authentication_mechanism
The root cause of this issue is that the get_db
function is handling the transform (mongoengine's authentication_mechanism
to pymongo's mechanism
) when a query is taking off. However, that is not happening during the connection phase.
mongoengine/mongoengine/connection.py
Line 356 in 5fe9436
authentication_source
authentication_source
is not handling during the connection phase too. But this will be set as db name
by default in pymongo.
2. If auth options to be in the host string
Everything goes fine in this situation because pymongo will parse the host string and set them correctly. Like,
connect(host='mongodb://{username}:{password}@{host}:{port}/{db}?authMechanism={mechanism}&authSource={db}'.format(**MONGO_CONFIG))