This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new f3065a7  [database] Improve form and API validation for invalid URI 
(#8240)
f3065a7 is described below

commit f3065a763f34d00dc5da6b5c5fc1c13d3f6095d0
Author: Daniel Vaz Gaspar <[email protected]>
AuthorDate: Mon Sep 23 16:28:08 2019 +0100

    [database] Improve form and API validation for invalid URI (#8240)
    
    * [database] Improve form and API validation for invalid URI
    
    * [database] Added missing EOL
    
    * [database] lint
---
 superset/views/database/__init__.py | 29 +++++++++++++++++++++++++++--
 superset/views/database/api.py      |  3 ++-
 superset/views/database/views.py    | 13 ++++++++++++-
 3 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/superset/views/database/__init__.py 
b/superset/views/database/__init__.py
index 9b7c8cf..4673644 100644
--- a/superset/views/database/__init__.py
+++ b/superset/views/database/__init__.py
@@ -16,10 +16,14 @@
 # under the License.
 # pylint: disable=C,R,W
 import inspect
+from typing import Type
 
 from flask import Markup
 from flask_babel import lazy_gettext as _
+from marshmallow import ValidationError
 from sqlalchemy import MetaData
+from sqlalchemy.engine.url import make_url
+from sqlalchemy.exc import ArgumentError
 
 from superset import security_manager
 from superset.exceptions import SupersetException
@@ -27,6 +31,24 @@ from superset.utils import core as utils
 from superset.views.base import SupersetFilter
 
 
+def sqlalchemy_uri_validator(
+    uri: str, exception: Type[ValidationError] = ValidationError
+) -> None:
+    """
+    Check if a user has submitted a valid SQLAlchemy URI
+    """
+    try:
+        make_url(uri.strip())
+    except ArgumentError:
+        raise exception(
+            _(
+                "Invalid connnection string, a valid string follows: "
+                " 'DRIVER://USER:PASSWORD@DB-HOST/DATABASE-NAME'"
+                " 
<p>Example:'postgresql://user:password@your-postgres-db/database'</p>"
+            )
+        )
+
+
 class DatabaseFilter(SupersetFilter):
     def apply(self, query, func):  # noqa
         if security_manager.all_database_access():
@@ -189,7 +211,7 @@ class DatabaseMixin:  # noqa
         "backend": _("Backend"),
     }
 
-    def pre_add(self, db):
+    def _pre_add_update(self, db):
         self.check_extra(db)
         db.set_sqlalchemy_uri(db.sqlalchemy_uri)
         security_manager.add_permission_view_menu("database_access", db.perm)
@@ -199,8 +221,11 @@ class DatabaseMixin:  # noqa
                 "schema_access", security_manager.get_schema_perm(db, schema)
             )
 
+    def pre_add(self, db):
+        self._pre_add_update(db)
+
     def pre_update(self, db):
-        self.pre_add(db)
+        self._pre_add_update(db)
 
     def pre_delete(self, obj):
         if obj.tables:
diff --git a/superset/views/database/api.py b/superset/views/database/api.py
index 352086e..fbfbc5b 100644
--- a/superset/views/database/api.py
+++ b/superset/views/database/api.py
@@ -19,7 +19,7 @@ from flask_appbuilder.models.sqla.interface import 
SQLAInterface
 
 from superset import appbuilder
 import superset.models.core as models
-from . import DatabaseFilter, DatabaseMixin
+from . import DatabaseFilter, DatabaseMixin, sqlalchemy_uri_validator
 
 
 class DatabaseRestApi(DatabaseMixin, ModelRestApi):
@@ -53,6 +53,7 @@ class DatabaseRestApi(DatabaseMixin, ModelRestApi):
     ]
     # Removes the local limit for the page size
     max_page_size = -1
+    validators_columns = {"sqlalchemy_uri": sqlalchemy_uri_validator}
 
 
 appbuilder.add_api(DatabaseRestApi)
diff --git a/superset/views/database/views.py b/superset/views/database/views.py
index 19fe490..0c616d1 100644
--- a/superset/views/database/views.py
+++ b/superset/views/database/views.py
@@ -19,18 +19,21 @@ import os
 
 from flask import flash, redirect
 from flask_appbuilder import SimpleFormView
+from flask_appbuilder.forms import DynamicForm
 from flask_appbuilder.models.sqla.interface import SQLAInterface
 from flask_babel import gettext as __
 from flask_babel import lazy_gettext as _
 from sqlalchemy.exc import IntegrityError
 from werkzeug.utils import secure_filename
+from wtforms.fields import StringField
+from wtforms.validators import ValidationError
 
 from superset import app, appbuilder, security_manager
 from superset.connectors.sqla.models import SqlaTable
 import superset.models.core as models
 from superset.utils import core as utils
 from superset.views.base import DeleteMixin, SupersetModelView, YamlExportMixin
-from . import DatabaseMixin
+from . import DatabaseMixin, sqlalchemy_uri_validator
 from .forms import CsvToDatabaseForm
 
 
@@ -38,6 +41,13 @@ config = app.config
 stats_logger = config.get("STATS_LOGGER")
 
 
+def sqlalchemy_uri_form_validator(form: DynamicForm, field: StringField) -> 
None:
+    """
+        Check if user has submitted a valid SQLAlchemy URI
+    """
+    sqlalchemy_uri_validator(field.data, exception=ValidationError)
+
+
 class DatabaseView(
     DatabaseMixin, SupersetModelView, DeleteMixin, YamlExportMixin
 ):  # noqa
@@ -45,6 +55,7 @@ class DatabaseView(
 
     add_template = "superset/models/database/add.html"
     edit_template = "superset/models/database/edit.html"
+    validators_columns = {"sqlalchemy_uri": [sqlalchemy_uri_form_validator]}
 
     def _delete(self, pk):
         DeleteMixin._delete(self, pk)

Reply via email to