-
Couldn't load subscription status.
- Fork 1.2k
connection: Add authentication mechanism support. #905 #1333
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…09 without password)
Conflicts: mongoengine/connection.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this @zeezdev ! I added some comments to the code. Let me know what you think.
mongoengine/connection.py
Outdated
| from pymongo import MongoReplicaSetClient | ||
| READ_PREFERENCE = False | ||
|
|
||
| if IS_PYMONGO_27: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could use a comment - why do we need to explicitly specify "MONGODB-CR" as the default authentication mechanism for PyMongo v2.7, while all the other PyMongo versions can use the "DEFAULT"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mongoengine is supported pymongo >= 2.7. Is not it?
for pymogo v2.7 is required authmechanism = 'MONGODB-CR' by default:
https://github.com/mongodb/mongo-python-driver/blob/v2.7/pymongo/mongo_client.py#L372
While all the other pymongo versions can use the "DEFAULT", It is pymongo requires:
https://github.com/mongodb/mongo-python-driver/blob/v2.8/pymongo/mongo_client.py#L381
https://github.com/mongodb/mongo-python-driver/blob/v2.9/pymongo/mongo_client.py#L431
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in PyMongo v2.7 (https://github.com/mongodb/mongo-python-driver/blob/v2.7/pymongo/mongo_client.py#L372) I see:
mechanism = options.get('authmechanism', 'MONGODB-CR')
and in PyMongo v2.8+ I see:
mechanism = options.get('authmechanism', 'DEFAULT')
It seems that PyMongo v2.7 already defaults to MONGODB-CR and v2.8+ defaults to DEFAULT if authmechanism is not specified. I don't understand then why we have to explicitly specify it in MongoEngine... It seems that it should be enough to not pass it to PyMongo at all unless it was specified in the config. Am I missing something?
mongoengine/connection.py
Outdated
| source=conn_settings['authentication_source']) | ||
| source=conn_settings['authentication_source'], | ||
| mechanism=conn_settings['authentication_mechanism'] | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this closing parenthesis can be at the end of the previous line, i.e. mechanism=conn_settings['authentication_mechanism']).
| conn_settings = _connection_settings[alias] | ||
| db = conn[conn_settings['name']] | ||
| # Authenticate if necessary | ||
| if conn_settings['username'] and conn_settings['password']: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it ok to drop the password check here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because when use MONGODB-X509 authentication mechanism not need specify password argument, only username.
http://api.mongodb.com/python/3.0b0/examples/authentication.html#mongodb-x509
While I agree that it is necessary to check the username for other authentication mechanisms.
mongoengine/python_support.py
Outdated
| import pymongo | ||
|
|
||
|
|
||
| IS_PYMONGO_27 = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could be more explicit with this test. For example PyMongo v1.7, v2.6, etc. would make IS_PYMONGO_27 true right now (no matter if they actually exist/are used in the wild).
How about:
IS_PYMONGO_27 = pymongo.version_tuple[0] == 2 and pymongo.version_tuple[1] == 7
… conn_settings for connections with not 'MONGODB-X509' mechanism
mongoengine/python_support.py
Outdated
| IS_PYMONGO_3 = False | ||
| if pymongo.version_tuple[1] < 8: | ||
| IS_PYMONGO_27 = True | ||
| IS_PYMONGO_27 = pymongo.version_tuple[0] == 2 and pymongo.version_tuple[1] == 7 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I change it how @wojcikstefan showed.
mongoengine/connection.py
Outdated
| db = conn[conn_settings['name']] | ||
| # Authenticate if necessary | ||
| if conn_settings['username']: | ||
| if (conn_settings['authentication_mechanism'] == 'MONGODB-X509' and conn_settings['username']) or\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check 'username' only for connections with 'MONGODB-X509' authentication mechanism. For others 'username' and 'password' are checked.
mongoengine/connection.py
Outdated
| source=conn_settings['authentication_source'], | ||
| mechanism=conn_settings['authentication_mechanism'] | ||
| ) | ||
| mechanism=conn_settings['authentication_mechanism']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed newline here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your comments @zeezdev, I think that wee can actually make this code simpler and rely on the defaults that PyMongo itself imposes.
mongoengine/connection.py
Outdated
| from pymongo import MongoReplicaSetClient | ||
| READ_PREFERENCE = False | ||
|
|
||
| if IS_PYMONGO_27: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So in PyMongo v2.7 (https://github.com/mongodb/mongo-python-driver/blob/v2.7/pymongo/mongo_client.py#L372) I see:
mechanism = options.get('authmechanism', 'MONGODB-CR')
and in PyMongo v2.8+ I see:
mechanism = options.get('authmechanism', 'DEFAULT')
It seems that PyMongo v2.7 already defaults to MONGODB-CR and v2.8+ defaults to DEFAULT if authmechanism is not specified. I don't understand then why we have to explicitly specify it in MongoEngine... It seems that it should be enough to not pass it to PyMongo at all unless it was specified in the config. Am I missing something?
mongoengine/connection.py
Outdated
| # Authenticate if necessary | ||
| if conn_settings['username'] and conn_settings['password']: | ||
| if (conn_settings['authentication_mechanism'] == 'MONGODB-X509' and conn_settings['username']) or\ | ||
| (conn_settings['username'] and conn_settings['password']): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could break it down into something more elegant, e.g.:
needs_auth = conn_settings['username'] and (conn_settings['password'] or
conn_settings['authentication_mechanism'] == 'MONGODB-X509')
if needs_auth:
...
mongoengine/connection.py
Outdated
| conn_settings['password'], | ||
| source=conn_settings['authentication_source']) | ||
| source=conn_settings['authentication_source'], | ||
| mechanism=conn_settings['authentication_mechanism']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given my comment above about PyMongo's default behavior, I think that we could default authentication_mechanism to None and then we could modify this code to do:
# include the authentication mechanism in kwargs if specified
auth_kwargs = {'source': conn_settings['authentication_source']}
if conn_settings['authenticaltion_mechanism'] is not None:
auth_kwargs['mechanism'] = conn_settings['authenticaltion_mechanism']
db.authenticate(conn_settings['username'], conn_settings['password'], **kwargs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is a great solution. Thank you!
https://github.com/mongodb/mongo-python-driver/blob/v2.7/pymongo/database.py#L832
Apparently, this is a clear indication of the mechanism in the authenticate method confused me on my first failed commits. And I try to set it explicitly:
https://travis-ci.org/MongoEngine/mongoengine/jobs/142701892#L1483
Please look fixes in my last commit.
|
Thanks @zeezdev, this looks good at a glance! Can you confirm that this branch works in a real life scenario with X.509 auth? I'll merge this when you confirm. Thanks for your contribution! |
|
I use the MongoEngine in one real commercial project. How can I show it? But I wrote about this problem in my article about "building mongodb ShardedCluster with X.509 authentication" (see last paragraph). I am sorry it is not in English: https://habrahabr.ru/post/308740/ ...sample code from the article: import ssl
from mongoengine import DEFAULT_CONNECTION_NAME, register_connection
db_hosts="server1.cluster.com:27017,server2.cluster.com:27017"
db_port=None
ssl_config = {
'ssl': True,
'ssl_certfile': "/path_to_certs/analyticsuser.PEM",
'ssl_cert_reqs': ssl.CERT_REQUIRED,
'ssl_ca_certs': "/path_to_certs/mongodb-CA-cert.crt",
}
register_connection(alias=DEFAULT_CONNECTION_NAME,
name="statistic",
host=db_hosts,
port=db_port,
username="CN=username,OU=StatisticsClient,O=SomeSystems,L=Moscow,ST=MoscowRegion,C=RU",
password=None,
read_preference=ReadPreference.NEAREST,
authentication_source="$external",
authentication_mechanism="MONGODB-X509",
**ssl_config)I will be happy if this will merged and appeared in official version. |
|
Thanks! What I meant when I asked you for confirmation is simply to say "yes, I ran this branch of mongoengine against a running mongod process and I was successfully able to authenticate via X.509". It seems that you have, is that correct? |
|
Yes. (but small remark: connection to mongodb cluster performed by means mongos process not mongod). Thanks! |
|
Thanks @zeezdev ! |
It is necessary to work with the mongodb-cluster and support authentication X.509, or other non-default mechamism.
This change is