Skip to content
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

let username and password be specified outside a uri #731

Closed
wants to merge 12 commits into from

Conversation

ThisGuyCodes
Copy link

currently, if you use a uri without the username/password/db name inside it, ie:
mongodb://host1.com:1337,host2.com:1337/
it will pull the db name from the other args passed to connet():
'name': uri_dict.get('database') or name,
but the username and password specified in connect() will be overwritten with None:

'password': uri_dict.get('password'), # <---Returns None if no password was in the uri
'password': uri_dict.get('password'),

This forces anyone using a uri, say to make handling multiple nodes easier, to put their username and password into the uri. This isn't a huge problem, but forces some awkward manual string formatting in some setups, and there's no need for that.

I also removed a duplicate read_preference update.

Review on Reviewable

since parse_uri() returns None for values not specified in the uri, 'name' was allowed to be specified outside the uri. This commit allows the same for username and password.
read_preference was already put in the conn_settings dict earlier, and the version placed here is unchanged, remove it.
@yograterol
Copy link
Contributor

@conslo good, can you add a test case please for that case?

@yograterol
Copy link
Contributor

ping @conslo

@ThisGuyCodes
Copy link
Author

@yograterol sorry, been busy, I should have this done later today or tomorrow

This setting was being removed from the dict it’s being compared to,
but not it. This would cause any connection_setting with an
authentication_source set to always cause new connections to be made.
Explanation of that slightly cryptic loop through settings when
establishing a connection
@coveralls
Copy link

Coverage Status

Coverage increased (+0.04%) when pulling 8b0ae25 on conslo:master into bce7ca7 on MongoEngine:master.

@@ -54,9 +54,8 @@ def register_connection(alias, name=None, host=None, port=None,
uri_dict = uri_parser.parse_uri(conn_settings['host'])
conn_settings.update({
'name': uri_dict.get('database') or name,
'username': uri_dict.get('username'),
'password': uri_dict.get('password'),
'read_preference': read_preference,
Copy link
Author

Choose a reason for hiding this comment

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

this was redundant, as it gets overriden later anyway

# work fine, but there would be no username/password on the
# connection.
self.assertEqual(me_connection._connection_settings['test_uri_no_username']['username'], 'username')
self.assertEqual(me_connection._connection_settings['test_uri_no_username']['password'], 'password')
Copy link
Author

Choose a reason for hiding this comment

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

I really don't like doing it this way. If anyone knows how to check the authenticated username on a connection (or anything else that solves this problem) please let me know?

@ThisGuyCodes
Copy link
Author

I also added some comments to explain some stuff in get_connection(). I'm at least 95% sure that I'm correct in my assumption, but it could use a look by someone more experienced with the project.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.04%) when pulling 974faad on conslo:master into bce7ca7 on MongoEngine:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.04%) when pulling 974faad on conslo:master into bce7ca7 on MongoEngine:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.04%) when pulling 974faad on conslo:master into bce7ca7 on MongoEngine:master.

Conflicts:
	mongoengine/connection.py
connection_settings.pop('name', None)
connection_settings.pop('username', None)
connection_settings.pop('password', None)
if conn_settings == connection_settings and _connections.get(db_alias, None):
connection = _connections[db_alias]
connection_settings.pop('authentication_source', None)
Copy link
Author

Choose a reason for hiding this comment

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

comment got removed due to merge, but I feel it's worth being visible:
this was popped off the setting we were comparing, but not within the loop. This would cause any settings with an authentication_source set to equate as not equal every time.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.04%) when pulling bbcb97f on conslo:master into 6942f9c on MongoEngine:master.

@MRigal
Copy link
Member

MRigal commented Dec 10, 2014

@conslo have you seen that the tests are not passing ? You should maybe explain the count difference.

Also, don't you think that the possibility to connect without adding username and password could introduce a security hole ?

@ThisGuyCodes
Copy link
Author

Sorry, this totally fell off my radar. I haven't looked into it but at first glance it looks like there's a merge conflict, I'll reserve some cycles this week.

