Skip to content

Commit

Permalink
[security] improving the security scheme (#1587)
Browse files Browse the repository at this point in the history
* [security] improving the security scheme

* Addressing comments

* improving docs

* Creating security module to organize things

* Moving CLI to its own module

* perms

* Materializung perms

* progrss

* Addressing comments, linting
  • Loading branch information
mistercrunch committed Nov 17, 2016
1 parent aad9744 commit bce02e3
Show file tree
Hide file tree
Showing 19 changed files with 769 additions and 547 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -8,6 +8,7 @@ changelog.sh
_build
_static
_images
_modules
superset/bin/supersetc
env_py3
.eggs
Expand Down
21 changes: 19 additions & 2 deletions docs/security.rst
Expand Up @@ -7,8 +7,19 @@ FAB provides authentication, user management, permissions and roles.

Provided Roles
--------------
Superset ships with 3 roles that are handled by Superset itself. You can
assume that these 3 roles will stay up-to-date as Superset evolves.
Superset ships with a set of roles that are handled by Superset itself.
You can assume that these roles will stay up-to-date as Superset evolves.
Even though it's possible for ``Admin`` usrs to do so, it is not recommended
that you alter these roles in any way by removing
or adding permissions to them as these roles will be re-synchronized to
their original values as you run your next ``superset init`` command.

Since it's not recommended to alter the roles described here, it's right
to assume that your security strategy should be to compose user access based
on these base roles and roles that you create. For instance you could
create a role ``Financial Analyst`` that would be made of set of permissions
to a set of data sources (tables) and/or databases. Users would then be
granted ``Gamma``, ``Financial Analyst``, and perhaps ``sql_lab``.

Admin
"""""
Expand All @@ -33,6 +44,12 @@ mostly content consumers, though they can create slices and dashboards.
Also note that when Gamma users look at the dashboards and slices list view,
they will only see the objects that they have access to.

sql_lab
"""""""
The ``sql_lab`` role grants access to SQL Lab. Note that while ``Admin``
users have access to all databases by default, both ``Alpha`` and ``Gamma``
users need to be given access on a per database basis.


Managing Gamma per data source access
-------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions superset/__init__.py
Expand Up @@ -23,6 +23,8 @@

app = Flask(__name__)
app.config.from_object(CONFIG_MODULE)
conf = app.config

if not app.debug:
# In production mode, add log handler to sys.stderr.
app.logger.addHandler(logging.StreamHandler())
Expand Down
2 changes: 1 addition & 1 deletion superset/assets/javascripts/SqlLab/actions.js
Expand Up @@ -158,7 +158,7 @@ export function setNetworkStatus(networkOn) {
export function addAlert(alert) {
const o = Object.assign({}, alert);
o.id = shortid.generate();
return { type: ADD_ALERT, o };
return { type: ADD_ALERT, alert: o };
}

export function removeAlert(alert) {
Expand Down
20 changes: 14 additions & 6 deletions superset/assets/javascripts/SqlLab/components/DatabaseSelect.jsx
Expand Up @@ -2,6 +2,13 @@ const $ = window.$ = require('jquery');
import React from 'react';
import Select from 'react-select';

const propTypes = {
onChange: React.PropTypes.func,
actions: React.PropTypes.object,
databaseId: React.PropTypes.number,
valueRenderer: React.PropTypes.func,
};

class DatabaseSelect extends React.PureComponent {
constructor(props) {
super(props);
Expand All @@ -23,6 +30,12 @@ class DatabaseSelect extends React.PureComponent {
const options = data.result.map((db) => ({ value: db.id, label: db.database_name }));
this.setState({ databaseOptions: options, databaseLoading: false });
this.props.actions.setDatabases(data.result);
if (data.result.length === 0) {
this.props.actions.addAlert({
bsStyle: 'danger',
msg: "It seems you don't have access to any database",
});
}
});
}
render() {
Expand All @@ -43,11 +56,6 @@ class DatabaseSelect extends React.PureComponent {
}
}

DatabaseSelect.propTypes = {
onChange: React.PropTypes.func,
actions: React.PropTypes.object,
databaseId: React.PropTypes.number,
valueRenderer: React.PropTypes.func,
};
DatabaseSelect.propTypes = propTypes;

export default DatabaseSelect;
151 changes: 1 addition & 150 deletions superset/bin/superset
Expand Up @@ -4,156 +4,7 @@ from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import logging
import celery
from celery.bin import worker as celery_worker
from datetime import datetime
from subprocess import Popen

from flask_migrate import MigrateCommand
from flask_script import Manager

import superset
from superset import app, ascii_art, db, data, utils

config = app.config

manager = Manager(app)
manager.add_command('db', MigrateCommand)


@manager.option(
'-d', '--debug', action='store_true',
help="Start the web server in debug mode")
@manager.option(
'-a', '--address', default=config.get("SUPERSET_WEBSERVER_ADDRESS"),
help="Specify the address to which to bind the web server")
@manager.option(
'-p', '--port', default=config.get("SUPERSET_WEBSERVER_PORT"),
help="Specify the port on which to run the web server")
@manager.option(
'-w', '--workers', default=config.get("SUPERSET_WORKERS", 2),
help="Number of gunicorn web server workers to fire up")
@manager.option(
'-t', '--timeout', default=config.get("SUPERSET_WEBSERVER_TIMEOUT"),
help="Specify the timeout (seconds) for the gunicorn web server")
def runserver(debug, address, port, timeout, workers):
"""Starts a Superset web server"""
debug = debug or config.get("DEBUG")
if debug:
app.run(
host='0.0.0.0',
port=int(port),
threaded=True,
debug=True)
else:
cmd = (
"gunicorn "
"-w {workers} "
"--timeout {timeout} "
"-b {address}:{port} "
"--limit-request-line 0 "
"--limit-request-field_size 0 "
"superset:app").format(**locals())
print("Starting server with command: " + cmd)
Popen(cmd, shell=True).wait()

@manager.command
def init():
"""Inits the Superset application"""
utils.init(superset)

@manager.option(
'-v', '--verbose', action='store_true',
help="Show extra information")
def version(verbose):
"""Prints the current version number"""
s = (
"\n{boat}\n\n"
"-----------------------\n"
"Superset {version}\n"
"-----------------------").format(
boat=ascii_art.boat, version=config.get('VERSION_STRING'))
print(s)
if verbose:
print("[DB] : " + "{}".format(db.engine))

@manager.option(
'-t', '--load-test-data', action='store_true',
help="Load additional test data")
def load_examples(load_test_data):
"""Loads a set of Slices and Dashboards and a supporting dataset """
print("Loading examples into {}".format(db))

data.load_css_templates()

print("Loading energy related dataset")
data.load_energy()

print("Loading [World Bank's Health Nutrition and Population Stats]")
data.load_world_bank_health_n_pop()

print("Loading [Birth names]")
data.load_birth_names()

print("Loading [Random time series data]")
data.load_random_time_series_data()

print("Loading [Random long/lat data]")
data.load_long_lat_data()

print("Loading [Multiformat time series]")
data.load_multiformat_time_series_data()

print("Loading [Misc Charts] dashboard")
data.load_misc_dashboard()

if load_test_data:
print("Loading [Unicode test data]")
data.load_unicode_test_data()

@manager.option(
'-d', '--datasource',
help=(
"Specify which datasource name to load, if omitted, all "
"datasources will be refreshed"))
def refresh_druid(datasource):
"""Refresh druid datasources"""
session = db.session()
from superset import models
for cluster in session.query(models.DruidCluster).all():
try:
cluster.refresh_datasources(datasource_name=datasource)
except Exception as e:
print(
"Error while processing cluster '{}'\n{}".format(
cluster, str(e)))
logging.exception(e)
cluster.metadata_last_refreshed = datetime.now()
print(
"Refreshed metadata from cluster "
"[" + cluster.cluster_name + "]")
session.commit()


@manager.command
def worker():
"""Starts a Superset worker for async SQL query execution."""
# celery -A tasks worker --loglevel=info
print("Starting SQL Celery worker.")
if config.get('CELERY_CONFIG'):
print("Celery broker url: ")
print(config.get('CELERY_CONFIG').BROKER_URL)

application = celery.current_app._get_current_object()
c_worker = celery_worker.worker(app=application)
options = {
'broker': config.get('CELERY_CONFIG').BROKER_URL,
'loglevel': 'INFO',
'traceback': True,
}
c_worker.run(**options)

from superset.cli import manager

if __name__ == "__main__":
manager.run()

0 comments on commit bce02e3

Please sign in to comment.