]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- The :func:`.create_engine` routine and the related
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 24 Nov 2013 23:11:37 +0000 (18:11 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 24 Nov 2013 23:11:37 +0000 (18:11 -0500)
: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]

doc/build/changelog/changelog_09.rst
doc/build/changelog/migration_09.rst
lib/sqlalchemy/engine/url.py
test/engine/test_parseconnect.py

index f0d58f205abd089c4c28e774aadf86d3ceb46d17..571f11b2f9fbfbad6f78c295e7b6bc046192824c 100644 (file)
 .. changelog::
     :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
index 4e9112f871aaad3a8d25d654be6688fecbe5f869..2d490e9122a52bf87a3ab6488021ff70a22f7b9a 100644 (file)
@@ -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
index 8f84ab0394dcda25d7e30a720f71a9147b161631..28c15299e502a500d62ed004f35f480ac7d05900 100644 (file)
@@ -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
index 07ce96d5e06f033cf3556c4168ad636722d93db9..d1ffe426d6e623ac413ce467375caf975e6f923f 100644 (file)
@@ -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):