As for the security hole; I don't understand what you mean. It's up to the database to require or not require a username/password, not the connection library (you can have a mongo connection without them, in fact this is the default). What I'm doing here is allowing you to use a URI string but specify the username and password outside the URI.

@MRigal
Copy link
Member

MRigal commented Dec 10, 2014

Right, I misunderstood the purpose. But then I have difficulties to see the improvement, especially if one anyway has to "initiate" the connection with username and password a first time.

@ThisGuyCodes
Copy link
Author

I'll outline the case at my work where this came up to illustrate the reasoning.
We do not commit secrets to our repository, instead we have a setup like:

__init__.py
local.py
production.py

local.py is untracked in git, and contains lines like:
MONGO_USERNAME = "xxx"

production.py is tracked in git, and contains lines like:
MONGO_USERNAME = os.environ.get('MONGO_USERNAME', None)

Then __init__.py looks like this:

from .production import *
try:
    from .local import *
except ImportError:
    pass

Doing this allows us to put configuration in the environment on production, but still keep development values locally without constant re-setup.

In order to specify multiple databases (we have two: a master and slave, with auto-failover) you need to use the URI, ie:
MONGO_URI = "mongodb://host1.com:1337,host2.com:1337/"
But this won't work right now, as register_connection('foo', host=MONGO_URI, username=USERNAME, password=PASSWORD) with this URI will attempt a connection without a username and password. This is because if the host kwarg is a URI, it over-writes the username and password with the username and password pulled from the URI (which in this case are both None as I didn't put a username or password in the URI).

(I'm going to take this too far now, but as I was writing this it started getting humorous so I kept going)
I could put the username and password in the URI; but this is annoying to deal with. Imagine for instance you have n connections with different permissions (username + password combinations), but all use the same database. I also could do everything MongoEngine does manually with PyMongo, and I could do everything PyMongo does by manually reading and writing to/from a TCP socket. And I could do everything Python does with BF (or gods forbid, C). Just because there's already a way to get it done (we call these 'work arounds'), doesn't mean it's not something that should be fixed.

@MRigal
Copy link
Member

MRigal commented Dec 11, 2014

@conslo ok I've understood the case and it makes sense. Just aside, I think that using a replica set and connecting via mongos (and therefore simplifying the connection parameters) could be more adapted to your setup, but that is out of scope ;-)

Anyway to get a chance to get the pull request merged, you should fix the tests not passing. I don't know if you noticed that 'test_multiple_connection_settings' is not passing anymore.

In this case, it is maybe really expected to get only one connection anymore with your fix. But then you should change this test and IMHO also write a new one with really different settings and an assert that we do have 2 distinct connections.

@ThisGuyCodes
Copy link
Author

@MRigal could you elaborate on 'connecting via mongos'? I'm not totally fluent on mongo connection handling, but from what a quick set of googles tell me using this multi-host connection URI is using a replica set: doc. Save for this ?replicaSet=xxx option we're not using. If I understand correctly this option allows the driver to pull the location of the other instances from the DB instead of having them all handed to it? If this is the case that seems wonderful: but the documentation does still say When connecting to a replica set it is important to give a seed list of at least two mongod instances.

@ThisGuyCodes
Copy link
Author

and yes I see that, I'll have to dig into it to understand if this is a failure or a change in behavior and change things accordingly.

@MRigal
Copy link
Member

MRigal commented Apr 29, 2015

@conslo ping for the failed tests. I think the ground idea is quite good, but I also think the current logic is not completely right.
Since we have release 0.9 and we will release a new version soon, it may be a good time to work on it. Also a rebase would be good.

Regarding the connection credentials, what I was meaning is that the way MongoDB recommends to have the servers and the connections, for optimised security is a bit different from what you are using.
Instead of a master/slave, it should be a replica set (http://docs.mongodb.org/manual/core/master-slave/#convert-master-slave-to-replica-set), with at least an arbiter on another machine, and then, with a mongos instance on each machine you are using to connect to, you would not have these problems anymore of having to define a list of servers, since you connect to a replica set, which knows itself about the composing servers.

@wojcikstefan
Copy link
Member

See #1421

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

Successfully merging this pull request may close these issues.

5 participants