pymongo-migrate icon indicating copy to clipboard operation
pymongo-migrate copied to clipboard

WIP: Quote credentials (as per pymongo documentation)

Open AndreLouisCaron opened this issue 4 years ago • 6 comments

When using a password tin the pymongo URI such that the password contains special characters, authentication with MongoDB fails.

The pymongo documentation explains how to escape these special characters (see https://pymongo.readthedocs.io/en/stable/examples/authentication.html).

The password could technically be escaped directly in the URI on the command-line, but this is very awkward and it would be quite convenient if pymongo-migrate could do this automatically.

AndreLouisCaron avatar Jan 16 '21 03:01 AndreLouisCaron

isn't this inherently broken? special characters needed encoding are : / ? # [ ] @ so how urllib was supposed to parse such mangled URI in the first place? for example: mongodb://[email protected]@P/a/ss?word/db

seems like it would be better to allow providing username/password as optional args

rooterkyberian avatar Jan 17 '21 04:01 rooterkyberian

The password could technically be escaped directly in the URI on the command-line, but this is very awkward and it would be quite convenient if pymongo-migrate could do this automatically.

pymongo-migrate cannot do this automatically because it would double encode credentials that are already encoded, thus invalidating them.

Also, urllib.parse.urlparse will not work as expected on an URL with a "unescaped" password.

You must provide a valid (i.e. already escaped) URL on the command line.

pawel-slowik avatar Jan 17 '21 22:01 pawel-slowik

@rooterkyberian Thanks for the feedback!

isn't this inherently broken? special characters needed encoding are : / ? # [ ] @ so how urllib was supposed to parse such mangled URI in the first place?

Indeed, it is. I encountered this while writing tests after pushing this (very prematurely :-). It's most obvious with # because everything after that interpreted as the fragment.

seems like it would be better to allow providing username/password as optional args

Yeah, I think so.

AndreLouisCaron avatar Jan 18 '21 01:01 AndreLouisCaron

@pawel-slowik Thanks for the feedback!

pymongo-migrate cannot do this automatically because it would double encode credentials that are already encoded, thus invalidating them

Indeed, urllib.parse.quote_plus() is not idempotent and it's not possible to detect if it has already been applied. And clearly, it's now obvious that I cannot "fix" the password when it's passed to pymongo-migrate in the URI because an unescaped password creates an invalid URI anyways.

Are you open to other approaches for accepting and encoding credentials such as a dedicated command-line arguments or environment variables for the username and password? Or should I take your comment to mean that you think pymongo-migrate should not try to encode credentials at all?

AndreLouisCaron avatar Jan 18 '21 01:01 AndreLouisCaron

While I don't think is essential, I'm open to any solution as long as its not too complicated or solves more problems than this particular corner case.

# solution with user/password args
pymongo-migrate migrate -u 'mongodb://localhost/test_db' --user User --password Password -m tests/migrations
# solution with URL generator - a pymongo-migrate-url script which only purpose is to construct mongodb connection URL
pymongo-migrate migrate -u "$(pymongo-migrate-url mongodb://localhost/test_db --user User --password Password)" -m tests/migrations
# solution with Config file in which we can have a support for extra arguments, such as user/password
pymongo-migrate migrate --config pymongo-migrate.ini
# solution where we just ignore this problem and force anyone encountering it to do encode it manually

One could argue that transmitting user/password through either ENVs or command line options is dangerous. We could try to kill two birds with one stone here. "Safe" options to provide application with credentials are:

  • prompt
  • config file - this is little bit less secure, but overall it can be useful for everyone

rooterkyberian avatar Jan 22 '21 01:01 rooterkyberian

OK, I'll try to whip up another implementation based on what you proposed.

FWIW, I'm running pymongo-migrate in a Kubernetes job to which all secrets are passed as environment variables. My current workaround for this issue is roughly equivalent to your URL generator script:

# solution with URL generator - a pymongo-migrate-url script which only purpose is to construct mongodb connection URL
pymongo-migrate migrate -u "$(pymongo-migrate-url mongodb://localhost/test_db --user User --password Password)" -m tests/migrations

However, I would actually prefer the option of invoking pymongo-migrate directly as it allows more control in the job specification (in contrast with baking a shell script with the above snippet into the Docker image).

I'll update this PR with a solution based on this using with environment variables and we can discuss it again once I get that working:

# solution with user/password args
pymongo-migrate migrate -u 'mongodb://localhost/test_db' --user User --password Password -m tests/migrations

AndreLouisCaron avatar Jan 22 '21 16:01 AndreLouisCaron