Skip to content

Conversation

@zeezdev
Copy link
Contributor

@zeezdev zeezdev commented Jul 6, 2016

It is necessary to work with the mongodb-cluster and support authentication X.509, or other non-default mechamism.


This change is Reviewable

Copy link
Member

@wojcikstefan wojcikstefan left a 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.

from pymongo import MongoReplicaSetClient
READ_PREFERENCE = False

if IS_PYMONGO_27:
Copy link
Member

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"?

Copy link
Contributor Author

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

Copy link
Member

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?

source=conn_settings['authentication_source'])
source=conn_settings['authentication_source'],
mechanism=conn_settings['authentication_mechanism']
)
Copy link
Member

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']:
Copy link
Member

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?

Copy link
Contributor Author

@zeezdev zeezdev Nov 30, 2016

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.

import pymongo


IS_PYMONGO_27 = False
Copy link
Member

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

Stanislav Tomilov added 2 commits November 30, 2016 16:30
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
Copy link
Contributor Author

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.

db = conn[conn_settings['name']]
# Authenticate if necessary
if conn_settings['username']:
if (conn_settings['authentication_mechanism'] == 'MONGODB-X509' and conn_settings['username']) or\
Copy link
Contributor Author

@zeezdev zeezdev Nov 30, 2016

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.

source=conn_settings['authentication_source'],
mechanism=conn_settings['authentication_mechanism']
)
mechanism=conn_settings['authentication_mechanism'])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed newline here.

Copy link
Member

@wojcikstefan wojcikstefan left a 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.

from pymongo import MongoReplicaSetClient
READ_PREFERENCE = False

if IS_PYMONGO_27:
Copy link
Member

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?

# 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']):
Copy link
Member

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:
    ...

conn_settings['password'],
source=conn_settings['authentication_source'])
source=conn_settings['authentication_source'],
mechanism=conn_settings['authentication_mechanism'])
Copy link
Member

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)

Copy link
Contributor Author

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.

@wojcikstefan
Copy link
Member

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!

@zeezdev
Copy link
Contributor Author

zeezdev commented Dec 2, 2016

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.

@wojcikstefan
Copy link
Member

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?

@zeezdev
Copy link
Contributor Author

zeezdev commented Dec 3, 2016

Yes. (but small remark: connection to mongodb cluster performed by means mongos process not mongod). Thanks!

@wojcikstefan wojcikstefan merged commit 02fb3b9 into MongoEngine:master Dec 3, 2016
@wojcikstefan
Copy link
Member

Thanks @zeezdev !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants