--- /dev/null
+.. change::
+ :tags: bug, documentation, mysql
+ :tickets: 5397
+
+ Added support for the ``ssl_check_hostname=`` parameter in mysql connection
+ URIs and updated the mysql dialect documentation regarding secure
+ connections. Original pull request courtesy of Jerry Zhao.
Please see :ref:`mysql_unicode` for current recommendations on unicode
handling.
+.. _mysqldb_ssl:
+
+SSL Connections
+----------------
+
+The mysqlclient and PyMySQL DBAPIs accept an additional dictionary under the
+key "ssl", which may be specified using the
+:paramref:`_sa.create_engine.connect_args` dictionary::
+
+ engine = create_engine(
+ "mysql+mysqldb://scott:tiger@192.168.0.134/test",
+ connect_args={
+ "ssl": {
+ "ssl_ca": "/home/gord/client-ssl/ca.pem",
+ "ssl_cert": "/home/gord/client-ssl/client-cert.pem",
+ "ssl_key": "/home/gord/client-ssl/client-key.pem"
+ }
+ }
+ )
+
+For convenience, the following keys may also be specified inline within the URL
+where they will be interpreted into the "ssl" dictionary automatically:
+"ssl_ca", "ssl_cert", "ssl_key", "ssl_capath", "ssl_cipher",
+"ssl_check_hostname". An example is as follows::
+
+ connection_uri = (
+ "mysql+mysqldb://scott:tiger@192.168.0.134/test"
+ "?ssl_ca=/home/gord/client-ssl/ca.pem"
+ "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
+ "&ssl_key=/home/gord/client-ssl/client-key.pem"
+ )
+
+If the server uses an automatically-generated certificate that is self-signed
+or does not match the host name (as seen from the client), it may also be
+necessary to indicate ``ssl_check_hostname=false``::
+
+ connection_uri = (
+ "mysql+pymysql://scott:tiger@192.168.0.134/test"
+ "?ssl_ca=/home/gord/client-ssl/ca.pem"
+ "&ssl_cert=/home/gord/client-ssl/client-cert.pem"
+ "&ssl_key=/home/gord/client-ssl/client-key.pem"
+ "&ssl_check_hostname=false"
+ )
+
+
+.. seealso::
+
+ :ref:`pymysql_ssl` in the PyMySQL dialect
+
Using MySQLdb with Google Cloud SQL
-----------------------------------
# query string.
ssl = {}
- keys = ["ssl_ca", "ssl_key", "ssl_cert", "ssl_capath", "ssl_cipher"]
- for key in keys:
+ keys = [
+ ("ssl_ca", str),
+ ("ssl_key", str),
+ ("ssl_cert", str),
+ ("ssl_capath", str),
+ ("ssl_cipher", str),
+ ("ssl_check_hostname", bool),
+ ]
+ for key, kw_type in keys:
if key in opts:
ssl[key[4:]] = opts[key]
- util.coerce_kw_type(ssl, key[4:], str)
+ util.coerce_kw_type(ssl, key[4:], kw_type)
del opts[key]
if ssl:
opts["ssl"] = ssl
Please see :ref:`mysql_unicode` for current recommendations on unicode
handling.
+.. _pymysql_ssl:
+
+SSL Connections
+------------------
+
+The PyMySQL DBAPI accepts the same SSL arguments as that of MySQLdb,
+described at :ref:`mysqldb_ssl`. See that section for examples.
+
+
MySQL-Python Compatibility
--------------------------
error = getattr(dbapi, exc_cls_name)(arg0, message)
eq_(dialect.is_disconnect(error, None, None), is_disconnect)
- def test_ssl_arguments_mysqldb(self):
- from sqlalchemy.dialects.mysql import mysqldb
-
- dialect = mysqldb.dialect()
- self._test_ssl_arguments(dialect)
-
- def test_ssl_arguments_oursql(self):
- from sqlalchemy.dialects.mysql import oursql
-
- dialect = oursql.dialect()
- self._test_ssl_arguments(dialect)
-
- def _test_ssl_arguments(self, dialect):
- kwarg = dialect.create_connect_args(
- make_url(
- "mysql://scott:tiger@localhost:3306/test"
- "?ssl_ca=/ca.pem&ssl_cert=/cert.pem&ssl_key=/key.pem"
- )
- )[1]
- # args that differ among mysqldb and oursql
+ @testing.combinations(
+ ("mysqldb"),
+ ("pymysql"),
+ ("oursql"),
+ id_="s",
+ argnames="driver_name",
+ )
+ def test_ssl_arguments(self, driver_name):
+ url = (
+ "mysql+%s://scott:tiger@localhost:3306/test"
+ "?ssl_ca=/ca.pem&ssl_cert=/cert.pem&ssl_key=/key.pem" % driver_name
+ )
+ url_obj = make_url(url)
+ dialect = url_obj.get_dialect()()
+
+ expected = {
+ "{}".format(
+ "password" if driver_name == "pymysql" else "passwd"
+ ): "tiger",
+ "{}".format(
+ "database" if driver_name == "pymysql" else "db"
+ ): "test",
+ "ssl": {"ca": "/ca.pem", "cert": "/cert.pem", "key": "/key.pem"},
+ "host": "localhost",
+ "user": "scott",
+ "port": 3306,
+ }
+ # add check_hostname check for mysqldb and pymysql
+ if driver_name in ["mysqldb", "pymysql"]:
+ url = url + "&ssl_check_hostname=false"
+ expected["ssl"]["check_hostname"] = False
+
+ kwarg = dialect.create_connect_args(make_url(url))[1]
+ # args that differ between oursql and others
for k in ("use_unicode", "found_rows", "client_flag"):
kwarg.pop(k, None)
- eq_(
- kwarg,
- {
- "passwd": "tiger",
- "db": "test",
- "ssl": {
- "ca": "/ca.pem",
- "cert": "/cert.pem",
- "key": "/key.pem",
- },
- "host": "localhost",
- "user": "scott",
- "port": 3306,
- },
- )
+ eq_(kwarg, expected)
@testing.combinations(
("compress", True),