You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by jo...@apache.org on 2018/03/27 23:46:06 UTC

[incubator-superset] branch master updated: [security] Refactor security code into SupersetSecurityManager (#4565)

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

johnbodley 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 8dd052d  [security] Refactor security code into SupersetSecurityManager (#4565)
8dd052d is described below

commit 8dd052de4bbd8176f78318e3beb81526b1ecddd4
Author: timifasubaa <30...@users.noreply.github.com>
AuthorDate: Tue Mar 27 16:46:02 2018 -0700

    [security] Refactor security code into SupersetSecurityManager (#4565)
    
    * move access permissions methods to security manager
    
    * consolidate all security methods into SupersetSecurityManager
    
    * update security method calls
    
    * update calls from tests
    
    * move get_or_create_main_db to utils
    
    * raise if supersetsecuritymanager is not extended
    
    * rename sm to security_manager
---
 UPDATING.MD                         |   8 +
 superset/__init__.py                |  14 +-
 superset/cli.py                     |   6 +-
 superset/connectors/druid/models.py |   8 +-
 superset/connectors/druid/views.py  |  12 +-
 superset/connectors/sqla/models.py  |   6 +-
 superset/connectors/sqla/views.py   |  10 +-
 superset/data/__init__.py           |  27 +--
 superset/models/core.py             |  13 +-
 superset/models/helpers.py          |   4 +-
 superset/models/sql_lab.py          |   6 +-
 superset/security.py                | 458 ++++++++++++++++++++++--------------
 superset/sql_lab.py                 |   5 +-
 superset/utils.py                   |  32 ++-
 superset/views/base.py              | 145 +-----------
 superset/views/core.py              |  70 +++---
 tests/access_tests.py               | 161 ++++++-------
 tests/base_tests.py                 |  66 +++---
 tests/celery_tests.py               |  11 +-
 tests/core_tests.py                 |  22 +-
 tests/druid_tests.py                |  19 +-
 tests/security_tests.py             |  57 ++---
 tests/sqllab_tests.py               |  24 +-
 23 files changed, 599 insertions(+), 585 deletions(-)

diff --git a/UPDATING.MD b/UPDATING.MD
new file mode 100644
index 0000000..7fb3e85
--- /dev/null
+++ b/UPDATING.MD
@@ -0,0 +1,8 @@
+# Updating Superset
+
+This file documents any backwards-incompatible changes in Superset and
+assists people when migrating to a new version.
+
+## Superset 0.23.0
+
+* [4565](https://github.com/apache/incubator-superset/pull/4565)
\ No newline at end of file
diff --git a/superset/__init__.py b/superset/__init__.py
index d420b76..83154ca 100644
--- a/superset/__init__.py
+++ b/superset/__init__.py
@@ -18,8 +18,9 @@ from flask_migrate import Migrate
 from flask_wtf.csrf import CSRFProtect
 from werkzeug.contrib.fixers import ProxyFix
 
+from superset import config, utils
 from superset.connectors.connector_registry import ConnectorRegistry
-from superset import utils, config  # noqa
+from superset.security import SupersetSecurityManager
 
 APP_DIR = os.path.dirname(__file__)
 CONFIG_MODULE = os.environ.get('SUPERSET_CONFIG', 'superset.config')
@@ -149,16 +150,23 @@ class MyIndexView(IndexView):
         return redirect('/superset/welcome')
 
 
+custom_sm = app.config.get('CUSTOM_SECURITY_MANAGER') or SupersetSecurityManager
+if not issubclass(custom_sm, SupersetSecurityManager):
+    raise Exception(
+        """Your CUSTOM_SECURITY_MANAGER must now extend SupersetSecurityManager,
+         not FAB's security manager.
+         See [4565] in UPDATING.md""")
+
 appbuilder = AppBuilder(
     app,
     db.session,
     base_template='superset/base.html',
     indexview=MyIndexView,
-    security_manager_class=app.config.get('CUSTOM_SECURITY_MANAGER'),
+    security_manager_class=custom_sm,
     update_perms=utils.get_update_perms_flag(),
 )
 
-sm = appbuilder.sm
+security_manager = appbuilder.sm
 
 results_backend = app.config.get('RESULTS_BACKEND')
 
diff --git a/superset/cli.py b/superset/cli.py
index 95f6df7..b146877 100755
--- a/superset/cli.py
+++ b/superset/cli.py
@@ -16,7 +16,7 @@ from flask_script import Manager
 from pathlib2 import Path
 import yaml
 
-from superset import app, db, dict_import_export_util, security, utils
+from superset import app, data, db, dict_import_export_util, security_manager, utils
 
 config = app.config
 celery_app = utils.get_celery_app(config)
@@ -28,7 +28,8 @@ manager.add_command('db', MigrateCommand)
 @manager.command
 def init():
     """Inits the Superset application"""
-    security.sync_role_definitions()
+    utils.get_or_create_main_db()
+    security_manager.sync_role_definitions()
 
 
 @manager.option(
@@ -108,7 +109,6 @@ def version(verbose):
     help='Load additional test data')
 def load_examples(load_test_data):
     """Loads a set of Slices and Dashboards and a supporting dataset """
-    from superset import data
     print('Loading examples into {}'.format(db))
 
     data.load_css_templates()
diff --git a/superset/connectors/druid/models.py b/superset/connectors/druid/models.py
index f4f137e..107e3c8 100644
--- a/superset/connectors/druid/models.py
+++ b/superset/connectors/druid/models.py
@@ -33,7 +33,7 @@ from sqlalchemy import (
 )
 from sqlalchemy.orm import backref, relationship
 
-from superset import conf, db, import_util, sm, utils
+from superset import conf, db, import_util, security_manager, utils
 from superset.connectors.base.models import BaseColumn, BaseDatasource, BaseMetric
 from superset.exceptions import MetricPermException
 from superset.models.helpers import (
@@ -465,7 +465,7 @@ class DruidDatasource(Model, BaseDatasource):
         'DruidCluster', backref='datasources', foreign_keys=[cluster_name])
     user_id = Column(Integer, ForeignKey('ab_user.id'))
     owner = relationship(
-        sm.user_model,
+        security_manager.user_model,
         backref=backref('datasources', cascade='all, delete-orphan'),
         foreign_keys=[user_id])
     UniqueConstraint('cluster_name', 'datasource_name')
@@ -506,7 +506,7 @@ class DruidDatasource(Model, BaseDatasource):
     @property
     def schema_perm(self):
         """Returns schema permission if present, cluster one otherwise."""
-        return utils.get_schema_perm(self.cluster, self.schema)
+        return security_manager.get_schema_perm(self.cluster, self.schema)
 
     def get_perm(self):
         return (
@@ -980,7 +980,7 @@ class DruidDatasource(Model, BaseDatasource):
             m.metric_name for m in self.metrics
             if m.is_restricted and
             m.metric_name in aggregations.keys() and
-            not sm.has_access('metric_access', m.perm)
+            not security_manager.has_access('metric_access', m.perm)
         ]
         if rejected_metrics:
             raise MetricPermException(
diff --git a/superset/connectors/druid/views.py b/superset/connectors/druid/views.py
index f4a2089..02b8cb4 100644
--- a/superset/connectors/druid/views.py
+++ b/superset/connectors/druid/views.py
@@ -14,7 +14,7 @@ from flask_appbuilder.models.sqla.interface import SQLAInterface
 from flask_babel import gettext as __
 from flask_babel import lazy_gettext as _
 
-from superset import appbuilder, db, security, sm, utils
+from superset import appbuilder, db, security_manager, utils
 from superset.connectors.base.views import DatasourceModelView
 from superset.connectors.connector_registry import ConnectorRegistry
 from superset.utils import has_access
@@ -140,11 +140,11 @@ class DruidMetricInlineView(CompactCRUDMixin, SupersetModelView):  # noqa
 
     def post_add(self, metric):
         if metric.is_restricted:
-            security.merge_perm(sm, 'metric_access', metric.get_perm())
+            security_manager.merge_perm('metric_access', metric.get_perm())
 
     def post_update(self, metric):
         if metric.is_restricted:
-            security.merge_perm(sm, 'metric_access', metric.get_perm())
+            security_manager.merge_perm('metric_access', metric.get_perm())
 
 
 appbuilder.add_view_no_menu(DruidMetricInlineView)
@@ -177,7 +177,7 @@ class DruidClusterModelView(SupersetModelView, DeleteMixin, YamlExportMixin):  #
     }
 
     def pre_add(self, cluster):
-        security.merge_perm(sm, 'database_access', cluster.perm)
+        security_manager.merge_perm('database_access', cluster.perm)
 
     def pre_update(self, cluster):
         self.pre_add(cluster)
@@ -278,9 +278,9 @@ class DruidDatasourceModelView(DatasourceModelView, DeleteMixin, YamlExportMixin
 
     def post_add(self, datasource):
         datasource.refresh_metrics()
-        security.merge_perm(sm, 'datasource_access', datasource.get_perm())
+        security_manager.merge_perm('datasource_access', datasource.get_perm())
         if datasource.schema:
-            security.merge_perm(sm, 'schema_access', datasource.schema_perm)
+            security_manager.merge_perm('schema_access', datasource.schema_perm)
 
     def post_update(self, datasource):
         self.post_add(datasource)
diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py
index ef8c68e..e2dca32 100644
--- a/superset/connectors/sqla/models.py
+++ b/superset/connectors/sqla/models.py
@@ -24,7 +24,7 @@ from sqlalchemy.sql import column, literal_column, table, text
 from sqlalchemy.sql.expression import TextAsFrom
 import sqlparse
 
-from superset import db, import_util, sm, utils
+from superset import db, import_util, security_manager, utils
 from superset.connectors.base.models import BaseColumn, BaseDatasource, BaseMetric
 from superset.jinja_context import get_template_processor
 from superset.models.annotations import Annotation
@@ -259,7 +259,7 @@ class SqlaTable(Model, BaseDatasource):
     fetch_values_predicate = Column(String(1000))
     user_id = Column(Integer, ForeignKey('ab_user.id'))
     owner = relationship(
-        sm.user_model,
+        security_manager.user_model,
         backref='tables',
         foreign_keys=[user_id])
     database = relationship(
@@ -298,7 +298,7 @@ class SqlaTable(Model, BaseDatasource):
     @property
     def schema_perm(self):
         """Returns schema permission if present, database one otherwise."""
-        return utils.get_schema_perm(self.database, self.schema)
+        return security_manager.get_schema_perm(self.database, self.schema)
 
     def get_perm(self):
         return (
diff --git a/superset/connectors/sqla/views.py b/superset/connectors/sqla/views.py
index b667475..16a218f 100644
--- a/superset/connectors/sqla/views.py
+++ b/superset/connectors/sqla/views.py
@@ -13,7 +13,7 @@ from flask_babel import gettext as __
 from flask_babel import lazy_gettext as _
 from past.builtins import basestring
 
-from superset import appbuilder, db, security, sm, utils
+from superset import appbuilder, db, security_manager, utils
 from superset.connectors.base.views import DatasourceModelView
 from superset.utils import has_access
 from superset.views.base import (
@@ -144,11 +144,11 @@ class SqlMetricInlineView(CompactCRUDMixin, SupersetModelView):  # noqa
 
     def post_add(self, metric):
         if metric.is_restricted:
-            security.merge_perm(sm, 'metric_access', metric.get_perm())
+            security_manager.merge_perm('metric_access', metric.get_perm())
 
     def post_update(self, metric):
         if metric.is_restricted:
-            security.merge_perm(sm, 'metric_access', metric.get_perm())
+            security_manager.merge_perm('metric_access', metric.get_perm())
 
 
 appbuilder.add_view_no_menu(SqlMetricInlineView)
@@ -253,9 +253,9 @@ class TableModelView(DatasourceModelView, DeleteMixin, YamlExportMixin):  # noqa
 
     def post_add(self, table, flash_message=True):
         table.fetch_metadata()
-        security.merge_perm(sm, 'datasource_access', table.get_perm())
+        security_manager.merge_perm('datasource_access', table.get_perm())
         if table.schema:
-            security.merge_perm(sm, 'schema_access', table.schema_perm)
+            security_manager.merge_perm('schema_access', table.schema_perm)
 
         if flash_message:
             flash(_(
diff --git a/superset/data/__init__.py b/superset/data/__init__.py
index ec87994..9f505c1 100644
--- a/superset/data/__init__.py
+++ b/superset/data/__init__.py
@@ -16,10 +16,9 @@ from sqlalchemy import BigInteger, Date, DateTime, Float, String, Text
 import geohash
 import polyline
 
-from superset import app, db, utils
+from superset import app, db, security_manager, utils
 from superset.connectors.connector_registry import ConnectorRegistry
 from superset.models import core as models
-from superset.security import get_or_create_main_db
 
 # Shortcuts
 DB = models.Database
@@ -71,7 +70,7 @@ def load_energy():
     if not tbl:
         tbl = TBL(table_name=tbl_name)
     tbl.description = "Energy consumption"
-    tbl.database = get_or_create_main_db()
+    tbl.database = utils.get_or_create_main_db()
     db.session.merge(tbl)
     db.session.commit()
     tbl.fetch_metadata()
@@ -179,7 +178,7 @@ def load_world_bank_health_n_pop():
         tbl = TBL(table_name=tbl_name)
     tbl.description = utils.readfile(os.path.join(DATA_FOLDER, 'countries.md'))
     tbl.main_dttm_col = 'year'
-    tbl.database = get_or_create_main_db()
+    tbl.database = utils.get_or_create_main_db()
     tbl.filter_select_enabled = True
     db.session.merge(tbl)
     db.session.commit()
@@ -583,7 +582,7 @@ def load_birth_names():
     if not obj:
         obj = TBL(table_name='birth_names')
     obj.main_dttm_col = 'ds'
-    obj.database = get_or_create_main_db()
+    obj.database = utils.get_or_create_main_db()
     obj.filter_select_enabled = True
     db.session.merge(obj)
     db.session.commit()
@@ -870,7 +869,7 @@ def load_unicode_test_data():
     if not obj:
         obj = TBL(table_name='unicode_test')
     obj.main_dttm_col = 'dttm'
-    obj.database = get_or_create_main_db()
+    obj.database = utils.get_or_create_main_db()
     db.session.merge(obj)
     db.session.commit()
     obj.fetch_metadata()
@@ -948,7 +947,7 @@ def load_random_time_series_data():
     if not obj:
         obj = TBL(table_name='random_time_series')
     obj.main_dttm_col = 'ds'
-    obj.database = get_or_create_main_db()
+    obj.database = utils.get_or_create_main_db()
     db.session.merge(obj)
     db.session.commit()
     obj.fetch_metadata()
@@ -1011,7 +1010,7 @@ def load_country_map_data():
     if not obj:
         obj = TBL(table_name='birth_france_by_region')
     obj.main_dttm_col = 'dttm'
-    obj.database = get_or_create_main_db()
+    obj.database = utils.get_or_create_main_db()
     db.session.merge(obj)
     db.session.commit()
     obj.fetch_metadata()
@@ -1086,7 +1085,7 @@ def load_long_lat_data():
     if not obj:
         obj = TBL(table_name='long_lat')
     obj.main_dttm_col = 'datetime'
-    obj.database = get_or_create_main_db()
+    obj.database = utils.get_or_create_main_db()
     db.session.merge(obj)
     db.session.commit()
     obj.fetch_metadata()
@@ -1147,7 +1146,7 @@ def load_multiformat_time_series_data():
     if not obj:
         obj = TBL(table_name='multiformat_time_series')
     obj.main_dttm_col = 'ds'
-    obj.database = get_or_create_main_db()
+    obj.database = utils.get_or_create_main_db()
     dttm_and_expr_dict = {
         'ds': [None, None],
         'ds2': [None, None],
@@ -1769,7 +1768,7 @@ def load_flights():
     if not tbl:
         tbl = TBL(table_name=tbl_name)
     tbl.description = "Random set of flights in the US"
-    tbl.database = get_or_create_main_db()
+    tbl.database = utils.get_or_create_main_db()
     db.session.merge(tbl)
     db.session.commit()
     tbl.fetch_metadata()
@@ -1800,7 +1799,7 @@ def load_paris_iris_geojson():
     if not tbl:
         tbl = TBL(table_name=tbl_name)
     tbl.description = "Map of Paris"
-    tbl.database = get_or_create_main_db()
+    tbl.database = utils.get_or_create_main_db()
     db.session.merge(tbl)
     db.session.commit()
     tbl.fetch_metadata()
@@ -1830,7 +1829,7 @@ def load_sf_population_polygons():
     if not tbl:
         tbl = TBL(table_name=tbl_name)
     tbl.description = "Population density of San Francisco"
-    tbl.database = get_or_create_main_db()
+    tbl.database = utils.get_or_create_main_db()
     db.session.merge(tbl)
     db.session.commit()
     tbl.fetch_metadata()
@@ -1860,7 +1859,7 @@ def load_bart_lines():
     if not tbl:
         tbl = TBL(table_name=tbl_name)
     tbl.description = "BART lines"
-    tbl.database = get_or_create_main_db()
+    tbl.database = utils.get_or_create_main_db()
     db.session.merge(tbl)
     db.session.commit()
     tbl.fetch_metadata()
diff --git a/superset/models/core.py b/superset/models/core.py
index cd7cc44..c32ac5c 100644
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -33,7 +33,7 @@ from sqlalchemy.sql import text
 from sqlalchemy.sql.expression import TextAsFrom
 from sqlalchemy_utils import EncryptedType
 
-from superset import app, db, db_engine_specs, sm, utils
+from superset import app, db, db_engine_specs, security_manager, utils
 from superset.connectors.connector_registry import ConnectorRegistry
 from superset.models.helpers import AuditMixinNullable, ImportMixin, set_perm
 from superset.viz import viz_types
@@ -104,7 +104,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
     description = Column(Text)
     cache_timeout = Column(Integer)
     perm = Column(String(1000))
-    owners = relationship(sm.user_model, secondary=slice_user)
+    owners = relationship(security_manager.user_model, secondary=slice_user)
 
     export_fields = ('slice_name', 'datasource_type', 'datasource_name',
                      'viz_type', 'params', 'cache_timeout')
@@ -322,7 +322,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
     slug = Column(String(255), unique=True)
     slices = relationship(
         'Slice', secondary=dashboard_slices, backref='dashboards')
-    owners = relationship(sm.user_model, secondary=dashboard_user)
+    owners = relationship(security_manager.user_model, secondary=dashboard_user)
 
     export_fields = ('dashboard_title', 'position_json', 'json_metadata',
                      'description', 'css', 'slug')
@@ -681,7 +681,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
         DB_CONNECTION_MUTATOR = config.get('DB_CONNECTION_MUTATOR')
         if DB_CONNECTION_MUTATOR:
             url, params = DB_CONNECTION_MUTATOR(
-                url, params, effective_username, sm)
+                url, params, effective_username, security_manager)
         return create_engine(url, **params)
 
     def get_reserved_words(self):
@@ -862,7 +862,8 @@ class Log(Model):
     dashboard_id = Column(Integer)
     slice_id = Column(Integer)
     json = Column(Text)
-    user = relationship(sm.user_model, backref='logs', foreign_keys=[user_id])
+    user = relationship(
+        security_manager.user_model, backref='logs', foreign_keys=[user_id])
     dttm = Column(DateTime, default=datetime.utcnow)
     dt = Column(Date, default=date.today())
     duration_ms = Column(Integer)
@@ -961,7 +962,7 @@ class DatasourceAccessRequest(Model, AuditMixinNullable):
     def roles_with_datasource(self):
         action_list = ''
         perm = self.datasource.perm  # pylint: disable=no-member
-        pv = sm.find_permission_view_menu('datasource_access', perm)
+        pv = security_manager.find_permission_view_menu('datasource_access', perm)
         for r in pv.role:
             if r.name in self.ROLES_BLACKLIST:
                 continue
diff --git a/superset/models/helpers.py b/superset/models/helpers.py
index db395c1..718550b 100644
--- a/superset/models/helpers.py
+++ b/superset/models/helpers.py
@@ -20,7 +20,7 @@ from sqlalchemy.ext.declarative import declared_attr
 from sqlalchemy.orm.exc import MultipleResultsFound
 import yaml
 
-from superset import sm
+from superset import security_manager
 from superset.utils import QueryStatus
 
 
@@ -353,4 +353,4 @@ def set_perm(mapper, connection, target):  # noqa
         )
 
     # add to view menu if not already exists
-    merge_perm(sm, 'datasource_access', target.get_perm(), connection)
+    merge_perm(security_manager, 'datasource_access', target.get_perm(), connection)
diff --git a/superset/models/sql_lab.py b/superset/models/sql_lab.py
index 81ee41a..5dfd8a6 100644
--- a/superset/models/sql_lab.py
+++ b/superset/models/sql_lab.py
@@ -17,7 +17,7 @@ from sqlalchemy import (
 )
 from sqlalchemy.orm import backref, relationship
 
-from superset import sm
+from superset import security_manager
 from superset.models.helpers import AuditMixinNullable
 from superset.utils import QueryStatus, user_label
 
@@ -76,7 +76,7 @@ class Query(Model):
         'Database',
         foreign_keys=[database_id],
         backref=backref('queries', cascade='all, delete-orphan'))
-    user = relationship(sm.user_model, foreign_keys=[user_id])
+    user = relationship(security_manager.user_model, foreign_keys=[user_id])
 
     __table_args__ = (
         sqla.Index('ti_user_id_changed_on', user_id, changed_on),
@@ -138,7 +138,7 @@ class SavedQuery(Model, AuditMixinNullable):
     description = Column(Text)
     sql = Column(Text)
     user = relationship(
-        sm.user_model,
+        security_manager.user_model,
         backref=backref('saved_queries', cascade='all, delete-orphan'),
         foreign_keys=[user_id])
     database = relationship(
diff --git a/superset/security.py b/superset/security.py
index 4d5b9f4..8ea91b9 100644
--- a/superset/security.py
+++ b/superset/security.py
@@ -7,12 +7,13 @@ from __future__ import unicode_literals
 
 import logging
 
+from flask import g
 from flask_appbuilder.security.sqla import models as ab_models
+from flask_appbuilder.security.sqla.manager import SecurityManager
 from sqlalchemy import or_
 
-from superset import conf, db, sm
+from superset import sql_parse
 from superset.connectors.connector_registry import ConnectorRegistry
-from superset.models import core as models
 
 READ_ONLY_MODEL_VIEWS = {
     'DatabaseAsync',
@@ -77,177 +78,282 @@ OBJECT_SPEC_PERMISSIONS = set([
 ])
 
 
-def merge_perm(sm, permission_name, view_menu_name):
-    # Implementation copied from sm.find_permission_view_menu.
-    # TODO: use sm.find_permission_view_menu once issue
-    #       https://github.com/airbnb/superset/issues/1944 is resolved.
-    permission = sm.find_permission(permission_name)
-    view_menu = sm.find_view_menu(view_menu_name)
-    pv = None
-    if permission and view_menu:
-        pv = sm.get_session.query(sm.permissionview_model).filter_by(
-            permission=permission, view_menu=view_menu).first()
-    if not pv and permission_name and view_menu_name:
-        sm.add_permission_view_menu(permission_name, view_menu_name)
-
-
-def is_user_defined_permission(perm):
-    return perm.permission.name in OBJECT_SPEC_PERMISSIONS
-
-
-def get_or_create_main_db():
-    logging.info('Creating database reference')
-    dbobj = (
-        db.session.query(models.Database)
-        .filter_by(database_name='main')
-        .first()
-    )
-    if not dbobj:
-        dbobj = models.Database(database_name='main')
-    dbobj.set_sqlalchemy_uri(conf.get('SQLALCHEMY_DATABASE_URI'))
-    dbobj.expose_in_sqllab = True
-    dbobj.allow_run_sync = True
-    db.session.add(dbobj)
-    db.session.commit()
-    return dbobj
-
-
-def is_admin_only(pvm):
-    # not readonly operations on read only model views allowed only for admins
-    if (pvm.view_menu.name in READ_ONLY_MODEL_VIEWS and
-            pvm.permission.name not in READ_ONLY_PERMISSION):
-        return True
-    return (
-        pvm.view_menu.name in ADMIN_ONLY_VIEW_MENUS or
-        pvm.permission.name in ADMIN_ONLY_PERMISSIONS
-    )
-
-
-def is_alpha_only(pvm):
-    if (pvm.view_menu.name in GAMMA_READ_ONLY_MODEL_VIEWS and
-            pvm.permission.name not in READ_ONLY_PERMISSION):
-        return True
-    return (
-        pvm.view_menu.name in ALPHA_ONLY_VIEW_MENUS or
-        pvm.permission.name in ALPHA_ONLY_PERMISSIONS
-    )
-
-
-def is_admin_pvm(pvm):
-    return not is_user_defined_permission(pvm)
-
-
-def is_alpha_pvm(pvm):
-    return not (is_user_defined_permission(pvm) or is_admin_only(pvm))
-
-
-def is_gamma_pvm(pvm):
-    return not (is_user_defined_permission(pvm) or is_admin_only(pvm) or
-                is_alpha_only(pvm))
-
-
-def is_sql_lab_pvm(pvm):
-    return pvm.view_menu.name in {'SQL Lab'} or pvm.permission.name in {
-        'can_sql_json', 'can_csv', 'can_search_queries',
-    }
-
-
-def is_granter_pvm(pvm):
-    return pvm.permission.name in {
-        'can_override_role_permissions', 'can_approve',
-    }
-
-
-def set_role(role_name, pvm_check):
-    logging.info('Syncing {} perms'.format(role_name))
-    sesh = sm.get_session()
-    pvms = sesh.query(ab_models.PermissionView).all()
-    pvms = [p for p in pvms if p.permission and p.view_menu]
-    role = sm.add_role(role_name)
-    role_pvms = [p for p in pvms if pvm_check(p)]
-    role.permissions = role_pvms
-    sesh.merge(role)
-    sesh.commit()
-
-
-def create_custom_permissions():
-    # Global perms
-    merge_perm(sm, 'all_datasource_access', 'all_datasource_access')
-    merge_perm(sm, 'all_database_access', 'all_database_access')
-
-
-def create_missing_perms():
-    """Creates missing perms for datasources, schemas and metrics"""
-
-    logging.info(
-        'Fetching a set of all perms to lookup which ones are missing')
-    all_pvs = set()
-    for pv in sm.get_session.query(sm.permissionview_model).all():
-        if pv.permission and pv.view_menu:
-            all_pvs.add((pv.permission.name, pv.view_menu.name))
-
-    def merge_pv(view_menu, perm):
-        """Create permission view menu only if it doesn't exist"""
-        if view_menu and perm and (view_menu, perm) not in all_pvs:
-            merge_perm(sm, view_menu, perm)
-
-    logging.info('Creating missing datasource permissions.')
-    datasources = ConnectorRegistry.get_all_datasources(db.session)
-    for datasource in datasources:
-        merge_pv('datasource_access', datasource.get_perm())
-        merge_pv('schema_access', datasource.schema_perm)
-
-    logging.info('Creating missing database permissions.')
-    databases = db.session.query(models.Database).all()
-    for database in databases:
-        merge_pv('database_access', database.perm)
-
-    logging.info('Creating missing metrics permissions')
-    metrics = []
-    for datasource_class in ConnectorRegistry.sources.values():
-        metrics += list(db.session.query(datasource_class.metric_class).all())
-
-    for metric in metrics:
-        if metric.is_restricted:
-            merge_pv('metric_access', metric.perm)
-
-
-def clean_perms():
-    """FAB leaves faulty permissions that need to be cleaned up"""
-    logging.info('Cleaning faulty perms')
-    sesh = sm.get_session()
-    pvms = (
-        sesh.query(ab_models.PermissionView)
-        .filter(or_(
-            ab_models.PermissionView.permission == None,  # NOQA
-            ab_models.PermissionView.view_menu == None,  # NOQA
-        ))
-    )
-    deleted_count = pvms.delete()
-    sesh.commit()
-    if deleted_count:
-        logging.info('Deleted {} faulty permissions'.format(deleted_count))
-
-
-def sync_role_definitions():
-    """Inits the Superset application with security roles and such"""
-    logging.info('Syncing role definition')
-
-    get_or_create_main_db()
-    create_custom_permissions()
-
-    # Creating default roles
-    set_role('Admin', is_admin_pvm)
-    set_role('Alpha', is_alpha_pvm)
-    set_role('Gamma', is_gamma_pvm)
-    set_role('granter', is_granter_pvm)
-    set_role('sql_lab', is_sql_lab_pvm)
-
-    if conf.get('PUBLIC_ROLE_LIKE_GAMMA', False):
-        set_role('Public', is_gamma_pvm)
-
-    create_missing_perms()
-
-    # commit role and view menu updates
-    sm.get_session.commit()
-    clean_perms()
+class SupersetSecurityManager(SecurityManager):
+
+    def get_schema_perm(self, database, schema):
+        if schema:
+            return '[{}].[{}]'.format(database, schema)
+
+    def can_access(self, permission_name, view_name, user=None):
+        """Protecting from has_access failing from missing perms/view"""
+        if not user:
+            user = g.user
+        if user.is_anonymous():
+            return self.is_item_public(permission_name, view_name)
+        return self._has_view_access(user, permission_name, view_name)
+
+    def all_datasource_access(self, user=None):
+        return self.can_access(
+            'all_datasource_access', 'all_datasource_access', user=user)
+
+    def database_access(self, database, user=None):
+        return (
+            self.can_access(
+                'all_database_access', 'all_database_access', user=user) or
+            self.can_access('database_access', database.perm, user=user)
+        )
+
+    def schema_access(self, datasource, user=None):
+        return (
+            self.database_access(datasource.database, user=user) or
+            self.all_datasource_access(user=user) or
+            self.can_access('schema_access', datasource.schema_perm, user=user)
+        )
+
+    def datasource_access(self, datasource, user=None):
+        return (
+            self.schema_access(datasource, user=user) or
+            self.can_access('datasource_access', datasource.perm, user=user)
+        )
+
+    def datasource_access_by_name(
+            self, database, datasource_name, schema=None):
+        from superset import db
+
+        if self.database_access(database) or self.all_datasource_access():
+            return True
+
+        schema_perm = self.get_schema_perm(database, schema)
+        if schema and self.can_access('schema_access', schema_perm):
+            return True
+
+        datasources = ConnectorRegistry.query_datasources_by_name(
+            db.session, database, datasource_name, schema=schema)
+        for datasource in datasources:
+            if self.can_access('datasource_access', datasource.perm):
+                return True
+        return False
+
+    def datasource_access_by_fullname(
+            self, database, full_table_name, schema):
+        table_name_pieces = full_table_name.split('.')
+        if len(table_name_pieces) == 2:
+            table_schema = table_name_pieces[0]
+            table_name = table_name_pieces[1]
+        else:
+            table_schema = schema
+            table_name = table_name_pieces[0]
+        return self.datasource_access_by_name(
+            database, table_name, schema=table_schema)
+
+    def rejected_datasources(self, sql, database, schema):
+        superset_query = sql_parse.SupersetQuery(sql)
+        return [
+            t for t in superset_query.tables if not
+            self.datasource_access_by_fullname(database, t, schema)]
+
+    def user_datasource_perms(self):
+        datasource_perms = set()
+        for r in g.user.roles:
+            for perm in r.permissions:
+                if (
+                        perm.permission and
+                        'datasource_access' == perm.permission.name):
+                    datasource_perms.add(perm.view_menu.name)
+        return datasource_perms
+
+    def schemas_accessible_by_user(self, database, schemas):
+        from superset import db
+        from superset.connectors.sqla.models import SqlaTable
+        if self.database_access(database) or self.all_datasource_access():
+            return schemas
+
+        subset = set()
+        for schema in schemas:
+            schema_perm = self.get_schema_perm(database, schema)
+            if self.can_access('schema_access', schema_perm):
+                subset.add(schema)
+
+        perms = self.user_datasource_perms()
+        if perms:
+            tables = (
+                db.session.query(SqlaTable)
+                .filter(
+                    SqlaTable.perm.in_(perms),
+                    SqlaTable.database_id == database.id,
+                )
+                .all()
+            )
+            for t in tables:
+                if t.schema:
+                    subset.add(t.schema)
+        return sorted(list(subset))
+
+    def accessible_by_user(self, database, datasource_names, schema=None):
+        from superset import db
+        if self.database_access(database) or self.all_datasource_access():
+            return datasource_names
+
+        if schema:
+            schema_perm = self.get_schema_perm(database, schema)
+            if self.can_access('schema_access', schema_perm):
+                return datasource_names
+
+        user_perms = self.user_datasource_perms()
+        user_datasources = ConnectorRegistry.query_datasources_by_permissions(
+            db.session, database, user_perms)
+        if schema:
+            names = {
+                d.table_name
+                for d in user_datasources if d.schema == schema}
+            return [d for d in datasource_names if d in names]
+        else:
+            full_names = {d.full_name for d in user_datasources}
+            return [d for d in datasource_names if d in full_names]
+
+    def merge_perm(self, permission_name, view_menu_name):
+        # Implementation copied from sm.find_permission_view_menu.
+        # TODO: use sm.find_permission_view_menu once issue
+        #       https://github.com/airbnb/superset/issues/1944 is resolved.
+        permission = self.find_permission(permission_name)
+        view_menu = self.find_view_menu(view_menu_name)
+        pv = None
+        if permission and view_menu:
+            pv = self.get_session.query(self.permissionview_model).filter_by(
+                permission=permission, view_menu=view_menu).first()
+        if not pv and permission_name and view_menu_name:
+            self.add_permission_view_menu(permission_name, view_menu_name)
+
+    def is_user_defined_permission(self, perm):
+        return perm.permission.name in OBJECT_SPEC_PERMISSIONS
+
+    def create_custom_permissions(self):
+        # Global perms
+        self.merge_perm('all_datasource_access', 'all_datasource_access')
+        self.merge_perm('all_database_access', 'all_database_access')
+
+    def create_missing_perms(self):
+        """Creates missing perms for datasources, schemas and metrics"""
+        from superset import db
+        from superset.models import core as models
+
+        logging.info(
+            'Fetching a set of all perms to lookup which ones are missing')
+        all_pvs = set()
+        for pv in self.get_session.query(self.permissionview_model).all():
+            if pv.permission and pv.view_menu:
+                all_pvs.add((pv.permission.name, pv.view_menu.name))
+
+        def merge_pv(view_menu, perm):
+            """Create permission view menu only if it doesn't exist"""
+            if view_menu and perm and (view_menu, perm) not in all_pvs:
+                self.merge_perm(view_menu, perm)
+
+        logging.info('Creating missing datasource permissions.')
+        datasources = ConnectorRegistry.get_all_datasources(db.session)
+        for datasource in datasources:
+            merge_pv('datasource_access', datasource.get_perm())
+            merge_pv('schema_access', datasource.schema_perm)
+
+        logging.info('Creating missing database permissions.')
+        databases = db.session.query(models.Database).all()
+        for database in databases:
+            merge_pv('database_access', database.perm)
+
+        logging.info('Creating missing metrics permissions')
+        metrics = []
+        for datasource_class in ConnectorRegistry.sources.values():
+            metrics += list(db.session.query(datasource_class.metric_class).all())
+
+        for metric in metrics:
+            if metric.is_restricted:
+                merge_pv('metric_access', metric.perm)
+
+    def clean_perms(self):
+        """FAB leaves faulty permissions that need to be cleaned up"""
+        logging.info('Cleaning faulty perms')
+        sesh = self.get_session
+        pvms = (
+            sesh.query(ab_models.PermissionView)
+            .filter(or_(
+                ab_models.PermissionView.permission == None,  # NOQA
+                ab_models.PermissionView.view_menu == None,  # NOQA
+            ))
+        )
+        deleted_count = pvms.delete()
+        sesh.commit()
+        if deleted_count:
+            logging.info('Deleted {} faulty permissions'.format(deleted_count))
+
+    def sync_role_definitions(self):
+        """Inits the Superset application with security roles and such"""
+        from superset import conf
+        logging.info('Syncing role definition')
+
+        self.create_custom_permissions()
+
+        # Creating default roles
+        self.set_role('Admin', self.is_admin_pvm)
+        self.set_role('Alpha', self.is_alpha_pvm)
+        self.set_role('Gamma', self.is_gamma_pvm)
+        self.set_role('granter', self.is_granter_pvm)
+        self.set_role('sql_lab', self.is_sql_lab_pvm)
+
+        if conf.get('PUBLIC_ROLE_LIKE_GAMMA', False):
+            self.set_role('Public', self.is_gamma_pvm)
+
+        self.create_missing_perms()
+
+        # commit role and view menu updates
+        self.get_session.commit()
+        self.clean_perms()
+
+    def set_role(self, role_name, pvm_check):
+        logging.info('Syncing {} perms'.format(role_name))
+        sesh = self.get_session
+        pvms = sesh.query(ab_models.PermissionView).all()
+        pvms = [p for p in pvms if p.permission and p.view_menu]
+        role = self.add_role(role_name)
+        role_pvms = [p for p in pvms if pvm_check(p)]
+        role.permissions = role_pvms
+        sesh.merge(role)
+        sesh.commit()
+
+    def is_admin_only(self, pvm):
+        # not readonly operations on read only model views allowed only for admins
+        if (pvm.view_menu.name in READ_ONLY_MODEL_VIEWS and
+                pvm.permission.name not in READ_ONLY_PERMISSION):
+            return True
+        return (
+            pvm.view_menu.name in ADMIN_ONLY_VIEW_MENUS or
+            pvm.permission.name in ADMIN_ONLY_PERMISSIONS
+        )
+
+    def is_alpha_only(self, pvm):
+        if (pvm.view_menu.name in GAMMA_READ_ONLY_MODEL_VIEWS and
+                pvm.permission.name not in READ_ONLY_PERMISSION):
+            return True
+        return (
+            pvm.view_menu.name in ALPHA_ONLY_VIEW_MENUS or
+            pvm.permission.name in ALPHA_ONLY_PERMISSIONS
+        )
+
+    def is_admin_pvm(self, pvm):
+        return not self.is_user_defined_permission(pvm)
+
+    def is_alpha_pvm(self, pvm):
+        return not (self.is_user_defined_permission(pvm) or self.is_admin_only(pvm))
+
+    def is_gamma_pvm(self, pvm):
+        return not (self.is_user_defined_permission(pvm) or self.is_admin_only(pvm) or
+                    self.is_alpha_only(pvm))
+
+    def is_sql_lab_pvm(self, pvm):
+        return pvm.view_menu.name in {'SQL Lab'} or pvm.permission.name in {
+            'can_sql_json', 'can_csv', 'can_search_queries',
+        }
+
+    def is_granter_pvm(self, pvm):
+        return pvm.permission.name in {
+            'can_override_role_permissions', 'can_approve',
+        }
diff --git a/superset/sql_lab.py b/superset/sql_lab.py
index d28de7c..c13addf 100644
--- a/superset/sql_lab.py
+++ b/superset/sql_lab.py
@@ -17,7 +17,7 @@ import sqlalchemy
 from sqlalchemy.orm import sessionmaker
 from sqlalchemy.pool import NullPool
 
-from superset import app, dataframe, db, results_backend, sm, utils
+from superset import app, dataframe, db, results_backend, security_manager, utils
 from superset.db_engine_specs import LimitMethod
 from superset.jinja_context import get_template_processor
 from superset.models.sql_lab import Query
@@ -196,7 +196,8 @@ def execute_sql(
     # Hook to allow environment-specific mutation (usually comments) to the SQL
     SQL_QUERY_MUTATOR = config.get('SQL_QUERY_MUTATOR')
     if SQL_QUERY_MUTATOR:
-        executed_sql = SQL_QUERY_MUTATOR(executed_sql, user_name, sm, database)
+        executed_sql = SQL_QUERY_MUTATOR(
+            executed_sql, user_name, security_manager, database)
 
     query.executed_sql = executed_sql
     query.status = QueryStatus.RUNNING
diff --git a/superset/utils.py b/superset/utils.py
index 78fb12b..8e91e8d 100644
--- a/superset/utils.py
+++ b/superset/utils.py
@@ -55,13 +55,6 @@ EPOCH = datetime(1970, 1, 1)
 DTTM_ALIAS = '__timestamp'
 
 
-def can_access(sm, permission_name, view_name, user):
-    """Protecting from has_access failing from missing perms/view"""
-    if user.is_anonymous():
-        return sm.is_item_public(permission_name, view_name)
-    return sm._has_view_access(user, permission_name, view_name)
-
-
 def flasher(msg, severity=None):
     """Flask's flash if available, logging call if not"""
     try:
@@ -477,11 +470,6 @@ def get_datasource_full_name(database_name, datasource_name, schema=None):
     return '[{}].[{}].[{}]'.format(database_name, schema, datasource_name)
 
 
-def get_schema_perm(database, schema):
-    if schema:
-        return '[{}].[{}]'.format(database, schema)
-
-
 def validate_json(obj):
     if obj:
         try:
@@ -834,3 +822,23 @@ def user_label(user):
             return user.first_name + ' ' + user.last_name
         else:
             return user.username
+
+
+def get_or_create_main_db():
+    from superset import conf, db
+    from superset.models import core as models
+
+    logging.info('Creating database reference')
+    dbobj = (
+        db.session.query(models.Database)
+        .filter_by(database_name='main')
+        .first()
+    )
+    if not dbobj:
+        dbobj = models.Database(database_name='main')
+    dbobj.set_sqlalchemy_uri(conf.get('SQLALCHEMY_DATABASE_URI'))
+    dbobj.expose_in_sqllab = True
+    dbobj.allow_run_sync = True
+    db.session.add(dbobj)
+    db.session.commit()
+    return dbobj
diff --git a/superset/views/base.py b/superset/views/base.py
index 54a98c2..328789f 100644
--- a/superset/views/base.py
+++ b/superset/views/base.py
@@ -20,9 +20,7 @@ from flask_babel import gettext as __
 from flask_babel import lazy_gettext as _
 import yaml
 
-from superset import appbuilder, conf, db, sm, sql_parse, utils
-from superset.connectors.connector_registry import ConnectorRegistry
-from superset.connectors.sqla.models import SqlaTable
+from superset import conf, security_manager, utils
 from superset.translations.utils import get_language_pack
 
 FRONTEND_CONF_KEYS = (
@@ -83,131 +81,11 @@ def get_datasource_exist_error_mgs(full_name):
 def get_user_roles():
     if g.user.is_anonymous():
         public_role = conf.get('AUTH_ROLE_PUBLIC')
-        return [appbuilder.sm.find_role(public_role)] if public_role else []
+        return [security_manager.find_role(public_role)] if public_role else []
     return g.user.roles
 
 
 class BaseSupersetView(BaseView):
-    def can_access(self, permission_name, view_name, user=None):
-        if not user:
-            user = g.user
-        return utils.can_access(
-            appbuilder.sm, permission_name, view_name, user)
-
-    def all_datasource_access(self, user=None):
-        return self.can_access(
-            'all_datasource_access', 'all_datasource_access', user=user)
-
-    def database_access(self, database, user=None):
-        return (
-            self.can_access(
-                'all_database_access', 'all_database_access', user=user) or
-            self.can_access('database_access', database.perm, user=user)
-        )
-
-    def schema_access(self, datasource, user=None):
-        return (
-            self.database_access(datasource.database, user=user) or
-            self.all_datasource_access(user=user) or
-            self.can_access('schema_access', datasource.schema_perm, user=user)
-        )
-
-    def datasource_access(self, datasource, user=None):
-        return (
-            self.schema_access(datasource, user=user) or
-            self.can_access('datasource_access', datasource.perm, user=user)
-        )
-
-    def datasource_access_by_name(
-            self, database, datasource_name, schema=None):
-        if self.database_access(database) or self.all_datasource_access():
-            return True
-
-        schema_perm = utils.get_schema_perm(database, schema)
-        if schema and self.can_access('schema_access', schema_perm):
-            return True
-
-        datasources = ConnectorRegistry.query_datasources_by_name(
-            db.session, database, datasource_name, schema=schema)
-        for datasource in datasources:
-            if self.can_access('datasource_access', datasource.perm):
-                return True
-        return False
-
-    def datasource_access_by_fullname(
-            self, database, full_table_name, schema):
-        table_name_pieces = full_table_name.split('.')
-        if len(table_name_pieces) == 2:
-            table_schema = table_name_pieces[0]
-            table_name = table_name_pieces[1]
-        else:
-            table_schema = schema
-            table_name = table_name_pieces[0]
-        return self.datasource_access_by_name(
-            database, table_name, schema=table_schema)
-
-    def rejected_datasources(self, sql, database, schema):
-        superset_query = sql_parse.SupersetQuery(sql)
-        return [
-            t for t in superset_query.tables if not
-            self.datasource_access_by_fullname(database, t, schema)]
-
-    def user_datasource_perms(self):
-        datasource_perms = set()
-        for r in g.user.roles:
-            for perm in r.permissions:
-                if (
-                        perm.permission and
-                        'datasource_access' == perm.permission.name):
-                    datasource_perms.add(perm.view_menu.name)
-        return datasource_perms
-
-    def schemas_accessible_by_user(self, database, schemas):
-        if self.database_access(database) or self.all_datasource_access():
-            return schemas
-
-        subset = set()
-        for schema in schemas:
-            schema_perm = utils.get_schema_perm(database, schema)
-            if self.can_access('schema_access', schema_perm):
-                subset.add(schema)
-
-        perms = self.user_datasource_perms()
-        if perms:
-            tables = (
-                db.session.query(SqlaTable)
-                .filter(
-                    SqlaTable.perm.in_(perms),
-                    SqlaTable.database_id == database.id,
-                )
-                .all()
-            )
-            for t in tables:
-                if t.schema:
-                    subset.add(t.schema)
-        return sorted(list(subset))
-
-    def accessible_by_user(self, database, datasource_names, schema=None):
-        if self.database_access(database) or self.all_datasource_access():
-            return datasource_names
-
-        if schema:
-            schema_perm = utils.get_schema_perm(database, schema)
-            if self.can_access('schema_access', schema_perm):
-                return datasource_names
-
-        user_perms = self.user_datasource_perms()
-        user_datasources = ConnectorRegistry.query_datasources_by_permissions(
-            db.session, database, user_perms)
-        if schema:
-            names = {
-                d.table_name
-                for d in user_datasources if d.schema == schema}
-            return [d for d in datasource_names if d in names]
-        else:
-            full_names = {d.full_name for d in user_datasources}
-            return [d for d in datasource_names if d in full_names]
-
     def common_bootsrap_payload(self):
         """Common data always sent to the client"""
         messages = get_flashed_messages(with_categories=True)
@@ -274,31 +152,32 @@ class DeleteMixin(object):
         except Exception as e:
             flash(str(e), 'danger')
         else:
-            view_menu = sm.find_view_menu(item.get_perm())
-            pvs = sm.get_session.query(sm.permissionview_model).filter_by(
+            view_menu = security_manager.find_view_menu(item.get_perm())
+            pvs = security_manager.get_session.query(
+                security_manager.permissionview_model).filter_by(
                 view_menu=view_menu).all()
 
             schema_view_menu = None
             if hasattr(item, 'schema_perm'):
-                schema_view_menu = sm.find_view_menu(item.schema_perm)
+                schema_view_menu = security_manager.find_view_menu(item.schema_perm)
 
-                pvs.extend(sm.get_session.query(
-                    sm.permissionview_model).filter_by(
+                pvs.extend(security_manager.get_session.query(
+                    security_manager.permissionview_model).filter_by(
                     view_menu=schema_view_menu).all())
 
             if self.datamodel.delete(item):
                 self.post_delete(item)
 
                 for pv in pvs:
-                    sm.get_session.delete(pv)
+                    security_manager.get_session.delete(pv)
 
                 if view_menu:
-                    sm.get_session.delete(view_menu)
+                    security_manager.get_session.delete(view_menu)
 
                 if schema_view_menu:
-                    sm.get_session.delete(schema_view_menu)
+                    security_manager.get_session.delete(schema_view_menu)
 
-                sm.get_session.commit()
+                security_manager.get_session.commit()
 
             flash(*self.datamodel.message)
             self.update_redirect()
diff --git a/superset/views/core.py b/superset/views/core.py
index a080a4b..f0956e3 100755
--- a/superset/views/core.py
+++ b/superset/views/core.py
@@ -33,7 +33,7 @@ from werkzeug.routing import BaseConverter
 from werkzeug.utils import secure_filename
 
 from superset import (
-    app, appbuilder, cache, db, results_backend, security, sm, sql_lab, utils,
+    app, appbuilder, cache, db, results_backend, security_manager, sql_lab, utils,
     viz,
 )
 from superset.connectors.connector_registry import ConnectorRegistry
@@ -57,7 +57,6 @@ from .utils import bootstrap_user_data
 config = app.config
 stats_logger = config.get('STATS_LOGGER')
 log_this = models.Log.log_this
-can_access = utils.can_access
 DAR = models.DatasourceAccessRequest
 
 
@@ -284,10 +283,10 @@ class DatabaseView(SupersetModelView, DeleteMixin, YamlExportMixin):  # noqa
 
     def pre_add(self, db):
         db.set_sqlalchemy_uri(db.sqlalchemy_uri)
-        security.merge_perm(sm, 'database_access', db.perm)
+        security_manager.merge_perm('database_access', db.perm)
         for schema in db.all_schema_names():
-            security.merge_perm(
-                sm, 'schema_access', utils.get_schema_perm(db, schema))
+            security_manager.merge_perm(
+                'schema_access', security_manager.get_schema_perm(db, schema))
 
     def pre_update(self, db):
         self.pre_add(db)
@@ -809,13 +808,13 @@ class Superset(BaseSupersetView):
         existing_datasources = ConnectorRegistry.get_all_datasources(db.session)
         datasources = [
             d for d in existing_datasources if d.full_name in db_ds_names]
-        role = sm.find_role(role_name)
+        role = security_manager.find_role(role_name)
         # remove all permissions
         role.permissions = []
         # grant permissions to the list of datasources
         granted_perms = []
         for datasource in datasources:
-            view_menu_perm = sm.find_permission_view_menu(
+            view_menu_perm = security_manager.find_permission_view_menu(
                 view_menu_name=datasource.perm,
                 permission_name='datasource_access')
             # prevent creating empty permissions
@@ -854,7 +853,7 @@ class Superset(BaseSupersetView):
 
         has_access = all(
             (
-                datasource and self.datasource_access(datasource)
+                datasource and security_manager.datasource_access(datasource)
                 for datasource in datasources
             ))
         if has_access:
@@ -884,9 +883,9 @@ class Superset(BaseSupersetView):
             for r in session.query(DAR).all():
                 datasource = ConnectorRegistry.get_datasource(
                     r.datasource_type, r.datasource_id, session)
-                user = sm.get_user_by_id(r.created_by_fk)
+                user = security_manager.get_user_by_id(r.created_by_fk)
                 if not datasource or \
-                   self.datasource_access(datasource, user):
+                   security_manager.datasource_access(datasource, user):
                     # datasource does not exist anymore
                     session.delete(r)
             session.commit()
@@ -904,7 +903,7 @@ class Superset(BaseSupersetView):
             flash(DATASOURCE_MISSING_ERR, 'alert')
             return json_error_response(DATASOURCE_MISSING_ERR)
 
-        requested_by = sm.find_user(username=created_by_username)
+        requested_by = security_manager.find_user(username=created_by_username)
         if not requested_by:
             flash(USER_MISSING_ERR, 'alert')
             return json_error_response(USER_MISSING_ERR)
@@ -923,10 +922,10 @@ class Superset(BaseSupersetView):
             return json_error_response(ACCESS_REQUEST_MISSING_ERR)
 
         # check if you can approve
-        if self.all_datasource_access() or g.user.id == datasource.owner_id:
+        if security_manager.all_datasource_access() or g.user.id == datasource.owner_id:
             # can by done by admin only
             if role_to_grant:
-                role = sm.find_role(role_to_grant)
+                role = security_manager.find_role(role_to_grant)
                 requested_by.roles.append(role)
                 msg = __(
                     '%(user)s was granted the role %(role)s that gives access '
@@ -940,10 +939,10 @@ class Superset(BaseSupersetView):
                 flash(msg, 'info')
 
             if role_to_extend:
-                perm_view = sm.find_permission_view_menu(
+                perm_view = security_manager.find_permission_view_menu(
                     'email/datasource_access', datasource.perm)
-                role = sm.find_role(role_to_extend)
-                sm.add_permission_role(role, perm_view)
+                role = security_manager.find_role(role_to_extend)
+                security_manager.add_permission_role(role, perm_view)
                 msg = __('Role %(r)s was extended to provide the access to '
                          'the datasource %(ds)s', r=role_to_extend,
                          ds=datasource.full_name)
@@ -1085,7 +1084,7 @@ class Superset(BaseSupersetView):
                 utils.error_msg_from_exception(e),
                 stacktrace=traceback.format_exc())
 
-        if not self.datasource_access(viz_obj.datasource):
+        if not security_manager.datasource_access(viz_obj.datasource, g.user):
             return json_error_response(DATASOURCE_ACCESS_ERR, status=404)
 
         if csv:
@@ -1245,7 +1244,7 @@ class Superset(BaseSupersetView):
             flash(DATASOURCE_MISSING_ERR, 'danger')
             return redirect(error_redirect)
 
-        if not self.datasource_access(datasource):
+        if not security_manager.datasource_access(datasource):
             flash(
                 __(get_datasource_access_error_msg(datasource.name)),
                 'danger')
@@ -1260,9 +1259,10 @@ class Superset(BaseSupersetView):
             return redirect(datasource.default_endpoint)
 
         # slc perms
-        slice_add_perm = self.can_access('can_add', 'SliceModelView')
+        slice_add_perm = security_manager.can_access('can_add', 'SliceModelView')
         slice_overwrite_perm = is_owner(slc, g.user)
-        slice_download_perm = self.can_access('can_download', 'SliceModelView')
+        slice_download_perm = security_manager.can_access(
+            'can_download', 'SliceModelView')
 
         form_data['datasource'] = str(datasource_id) + '__' + datasource_type
 
@@ -1342,7 +1342,7 @@ class Superset(BaseSupersetView):
             datasource_type, datasource_id, db.session)
         if not datasource:
             return json_error_response(DATASOURCE_MISSING_ERR)
-        if not self.datasource_access(datasource):
+        if not security_manager.datasource_access(datasource):
             return json_error_response(DATASOURCE_ACCESS_ERR)
 
         payload = json.dumps(
@@ -1402,7 +1402,7 @@ class Superset(BaseSupersetView):
                 'info')
         elif request.args.get('add_to_dash') == 'new':
             # check create dashboard permissions
-            dash_add_perm = self.can_access('can_add', 'DashboardModelView')
+            dash_add_perm = security_manager.can_access('can_add', 'DashboardModelView')
             if not dash_add_perm:
                 return json_error_response(
                     _('You don\'t have the rights to ') + _('create a ') + _('dashboard'),
@@ -1501,7 +1501,7 @@ class Superset(BaseSupersetView):
             .one()
         )
         schemas = database.all_schema_names()
-        schemas = self.schemas_accessible_by_user(database, schemas)
+        schemas = security_manager.schemas_accessible_by_user(database, schemas)
         return Response(
             json.dumps({'schemas': schemas}),
             mimetype='application/json')
@@ -1515,9 +1515,9 @@ class Superset(BaseSupersetView):
         schema = utils.js_string_to_python(schema)
         substr = utils.js_string_to_python(substr)
         database = db.session.query(models.Database).filter_by(id=db_id).one()
-        table_names = self.accessible_by_user(
+        table_names = security_manager.accessible_by_user(
             database, database.all_table_names(schema), schema)
-        view_names = self.accessible_by_user(
+        view_names = security_manager.accessible_by_user(
             database, database.all_view_names(schema), schema)
 
         if substr:
@@ -1761,7 +1761,7 @@ class Superset(BaseSupersetView):
     @expose('/fave_dashboards_by_username/<username>/', methods=['GET'])
     def fave_dashboards_by_username(self, username):
         """This lets us use a user's username to pull favourite dashboards"""
-        user = sm.find_user(username=username)
+        user = security_manager.find_user(username=username)
         return self.fave_dashboards(user.get_id())
 
     @api
@@ -2041,7 +2041,7 @@ class Superset(BaseSupersetView):
 
         if config.get('ENABLE_ACCESS_REQUEST'):
             for datasource in datasources:
-                if datasource and not self.datasource_access(datasource):
+                if datasource and not security_manager.datasource_access(datasource):
                     flash(
                         __(get_datasource_access_error_msg(datasource.name)),
                         'danger')
@@ -2057,7 +2057,7 @@ class Superset(BaseSupersetView):
 
         dash_edit_perm = check_ownership(dash, raise_if_false=False)
         dash_save_perm = \
-            dash_edit_perm and self.can_access('can_save_dash', 'Superset')
+            dash_edit_perm and security_manager.can_access('can_save_dash', 'Superset')
 
         standalone_mode = request.args.get('standalone') == 'true'
 
@@ -2108,7 +2108,7 @@ class Superset(BaseSupersetView):
                 metrics_spec: list of metrics (dictionary). Metric consists of
                     2 attributes: type and name. Type can be count,
                     etc. `count` type is stored internally as longSum
-            other fields will be ignored.
+                    other fields will be ignored.
 
             Example: {
                 'name': 'test_click',
@@ -2121,7 +2121,7 @@ class Superset(BaseSupersetView):
         user_name = payload['user']
         cluster_name = payload['cluster']
 
-        user = sm.find_user(username=user_name)
+        user = security_manager.find_user(username=user_name)
         DruidDatasource = ConnectorRegistry.sources['druid']
         DruidCluster = DruidDatasource.cluster_class
         if not user:
@@ -2324,7 +2324,7 @@ class Superset(BaseSupersetView):
             )
 
         query = db.session.query(Query).filter_by(results_key=key).one()
-        rejected_tables = self.rejected_datasources(
+        rejected_tables = security_manager.rejected_datasources(
             query.sql, query.database, query.schema)
         if rejected_tables:
             return json_error_response(get_datasource_access_error_msg(
@@ -2373,7 +2373,7 @@ class Superset(BaseSupersetView):
             json_error_response(
                 'Database with id {} is missing.'.format(database_id))
 
-        rejected_tables = self.rejected_datasources(sql, mydb, schema)
+        rejected_tables = security_manager.rejected_datasources(sql, mydb, schema)
         if rejected_tables:
             return json_error_response(get_datasource_access_error_msg(
                 '{}'.format(rejected_tables)))
@@ -2470,7 +2470,7 @@ class Superset(BaseSupersetView):
             .one()
         )
 
-        rejected_tables = self.rejected_datasources(
+        rejected_tables = security_manager.rejected_datasources(
             query.sql, query.database, query.schema)
         if rejected_tables:
             flash(get_datasource_access_error_msg('{}'.format(rejected_tables)))
@@ -2514,7 +2514,7 @@ class Superset(BaseSupersetView):
             return json_error_response(DATASOURCE_MISSING_ERR)
 
         # Check permission for datasource
-        if not self.datasource_access(datasource):
+        if not security_manager.datasource_access(datasource):
             return json_error_response(DATASOURCE_ACCESS_ERR)
         return json_success(json.dumps(datasource.data))
 
@@ -2662,7 +2662,7 @@ class Superset(BaseSupersetView):
         get the database query string for this slice
         """
         viz_obj = self.get_viz(slice_id)
-        if not self.datasource_access(viz_obj.datasource):
+        if not security_manager.datasource_access(viz_obj.datasource):
             return json_error_response(DATASOURCE_ACCESS_ERR, status=401)
         return self.get_query_string_response(viz_obj)
 
diff --git a/tests/access_tests.py b/tests/access_tests.py
index 39938c9..a669f5d 100644
--- a/tests/access_tests.py
+++ b/tests/access_tests.py
@@ -10,7 +10,7 @@ import unittest
 
 import mock
 
-from superset import app, db, security, sm
+from superset import app, db, security_manager
 from superset.connectors.connector_registry import ConnectorRegistry
 from superset.connectors.druid.models import DruidDatasource
 from superset.connectors.sqla.models import SqlaTable
@@ -70,13 +70,14 @@ def create_access_request(session, ds_type, ds_name, role_name, user_name):
     else:
         ds = session.query(ds_class).filter(
             ds_class.datasource_name == ds_name).first()
-    ds_perm_view = sm.find_permission_view_menu(
+    ds_perm_view = security_manager.find_permission_view_menu(
         'datasource_access', ds.perm)
-    sm.add_permission_role(sm.find_role(role_name), ds_perm_view)
+    security_manager.add_permission_role(
+        security_manager.find_role(role_name), ds_perm_view)
     access_request = models.DatasourceAccessRequest(
         datasource_id=ds.id,
         datasource_type=ds_type,
-        created_by_fk=sm.find_user(username=user_name).id,
+        created_by_fk=security_manager.find_user(username=user_name).id,
     )
     session.add(access_request)
     session.commit()
@@ -89,21 +90,21 @@ class RequestAccessTests(SupersetTestCase):
 
     @classmethod
     def setUpClass(cls):
-        sm.add_role('override_me')
-        sm.add_role(TEST_ROLE_1)
-        sm.add_role(TEST_ROLE_2)
-        sm.add_role(DB_ACCESS_ROLE)
-        sm.add_role(SCHEMA_ACCESS_ROLE)
+        security_manager.add_role('override_me')
+        security_manager.add_role(TEST_ROLE_1)
+        security_manager.add_role(TEST_ROLE_2)
+        security_manager.add_role(DB_ACCESS_ROLE)
+        security_manager.add_role(SCHEMA_ACCESS_ROLE)
         db.session.commit()
 
     @classmethod
     def tearDownClass(cls):
-        override_me = sm.find_role('override_me')
+        override_me = security_manager.find_role('override_me')
         db.session.delete(override_me)
-        db.session.delete(sm.find_role(TEST_ROLE_1))
-        db.session.delete(sm.find_role(TEST_ROLE_2))
-        db.session.delete(sm.find_role(DB_ACCESS_ROLE))
-        db.session.delete(sm.find_role(SCHEMA_ACCESS_ROLE))
+        db.session.delete(security_manager.find_role(TEST_ROLE_1))
+        db.session.delete(security_manager.find_role(TEST_ROLE_2))
+        db.session.delete(security_manager.find_role(DB_ACCESS_ROLE))
+        db.session.delete(security_manager.find_role(SCHEMA_ACCESS_ROLE))
         db.session.commit()
 
     def setUp(self):
@@ -111,7 +112,7 @@ class RequestAccessTests(SupersetTestCase):
 
     def tearDown(self):
         self.logout()
-        override_me = sm.find_role('override_me')
+        override_me = security_manager.find_role('override_me')
         override_me.permissions = []
         db.session.commit()
         db.session.close()
@@ -133,7 +134,7 @@ class RequestAccessTests(SupersetTestCase):
             content_type='application/json')
         self.assertEquals(201, response.status_code)
 
-        updated_override_me = sm.find_role('override_me')
+        updated_override_me = security_manager.find_role('override_me')
         self.assertEquals(1, len(updated_override_me.permissions))
         birth_names = self.get_table_by_name('birth_names')
         self.assertEquals(
@@ -150,7 +151,7 @@ class RequestAccessTests(SupersetTestCase):
             content_type='application/json')
         self.assertEquals(201, response.status_code)
 
-        updated_role = sm.find_role('override_me')
+        updated_role = security_manager.find_role('override_me')
         perms = sorted(
             updated_role.permissions, key=lambda p: p.view_menu.name)
         druid_ds_1 = self.get_druid_ds_by_name('druid_ds_1')
@@ -169,9 +170,9 @@ class RequestAccessTests(SupersetTestCase):
         self.assertEquals(3, len(perms))
 
     def test_override_role_permissions_drops_absent_perms(self):
-        override_me = sm.find_role('override_me')
+        override_me = security_manager.find_role('override_me')
         override_me.permissions.append(
-            sm.find_permission_view_menu(
+            security_manager.find_permission_view_menu(
                 view_menu_name=self.get_table_by_name('long_lat').perm,
                 permission_name='datasource_access'),
         )
@@ -182,7 +183,7 @@ class RequestAccessTests(SupersetTestCase):
             data=json.dumps(ROLE_TABLES_PERM_DATA),
             content_type='application/json')
         self.assertEquals(201, response.status_code)
-        updated_override_me = sm.find_role('override_me')
+        updated_override_me = security_manager.find_role('override_me')
         self.assertEquals(1, len(updated_override_me.permissions))
         birth_names = self.get_table_by_name('birth_names')
         self.assertEquals(
@@ -218,8 +219,8 @@ class RequestAccessTests(SupersetTestCase):
             access_requests = self.get_access_requests('gamma', 'table', ds_1_id)
             self.assertFalse(access_requests)
 
-            gamma_user = sm.find_user(username='gamma')
-            gamma_user.roles.remove(sm.find_role('test_role1'))
+            gamma_user = security_manager.find_user(username='gamma')
+            gamma_user.roles.remove(security_manager.find_role('test_role1'))
 
     def test_clean_requests_after_alpha_grant(self):
         session = db.session
@@ -234,8 +235,8 @@ class RequestAccessTests(SupersetTestCase):
             session, 'table', 'birth_names', TEST_ROLE_2, 'gamma2')
         ds_1_id = access_request1.datasource_id
         # gamma becomes alpha
-        alpha_role = sm.find_role('Alpha')
-        gamma_user = sm.find_user(username='gamma')
+        alpha_role = security_manager.find_role('Alpha')
+        gamma_user = security_manager.find_user(username='gamma')
         gamma_user.roles.append(alpha_role)
         session.commit()
         access_requests = self.get_access_requests('gamma', 'table', ds_1_id)
@@ -245,8 +246,8 @@ class RequestAccessTests(SupersetTestCase):
         access_requests = self.get_access_requests('gamma', 'table', ds_1_id)
         self.assertFalse(access_requests)
 
-        gamma_user = sm.find_user(username='gamma')
-        gamma_user.roles.remove(sm.find_role('Alpha'))
+        gamma_user = security_manager.find_user(username='gamma')
+        gamma_user.roles.remove(security_manager.find_role('Alpha'))
         session.commit()
 
     def test_clean_requests_after_db_grant(self):
@@ -256,7 +257,7 @@ class RequestAccessTests(SupersetTestCase):
         # Gamma gets database access, gamma2 access request granted
         # Check if request by gamma has been deleted
 
-        gamma_user = sm.find_user(username='gamma')
+        gamma_user = security_manager.find_user(username='gamma')
         access_request1 = create_access_request(
             session, 'table', 'long_lat', TEST_ROLE_1, 'gamma')
         create_access_request(
@@ -265,13 +266,12 @@ class RequestAccessTests(SupersetTestCase):
         # gamma gets granted database access
         database = session.query(models.Database).first()
 
-        security.merge_perm(
-            sm, 'database_access', database.perm)
-        ds_perm_view = sm.find_permission_view_menu(
+        security_manager.merge_perm('database_access', database.perm)
+        ds_perm_view = security_manager.find_permission_view_menu(
             'database_access', database.perm)
-        sm.add_permission_role(
-            sm.find_role(DB_ACCESS_ROLE), ds_perm_view)
-        gamma_user.roles.append(sm.find_role(DB_ACCESS_ROLE))
+        security_manager.add_permission_role(
+            security_manager.find_role(DB_ACCESS_ROLE), ds_perm_view)
+        gamma_user.roles.append(security_manager.find_role(DB_ACCESS_ROLE))
         session.commit()
         access_requests = self.get_access_requests('gamma', 'table', ds_1_id)
         self.assertTrue(access_requests)
@@ -281,8 +281,8 @@ class RequestAccessTests(SupersetTestCase):
         access_requests = self.get_access_requests('gamma', 'table', ds_1_id)
 
         self.assertFalse(access_requests)
-        gamma_user = sm.find_user(username='gamma')
-        gamma_user.roles.remove(sm.find_role(DB_ACCESS_ROLE))
+        gamma_user = security_manager.find_user(username='gamma')
+        gamma_user.roles.remove(security_manager.find_role(DB_ACCESS_ROLE))
         session.commit()
 
     def test_clean_requests_after_schema_grant(self):
@@ -292,7 +292,7 @@ class RequestAccessTests(SupersetTestCase):
         # Gamma gets schema access, gamma2 access request granted
         # Check if request by gamma has been deleted
 
-        gamma_user = sm.find_user(username='gamma')
+        gamma_user = security_manager.find_user(username='gamma')
         access_request1 = create_access_request(
             session, 'table', 'wb_health_population', TEST_ROLE_1, 'gamma')
         create_access_request(
@@ -302,21 +302,20 @@ class RequestAccessTests(SupersetTestCase):
             table_name='wb_health_population').first()
 
         ds.schema = 'temp_schema'
-        security.merge_perm(
-            sm, 'schema_access', ds.schema_perm)
-        schema_perm_view = sm.find_permission_view_menu(
+        security_manager.merge_perm('schema_access', ds.schema_perm)
+        schema_perm_view = security_manager.find_permission_view_menu(
             'schema_access', ds.schema_perm)
-        sm.add_permission_role(
-            sm.find_role(SCHEMA_ACCESS_ROLE), schema_perm_view)
-        gamma_user.roles.append(sm.find_role(SCHEMA_ACCESS_ROLE))
+        security_manager.add_permission_role(
+            security_manager.find_role(SCHEMA_ACCESS_ROLE), schema_perm_view)
+        gamma_user.roles.append(security_manager.find_role(SCHEMA_ACCESS_ROLE))
         session.commit()
         # gamma2 request gets fulfilled
         self.client.get(EXTEND_ROLE_REQUEST.format(
             'table', ds_1_id, 'gamma2', TEST_ROLE_2))
         access_requests = self.get_access_requests('gamma', 'table', ds_1_id)
         self.assertFalse(access_requests)
-        gamma_user = sm.find_user(username='gamma')
-        gamma_user.roles.remove(sm.find_role(SCHEMA_ACCESS_ROLE))
+        gamma_user = security_manager.find_user(username='gamma')
+        gamma_user.roles.remove(security_manager.find_role(SCHEMA_ACCESS_ROLE))
 
         ds = session.query(SqlaTable).filter_by(
             table_name='wb_health_population').first()
@@ -329,7 +328,7 @@ class RequestAccessTests(SupersetTestCase):
         if app.config.get('ENABLE_ACCESS_REQUEST'):
             session = db.session
             TEST_ROLE_NAME = 'table_role'
-            sm.add_role(TEST_ROLE_NAME)
+            security_manager.add_role(TEST_ROLE_NAME)
 
             # Case 1. Grant new role to the user.
 
@@ -341,8 +340,8 @@ class RequestAccessTests(SupersetTestCase):
             # Test email content.
             self.assertTrue(mock_send_mime.called)
             call_args = mock_send_mime.call_args[0]
-            self.assertEqual([sm.find_user(username='gamma').email,
-                              sm.find_user(username='admin').email],
+            self.assertEqual([security_manager.find_user(username='gamma').email,
+                              security_manager.find_user(username='admin').email],
                              call_args[1])
             self.assertEqual(
                 '[Superset] Access to the datasource {} was granted'.format(
@@ -354,7 +353,7 @@ class RequestAccessTests(SupersetTestCase):
             # request was removed
             self.assertFalse(access_requests)
             # user was granted table_role
-            user_roles = [r.name for r in sm.find_user('gamma').roles]
+            user_roles = [r.name for r in security_manager.find_user('gamma').roles]
             self.assertIn(TEST_ROLE_NAME, user_roles)
 
             # Case 2. Extend the role to have access to the table
@@ -371,8 +370,8 @@ class RequestAccessTests(SupersetTestCase):
             # Test email content.
             self.assertTrue(mock_send_mime.called)
             call_args = mock_send_mime.call_args[0]
-            self.assertEqual([sm.find_user(username='gamma').email,
-                              sm.find_user(username='admin').email],
+            self.assertEqual([security_manager.find_user(username='gamma').email,
+                              security_manager.find_user(username='admin').email],
                              call_args[1])
             self.assertEqual(
                 '[Superset] Access to the datasource {} was granted'.format(
@@ -383,21 +382,21 @@ class RequestAccessTests(SupersetTestCase):
             # request was removed
             self.assertFalse(access_requests)
             # table_role was extended to grant access to the long_lat table/
-            perm_view = sm.find_permission_view_menu(
+            perm_view = security_manager.find_permission_view_menu(
                 'datasource_access', long_lat_perm)
-            TEST_ROLE = sm.find_role(TEST_ROLE_NAME)
+            TEST_ROLE = security_manager.find_role(TEST_ROLE_NAME)
             self.assertIn(perm_view, TEST_ROLE.permissions)
 
             # Case 3. Grant new role to the user to access the druid datasource.
 
-            sm.add_role('druid_role')
+            security_manager.add_role('druid_role')
             access_request3 = create_access_request(
                 session, 'druid', 'druid_ds_1', 'druid_role', 'gamma')
             self.get_resp(GRANT_ROLE_REQUEST.format(
                 'druid', access_request3.datasource_id, 'gamma', 'druid_role'))
 
             # user was granted table_role
-            user_roles = [r.name for r in sm.find_user('gamma').roles]
+            user_roles = [r.name for r in security_manager.find_user('gamma').roles]
             self.assertIn('druid_role', user_roles)
 
             # Case 4. Extend the role to have access to the druid datasource
@@ -409,17 +408,17 @@ class RequestAccessTests(SupersetTestCase):
             self.client.get(EXTEND_ROLE_REQUEST.format(
                 'druid', access_request4.datasource_id, 'gamma', 'druid_role'))
             # druid_role was extended to grant access to the druid_access_ds_2
-            druid_role = sm.find_role('druid_role')
-            perm_view = sm.find_permission_view_menu(
+            druid_role = security_manager.find_role('druid_role')
+            perm_view = security_manager.find_permission_view_menu(
                 'datasource_access', druid_ds_2_perm)
             self.assertIn(perm_view, druid_role.permissions)
 
             # cleanup
-            gamma_user = sm.find_user(username='gamma')
-            gamma_user.roles.remove(sm.find_role('druid_role'))
-            gamma_user.roles.remove(sm.find_role(TEST_ROLE_NAME))
-            session.delete(sm.find_role('druid_role'))
-            session.delete(sm.find_role(TEST_ROLE_NAME))
+            gamma_user = security_manager.find_user(username='gamma')
+            gamma_user.roles.remove(security_manager.find_role('druid_role'))
+            gamma_user.roles.remove(security_manager.find_role(TEST_ROLE_NAME))
+            session.delete(security_manager.find_role('druid_role'))
+            session.delete(security_manager.find_role(TEST_ROLE_NAME))
             session.commit()
 
     def test_request_access(self):
@@ -427,9 +426,9 @@ class RequestAccessTests(SupersetTestCase):
             session = db.session
             self.logout()
             self.login(username='gamma')
-            gamma_user = sm.find_user(username='gamma')
-            sm.add_role('dummy_role')
-            gamma_user.roles.append(sm.find_role('dummy_role'))
+            gamma_user = security_manager.find_user(username='gamma')
+            security_manager.add_role('dummy_role')
+            gamma_user.roles.append(security_manager.find_role('dummy_role'))
             session.commit()
 
             ACCESS_REQUEST = (
@@ -461,14 +460,16 @@ class RequestAccessTests(SupersetTestCase):
             table_3_id = table3.id
             table3_perm = table3.perm
 
-            sm.add_role('energy_usage_role')
-            alpha_role = sm.find_role('Alpha')
-            sm.add_permission_role(
+            security_manager.add_role('energy_usage_role')
+            alpha_role = security_manager.find_role('Alpha')
+            security_manager.add_permission_role(
                 alpha_role,
-                sm.find_permission_view_menu('datasource_access', table3_perm))
-            sm.add_permission_role(
-                sm.find_role('energy_usage_role'),
-                sm.find_permission_view_menu('datasource_access', table3_perm))
+                security_manager.find_permission_view_menu(
+                    'datasource_access', table3_perm))
+            security_manager.add_permission_role(
+                security_manager.find_role('energy_usage_role'),
+                security_manager.find_permission_view_menu(
+                    'datasource_access', table3_perm))
             session.commit()
 
             self.get_resp(
@@ -500,14 +501,16 @@ class RequestAccessTests(SupersetTestCase):
             druid_ds_5_id = druid_ds_5.id
             druid_ds_5_perm = druid_ds_5.perm
 
-            druid_ds_2_role = sm.add_role('druid_ds_2_role')
-            admin_role = sm.find_role('Admin')
-            sm.add_permission_role(
+            druid_ds_2_role = security_manager.add_role('druid_ds_2_role')
+            admin_role = security_manager.find_role('Admin')
+            security_manager.add_permission_role(
                 admin_role,
-                sm.find_permission_view_menu('datasource_access', druid_ds_5_perm))
-            sm.add_permission_role(
+                security_manager.find_permission_view_menu(
+                    'datasource_access', druid_ds_5_perm))
+            security_manager.add_permission_role(
                 druid_ds_2_role,
-                sm.find_permission_view_menu('datasource_access', druid_ds_5_perm))
+                security_manager.find_permission_view_menu(
+                    'datasource_access', druid_ds_5_perm))
             session.commit()
 
             self.get_resp(ACCESS_REQUEST.format('druid', druid_ds_5_id, 'go'))
@@ -520,8 +523,8 @@ class RequestAccessTests(SupersetTestCase):
                              '<ul><li>{}</li></ul>'.format(approve_link_5))
 
             # cleanup
-            gamma_user = sm.find_user(username='gamma')
-            gamma_user.roles.remove(sm.find_role('dummy_role'))
+            gamma_user = security_manager.find_user(username='gamma')
+            gamma_user.roles.remove(security_manager.find_role('dummy_role'))
             session.commit()
 
 
diff --git a/tests/base_tests.py b/tests/base_tests.py
index dcc6798..c491637 100644
--- a/tests/base_tests.py
+++ b/tests/base_tests.py
@@ -12,11 +12,10 @@ import unittest
 
 from flask_appbuilder.security.sqla import models as ab_models
 
-from superset import app, appbuilder, cli, db, security, sm
+from superset import app, cli, db, security_manager, utils
 from superset.connectors.druid.models import DruidCluster, DruidDatasource
 from superset.connectors.sqla.models import SqlaTable
 from superset.models import core as models
-from superset.security import sync_role_definitions
 
 os.environ['SUPERSET_CONFIG'] = 'tests.superset_test_config'
 
@@ -36,59 +35,60 @@ class SupersetTestCase(unittest.TestCase):
             logging.info('Loading examples')
             cli.load_examples(load_test_data=True)
             logging.info('Done loading examples')
-            sync_role_definitions()
+            security_manager.sync_role_definitions()
             os.environ['examples_loaded'] = '1'
         else:
-            sync_role_definitions()
+            security_manager.sync_role_definitions()
         super(SupersetTestCase, self).__init__(*args, **kwargs)
         self.client = app.test_client()
         self.maxDiff = None
 
-        gamma_sqllab_role = sm.add_role('gamma_sqllab')
-        for perm in sm.find_role('Gamma').permissions:
-            sm.add_permission_role(gamma_sqllab_role, perm)
-        db_perm = self.get_main_database(sm.get_session).perm
-        security.merge_perm(sm, 'database_access', db_perm)
-        db_pvm = sm.find_permission_view_menu(
+        gamma_sqllab_role = security_manager.add_role('gamma_sqllab')
+        for perm in security_manager.find_role('Gamma').permissions:
+            security_manager.add_permission_role(gamma_sqllab_role, perm)
+        utils.get_or_create_main_db()
+        db_perm = self.get_main_database(security_manager.get_session).perm
+        security_manager.merge_perm('database_access', db_perm)
+        db_pvm = security_manager.find_permission_view_menu(
             view_menu_name=db_perm, permission_name='database_access')
         gamma_sqllab_role.permissions.append(db_pvm)
-        for perm in sm.find_role('sql_lab').permissions:
-            sm.add_permission_role(gamma_sqllab_role, perm)
+        for perm in security_manager.find_role('sql_lab').permissions:
+            security_manager.add_permission_role(gamma_sqllab_role, perm)
 
-        admin = appbuilder.sm.find_user('admin')
+        admin = security_manager.find_user('admin')
         if not admin:
-            appbuilder.sm.add_user(
+            security_manager.add_user(
                 'admin', 'admin', ' user', 'admin@fab.org',
-                appbuilder.sm.find_role('Admin'),
+                security_manager.find_role('Admin'),
                 password='general')
 
-        gamma = appbuilder.sm.find_user('gamma')
+        gamma = security_manager.find_user('gamma')
         if not gamma:
-            appbuilder.sm.add_user(
+            security_manager.add_user(
                 'gamma', 'gamma', 'user', 'gamma@fab.org',
-                appbuilder.sm.find_role('Gamma'),
+                security_manager.find_role('Gamma'),
                 password='general')
 
-        gamma2 = appbuilder.sm.find_user('gamma2')
+        gamma2 = security_manager.find_user('gamma2')
         if not gamma2:
-            appbuilder.sm.add_user(
+            security_manager.add_user(
                 'gamma2', 'gamma2', 'user', 'gamma2@fab.org',
-                appbuilder.sm.find_role('Gamma'),
+                security_manager.find_role('Gamma'),
                 password='general')
 
-        gamma_sqllab_user = appbuilder.sm.find_user('gamma_sqllab')
+        gamma_sqllab_user = security_manager.find_user('gamma_sqllab')
         if not gamma_sqllab_user:
-            appbuilder.sm.add_user(
+            security_manager.add_user(
                 'gamma_sqllab', 'gamma_sqllab', 'user', 'gamma_sqllab@fab.org',
                 gamma_sqllab_role, password='general')
 
-        alpha = appbuilder.sm.find_user('alpha')
+        alpha = security_manager.find_user('alpha')
         if not alpha:
-            appbuilder.sm.add_user(
+            security_manager.add_user(
                 'alpha', 'alpha', 'user', 'alpha@fab.org',
-                appbuilder.sm.find_role('Alpha'),
+                security_manager.find_role('Alpha'),
                 password='general')
-        sm.get_session.commit()
+        security_manager.get_session.commit()
         # create druid cluster and druid datasources
         session = db.session
         cluster = (
@@ -177,7 +177,7 @@ class SupersetTestCase(unittest.TestCase):
         return (
             db.session.query(DAR)
             .filter(
-                DAR.created_by == sm.find_user(username=username),
+                DAR.created_by == security_manager.find_user(username=username),
                 DAR.datasource_type == ds_type,
                 DAR.datasource_id == ds_id,
             )
@@ -188,20 +188,20 @@ class SupersetTestCase(unittest.TestCase):
         self.client.get('/logout/', follow_redirects=True)
 
     def grant_public_access_to_table(self, table):
-        public_role = appbuilder.sm.find_role('Public')
+        public_role = security_manager.find_role('Public')
         perms = db.session.query(ab_models.PermissionView).all()
         for perm in perms:
             if (perm.permission.name == 'datasource_access' and
                     perm.view_menu and table.perm in perm.view_menu.name):
-                appbuilder.sm.add_permission_role(public_role, perm)
+                security_manager.add_permission_role(public_role, perm)
 
     def revoke_public_access_to_table(self, table):
-        public_role = appbuilder.sm.find_role('Public')
+        public_role = security_manager.find_role('Public')
         perms = db.session.query(ab_models.PermissionView).all()
         for perm in perms:
             if (perm.permission.name == 'datasource_access' and
                     perm.view_menu and table.perm in perm.view_menu.name):
-                appbuilder.sm.del_permission_role(public_role, perm)
+                security_manager.del_permission_role(public_role, perm)
 
     def run_sql(self, sql, client_id, user_name=None, raise_on_error=False):
         if user_name:
@@ -241,7 +241,7 @@ class SupersetTestCase(unittest.TestCase):
             assert_can_write(view_menu)
 
         gamma_perm_set = set()
-        for perm in sm.find_role('Gamma').permissions:
+        for perm in security_manager.find_role('Gamma').permissions:
             gamma_perm_set.add((perm.permission.name, perm.view_menu.name))
 
         # check read only perms
diff --git a/tests/celery_tests.py b/tests/celery_tests.py
index 172176e..79b71e9 100644
--- a/tests/celery_tests.py
+++ b/tests/celery_tests.py
@@ -14,10 +14,9 @@ import unittest
 import pandas as pd
 from past.builtins import basestring
 
-from superset import app, appbuilder, cli, dataframe, db
+from superset import app, cli, dataframe, db, security_manager
 from superset.models.helpers import QueryStatus
 from superset.models.sql_lab import Query
-from superset.security import sync_role_definitions
 from superset.sql_parse import SupersetQuery
 from .base_tests import SupersetTestCase
 
@@ -98,17 +97,17 @@ class CeleryTestCase(SupersetTestCase):
         except OSError as e:
             app.logger.warn(str(e))
 
-        sync_role_definitions()
+        security_manager.sync_role_definitions()
 
         worker_command = BASE_DIR + '/bin/superset worker'
         subprocess.Popen(
             worker_command, shell=True, stdout=subprocess.PIPE)
 
-        admin = appbuilder.sm.find_user('admin')
+        admin = security_manager.find_user('admin')
         if not admin:
-            appbuilder.sm.add_user(
+            security_manager.add_user(
                 'admin', 'admin', ' user', 'admin@fab.org',
-                appbuilder.sm.find_role('Admin'),
+                security_manager.find_role('Admin'),
                 password='general')
         cli.load_examples(load_test_data=True)
 
diff --git a/tests/core_tests.py b/tests/core_tests.py
index e4df8bf..6f82af8 100644
--- a/tests/core_tests.py
+++ b/tests/core_tests.py
@@ -22,7 +22,7 @@ import psycopg2
 from six import text_type
 import sqlalchemy as sqla
 
-from superset import appbuilder, dataframe, db, jinja_context, sm, sql_lab, utils
+from superset import dataframe, db, jinja_context, security_manager, sql_lab, utils
 from superset.connectors.sqla.models import SqlaTable
 from superset.models import core as models
 from superset.models.sql_lab import Query
@@ -136,7 +136,7 @@ class CoreTests(SupersetTestCase):
 
     def test_admin_only_permissions(self):
         def assert_admin_permission_in(role_name, assert_func):
-            role = sm.find_role(role_name)
+            role = security_manager.find_role(role_name)
             permissions = [p.permission.name for p in role.permissions]
             assert_func('can_sync_druid_source', permissions)
             assert_func('can_approve', permissions)
@@ -147,7 +147,7 @@ class CoreTests(SupersetTestCase):
 
     def test_admin_only_menu_views(self):
         def assert_admin_view_menus_in(role_name, assert_func):
-            role = sm.find_role(role_name)
+            role = security_manager.find_role(role_name)
             view_menus = [p.view_menu.name for p in role.permissions]
             assert_func('ResetPasswordView', view_menus)
             assert_func('RoleModelView', view_menus)
@@ -267,7 +267,7 @@ class CoreTests(SupersetTestCase):
 
     def test_get_user_slices(self):
         self.login(username='admin')
-        userid = appbuilder.sm.find_user('admin').id
+        userid = security_manager.find_user('admin').id
         url = '/sliceaddview/api/read?_flt_0_created_by={}'.format(userid)
         resp = self.client.get(url)
         self.assertEqual(resp.status_code, 200)
@@ -275,11 +275,11 @@ class CoreTests(SupersetTestCase):
     def test_slices_V2(self):
         # Add explore-v2-beta role to admin user
         # Test all slice urls as user with with explore-v2-beta role
-        sm.add_role('explore-v2-beta')
+        security_manager.add_role('explore-v2-beta')
 
-        appbuilder.sm.add_user(
+        security_manager.add_user(
             'explore_beta', 'explore_beta', ' user', 'explore_beta@airbnb.com',
-            appbuilder.sm.find_role('explore-v2-beta'),
+            security_manager.find_role('explore-v2-beta'),
             password='general')
         self.login(username='explore_beta', password='general')
 
@@ -651,8 +651,8 @@ class CoreTests(SupersetTestCase):
 
         dash = db.session.query(models.Dashboard).filter_by(
             slug='births').first()
-        dash.owners = [appbuilder.sm.find_user('admin')]
-        dash.created_by = appbuilder.sm.find_user('admin')
+        dash.owners = [security_manager.find_user('admin')]
+        dash.created_by = security_manager.find_user('admin')
         db.session.merge(dash)
         db.session.commit()
 
@@ -674,7 +674,7 @@ class CoreTests(SupersetTestCase):
         self.assertRaises(
             Exception, self.test_save_dash, 'alpha')
 
-        alpha = appbuilder.sm.find_user('alpha')
+        alpha = security_manager.find_user('alpha')
 
         dash = (
             db.session
@@ -775,7 +775,7 @@ class CoreTests(SupersetTestCase):
         resp = self.get_json_resp(url)
         self.assertEqual(resp['count'], 1)
 
-        userid = appbuilder.sm.find_user('admin').id
+        userid = security_manager.find_user('admin').id
         resp = self.get_resp('/superset/profile/admin/')
         self.assertIn('"app"', resp)
         data = self.get_json_resp('/superset/recent_activity/{}/'.format(userid))
diff --git a/tests/druid_tests.py b/tests/druid_tests.py
index e72eb25..7406dac 100644
--- a/tests/druid_tests.py
+++ b/tests/druid_tests.py
@@ -11,7 +11,7 @@ import unittest
 
 from mock import Mock, patch
 
-from superset import db, security, sm
+from superset import db, security_manager
 from superset.connectors.druid.models import (
     DruidCluster, DruidColumn, DruidDatasource, DruidMetric,
 )
@@ -278,13 +278,13 @@ class DruidTests(SupersetTestCase):
         db.session.merge(no_gamma_ds)
         db.session.commit()
 
-        security.merge_perm(sm, 'datasource_access', gamma_ds.perm)
-        security.merge_perm(sm, 'datasource_access', no_gamma_ds.perm)
+        security_manager.merge_perm('datasource_access', gamma_ds.perm)
+        security_manager.merge_perm('datasource_access', no_gamma_ds.perm)
 
-        perm = sm.find_permission_view_menu(
+        perm = security_manager.find_permission_view_menu(
             'datasource_access', gamma_ds.get_perm())
-        sm.add_permission_role(sm.find_role('Gamma'), perm)
-        sm.get_session.commit()
+        security_manager.add_permission_role(security_manager.find_role('Gamma'), perm)
+        security_manager.get_session.commit()
 
         self.login(username='gamma')
         url = '/druiddatasourcemodelview/list/'
@@ -331,10 +331,11 @@ class DruidTests(SupersetTestCase):
         db.session.commit()
 
         view_menu_name = cluster.datasources[0].get_perm()
-        view_menu = sm.find_view_menu(view_menu_name)
-        permission = sm.find_permission('datasource_access')
+        view_menu = security_manager.find_view_menu(view_menu_name)
+        permission = security_manager.find_permission('datasource_access')
 
-        pv = sm.get_session.query(sm.permissionview_model).filter_by(
+        pv = security_manager.get_session.query(
+            security_manager.permissionview_model).filter_by(
             permission=permission, view_menu=view_menu).first()
         assert pv is not None
 
diff --git a/tests/security_tests.py b/tests/security_tests.py
index e117394..58ec0c7 100644
--- a/tests/security_tests.py
+++ b/tests/security_tests.py
@@ -4,13 +4,13 @@ from __future__ import division
 from __future__ import print_function
 from __future__ import unicode_literals
 
-from superset import app, security, sm
+from superset import app, security_manager
 from .base_tests import SupersetTestCase
 
 
 def get_perm_tuples(role_name):
     perm_set = set()
-    for perm in sm.find_role(role_name).permissions:
+    for perm in security_manager.find_role(role_name).permissions:
         perm_set.add((perm.permission.name, perm.view_menu.name))
     return perm_set
 
@@ -103,46 +103,47 @@ class RolePermissionTests(SupersetTestCase):
         self.assertIn(('can_approve', 'Superset'), perm_set)
 
     def test_is_admin_only(self):
-        self.assertFalse(security.is_admin_only(
-            sm.find_permission_view_menu('can_show', 'TableModelView')))
-        self.assertFalse(security.is_admin_only(
-            sm.find_permission_view_menu(
+        self.assertFalse(security_manager.is_admin_only(
+            security_manager.find_permission_view_menu('can_show', 'TableModelView')))
+        self.assertFalse(security_manager.is_admin_only(
+            security_manager.find_permission_view_menu(
                 'all_datasource_access', 'all_datasource_access')))
 
-        self.assertTrue(security.is_admin_only(
-            sm.find_permission_view_menu('can_delete', 'DatabaseView')))
+        self.assertTrue(security_manager.is_admin_only(
+            security_manager.find_permission_view_menu('can_delete', 'DatabaseView')))
         if app.config.get('ENABLE_ACCESS_REQUEST'):
-            self.assertTrue(security.is_admin_only(
-                sm.find_permission_view_menu(
+            self.assertTrue(security_manager.is_admin_only(
+                security_manager.find_permission_view_menu(
                     'can_show', 'AccessRequestsModelView')))
-        self.assertTrue(security.is_admin_only(
-            sm.find_permission_view_menu(
+        self.assertTrue(security_manager.is_admin_only(
+            security_manager.find_permission_view_menu(
                 'can_edit', 'UserDBModelView')))
-        self.assertTrue(security.is_admin_only(
-            sm.find_permission_view_menu(
+        self.assertTrue(security_manager.is_admin_only(
+            security_manager.find_permission_view_menu(
                 'can_approve', 'Superset')))
-        self.assertTrue(security.is_admin_only(
-            sm.find_permission_view_menu(
+        self.assertTrue(security_manager.is_admin_only(
+            security_manager.find_permission_view_menu(
                 'all_database_access', 'all_database_access')))
 
     def test_is_alpha_only(self):
-        self.assertFalse(security.is_alpha_only(
-            sm.find_permission_view_menu('can_show', 'TableModelView')))
+        self.assertFalse(security_manager.is_alpha_only(
+            security_manager.find_permission_view_menu('can_show', 'TableModelView')))
 
-        self.assertTrue(security.is_alpha_only(
-            sm.find_permission_view_menu('muldelete', 'TableModelView')))
-        self.assertTrue(security.is_alpha_only(
-            sm.find_permission_view_menu(
+        self.assertTrue(security_manager.is_alpha_only(
+            security_manager.find_permission_view_menu('muldelete', 'TableModelView')))
+        self.assertTrue(security_manager.is_alpha_only(
+            security_manager.find_permission_view_menu(
                 'all_datasource_access', 'all_datasource_access')))
-        self.assertTrue(security.is_alpha_only(
-            sm.find_permission_view_menu('can_edit', 'SqlMetricInlineView')))
-        self.assertTrue(security.is_alpha_only(
-            sm.find_permission_view_menu(
+        self.assertTrue(security_manager.is_alpha_only(
+            security_manager.find_permission_view_menu(
+                'can_edit', 'SqlMetricInlineView')))
+        self.assertTrue(security_manager.is_alpha_only(
+            security_manager.find_permission_view_menu(
                 'can_delete', 'DruidMetricInlineView')))
 
     def test_is_gamma_pvm(self):
-        self.assertTrue(security.is_gamma_pvm(
-            sm.find_permission_view_menu('can_show', 'TableModelView')))
+        self.assertTrue(security_manager.is_gamma_pvm(
+            security_manager.find_permission_view_menu('can_show', 'TableModelView')))
 
     def test_gamma_permissions(self):
         self.assert_can_gamma(get_perm_tuples('Gamma'))
diff --git a/tests/sqllab_tests.py b/tests/sqllab_tests.py
index 977bbe0..afab140 100644
--- a/tests/sqllab_tests.py
+++ b/tests/sqllab_tests.py
@@ -11,7 +11,7 @@ import unittest
 
 from flask_appbuilder.security.sqla import models as ab_models
 
-from superset import appbuilder, db, sm, utils
+from superset import db, security_manager, utils
 from superset.models.sql_lab import Query
 from superset.sql_lab import convert_results_to_df
 from .base_tests import SupersetTestCase
@@ -56,7 +56,7 @@ class SqlLabTests(SupersetTestCase):
 
     def test_sql_json_has_access(self):
         main_db = self.get_main_database(db.session)
-        sm.add_permission_view_menu('database_access', main_db.perm)
+        security_manager.add_permission_view_menu('database_access', main_db.perm)
         db.session.commit()
         main_db_permission_view = (
             db.session.query(ab_models.PermissionView)
@@ -66,17 +66,17 @@ class SqlLabTests(SupersetTestCase):
             .filter(ab_models.Permission.name == 'database_access')
             .first()
         )
-        astronaut = sm.add_role('Astronaut')
-        sm.add_permission_role(astronaut, main_db_permission_view)
+        astronaut = security_manager.add_role('Astronaut')
+        security_manager.add_permission_role(astronaut, main_db_permission_view)
         # Astronaut role is Gamma + sqllab +  main db permissions
-        for perm in sm.find_role('Gamma').permissions:
-            sm.add_permission_role(astronaut, perm)
-        for perm in sm.find_role('sql_lab').permissions:
-            sm.add_permission_role(astronaut, perm)
+        for perm in security_manager.find_role('Gamma').permissions:
+            security_manager.add_permission_role(astronaut, perm)
+        for perm in security_manager.find_role('sql_lab').permissions:
+            security_manager.add_permission_role(astronaut, perm)
 
-        gagarin = appbuilder.sm.find_user('gagarin')
+        gagarin = security_manager.find_user('gagarin')
         if not gagarin:
-            appbuilder.sm.add_user(
+            security_manager.add_user(
                 'gagarin', 'Iurii', 'Gagarin', 'gagarin@cosmos.ussr',
                 astronaut,
                 password='general')
@@ -139,14 +139,14 @@ class SqlLabTests(SupersetTestCase):
         self.login('admin')
 
         # Test search queries on user Id
-        user_id = appbuilder.sm.find_user('admin').id
+        user_id = security_manager.find_user('admin').id
         data = self.get_json_resp(
             '/superset/search_queries?user_id={}'.format(user_id))
         self.assertEquals(2, len(data))
         user_ids = {k['userId'] for k in data}
         self.assertEquals(set([user_id]), user_ids)
 
-        user_id = appbuilder.sm.find_user('gamma_sqllab').id
+        user_id = security_manager.find_user('gamma_sqllab').id
         resp = self.get_resp(
             '/superset/search_queries?user_id={}'.format(user_id))
         data = json.loads(resp)

-- 
To stop receiving notification emails like this one, please contact
johnbodley@apache.org.