diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-11-24 18:11:37 -0500 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2013-11-24 18:11:37 -0500 |
| commit | 2800e34710672b408fa4a7bdd6d58d63a7128f04 (patch) | |
| tree | a2108d949abe6cc97b8766b4b73022be30bb3318 | |
| parent | 579f09974b4813f35fe0c701bbf678d5eccc4aeb (diff) | |
| download | sqlalchemy-2800e34710672b408fa4a7bdd6d58d63a7128f04.tar.gz | |
- The :func:`.create_engine` routine and the related
:func:`.make_url` function **no longer URL encode the password**.
Database passwords that include characters like spaces, plus signs
and anything else should now represent these characters directly,
without any URL escaping. [ticket:2873]
| -rw-r--r-- | doc/build/changelog/changelog_09.rst | 15 | ||||
| -rw-r--r-- | doc/build/changelog/migration_09.rst | 43 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/url.py | 9 | ||||
| -rw-r--r-- | test/engine/test_parseconnect.py | 24 |
4 files changed, 78 insertions, 13 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index f0d58f205..571f11b2f 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -15,6 +15,21 @@ :version: 0.9.0b2 .. change:: + :tags: bug, engine + :tickets: 2873 + + The :func:`.create_engine` routine and the related + :func:`.make_url` function **no longer URL encode the password**. + Database passwords that include characters like spaces, plus signs + and anything else should now represent these characters directly, + without any URL escaping. + + .. seealso:: + + :ref:`migration_2873` + + + .. change:: :tags: bug, orm :tickets: 2872 diff --git a/doc/build/changelog/migration_09.rst b/doc/build/changelog/migration_09.rst index 4e9112f87..2d490e912 100644 --- a/doc/build/changelog/migration_09.rst +++ b/doc/build/changelog/migration_09.rst @@ -9,7 +9,7 @@ What's New in SQLAlchemy 0.9? and SQLAlchemy version 0.9, which is expected for release in late 2013. - Document last updated: October 23, 2013 + Document last updated: November 24, 2013 Introduction ============ @@ -18,9 +18,8 @@ This guide introduces what's new in SQLAlchemy version 0.9, and also documents changes which affect users migrating their applications from the 0.8 series of SQLAlchemy to 0.9. -Version 0.9 is a faster-than-usual push from version 0.8, -featuring a more versatile codebase with regards to modern -Python versions. See :ref:`behavioral_changes_09` for +Please carefully review +:ref:`behavioral_changes_orm_09` and :ref:`behavioral_changes_core_09` for potentially backwards-incompatible changes. Platform Support @@ -47,7 +46,7 @@ in both Python 2 and Python 3 environments. :ticket:`2161` -.. _behavioral_changes_09: +.. _behavioral_changes_orm_09: Behavioral Changes - ORM ======================== @@ -468,10 +467,44 @@ This is a small change demonstrated as follows:: :ticket:`2787` +.. _behavioral_changes_core_09: Behavioral Changes - Core ========================= +.. _migration_2873: + +The "password" portion of a ``create_engine()`` URL is no longer URL encoded +---------------------------------------------------------------------------- + +For whatever reason, the Python function ``unquote_plus()`` was applied to the +"password" field of a URL, likely as a means of allowing the usage of escapes +(e.g. "%2F" or similar) to be used, and perhaps as some way of allowing spaces +to be present. However, this is not complaint with `RFC 1738 <http://www.ietf.org/rfc/rfc1738.txt>`_ +which has no reserved characters within the password field and does not specify +URL quoting - so the quote_plus routines are **no longer applied** to the password +field. + +Examples of URLs with characters such as colons, @ symbols, spaces, and plus signs +include:: + + # password: "pass word + other:words" + dbtype://user:pass word + other:words@host/dbname + + # password: "apples%2Foranges" + dbtype://username:apples%2Foranges@hostspec/database + + # password: "apples@oranges@@" + dbtype://username:apples@oranges@@@hostspec/database + + # password: '', username is "username@" + dbtype://username@:@hostspec/database + + +:ticket:`2873` + + + .. _migration_2850: A bindparam() construct with no type gets upgraded via copy when a type is available diff --git a/lib/sqlalchemy/engine/url.py b/lib/sqlalchemy/engine/url.py index 8f84ab039..28c15299e 100644 --- a/lib/sqlalchemy/engine/url.py +++ b/lib/sqlalchemy/engine/url.py @@ -67,8 +67,7 @@ class URL(object): if self.username is not None: s += self.username if self.password is not None: - s += ':' + ('***' if hide_password - else util.quote_plus(self.password)) + s += ':' + ('***' if hide_password else self.password) s += "@" if self.host is not None: if ':' in self.host: @@ -170,7 +169,7 @@ def _parse_rfc1738_args(name): (?P<name>[\w\+]+):// (?: (?P<username>[^:/]*) - (?::(?P<password>[^/]*))? + (?::(?P<password>.*))? @)? (?: (?: @@ -195,10 +194,6 @@ def _parse_rfc1738_args(name): query = None components['query'] = query - if components['password'] is not None: - components['password'] = \ - util.unquote_plus(components['password']) - ipv4host = components.pop('ipv4host') ipv6host = components.pop('ipv6host') components['host'] = ipv4host or ipv6host diff --git a/test/engine/test_parseconnect.py b/test/engine/test_parseconnect.py index 07ce96d5e..d1ffe426d 100644 --- a/test/engine/test_parseconnect.py +++ b/test/engine/test_parseconnect.py @@ -31,7 +31,7 @@ class ParseConnectTest(fixtures.TestBase): 'dbtype://', 'dbtype://username:password@/database', 'dbtype:////usr/local/_xtest@example.com/members.db', - 'dbtype://username:apples%2Foranges@hostspec/database', + 'dbtype://username:apples/oranges@hostspec/database', 'dbtype://username:password@[2001:da8:2004:1000:202:116:160:90]/database?foo=bar', 'dbtype://username:password@[2001:da8:2004:1000:202:116:160:90]:80/database?foo=bar' ): @@ -49,6 +49,28 @@ class ParseConnectTest(fixtures.TestBase): 'E:/work/src/LEM/db/hello.db', None), u.database eq_(str(u), text) + def test_rfc1738_password(self): + u = url.make_url("dbtype://user:pass word + other:words@host/dbname") + eq_(u.password, "pass word + other:words") + eq_(str(u), "dbtype://user:pass word + other:words@host/dbname") + + u = url.make_url('dbtype://username:apples%2Foranges@hostspec/database') + eq_(u.password, "apples%2Foranges") + eq_(str(u), 'dbtype://username:apples%2Foranges@hostspec/database') + + u = url.make_url('dbtype://username:apples@oranges@@@hostspec/database') + eq_(u.password, "apples@oranges@@") + eq_(str(u), 'dbtype://username:apples@oranges@@@hostspec/database') + + u = url.make_url('dbtype://username@:@hostspec/database') + eq_(u.password, '') + eq_(u.username, "username@") + eq_(str(u), 'dbtype://username@:@hostspec/database') + + u = url.make_url('dbtype://username:pass/word@hostspec/database') + eq_(u.password, 'pass/word') + eq_(str(u), 'dbtype://username:pass/word@hostspec/database') + class DialectImportTest(fixtures.TestBase): def test_import_base_dialects(self): |
