Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A first draft on default security roles #35

Merged
merged 7 commits into from
Oct 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions panoramix/bin/panoramix
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,18 @@ import json
from subprocess import Popen

from flask.ext.script import Manager
from panoramix import app
from flask.ext.migrate import MigrateCommand
from panoramix import db
from flask.ext.appbuilder import Base
from sqlalchemy import Column, Integer, String, Table, DateTime

from panoramix import app
from panoramix import models

from panoramix import models, utils

config = app.config

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

from flask.ext.appbuilder import Base

@manager.option(
'-d', '--debug', action='store_true',
Expand All @@ -45,6 +43,11 @@ def runserver(debug, port):
print("Starting server with command: " + cmd)
Popen(cmd, shell=True).wait()

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

@manager.option(
'-s', '--sample', action='store_true',
help="Only load 1000 rows (faster, used for testing)")
Expand Down Expand Up @@ -108,7 +111,7 @@ def load_examples(sample):
session.commit()

print("Creating table reference")
TBL = models.Table
TBL = models.SqlaTable
obj = session.query(TBL).filter_by(table_name='birth_names').first()
if not obj:
obj = TBL(table_name = 'birth_names')
Expand Down
21 changes: 14 additions & 7 deletions panoramix/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from pydruid.utils.filters import Dimension, Filter
from sqlalchemy import (
Column, Integer, String, ForeignKey, Text, Boolean, DateTime)
from sqlalchemy import Table as sqlaTable
from sqlalchemy import create_engine, MetaData, desc, select, and_, Table
from sqlalchemy import Table
from sqlalchemy import create_engine, MetaData, desc, select, and_
from sqlalchemy.orm import relationship
from sqlalchemy.sql import table, literal_column, text
from flask import request
Expand Down Expand Up @@ -55,7 +55,7 @@ class Slice(Model, AuditMixinNullable):
params = Column(Text)

table = relationship(
'Table', foreign_keys=[table_id], backref='slices')
'SqlaTable', foreign_keys=[table_id], backref='slices')
druid_datasource = relationship(
'Datasource', foreign_keys=[druid_datasource_id], backref='slices')

Expand Down Expand Up @@ -184,13 +184,13 @@ def get_sqla_engine(self):

def get_table(self, table_name):
meta = MetaData()
return sqlaTable(
return Table(
table_name, meta,
autoload=True,
autoload_with=self.get_sqla_engine())


class Table(Model, Queryable, AuditMixinNullable):
class SqlaTable(Model, Queryable, AuditMixinNullable):
type = "table"

__tablename__ = 'tables'
Expand All @@ -207,6 +207,12 @@ class Table(Model, Queryable, AuditMixinNullable):
def __repr__(self):
return self.table_name

@property
def perm(self):
return (
"[{self.database}].[{self.table_name}]"
"(id:{self.id})").format(self=self)

@property
def name(self):
return self.table_name
Expand Down Expand Up @@ -519,7 +525,7 @@ class SqlMetric(Model, AuditMixinNullable):
metric_type = Column(String(32))
table_id = Column(Integer, ForeignKey('tables.id'))
table = relationship(
'Table', backref='metrics', foreign_keys=[table_id])
'SqlaTable', backref='metrics', foreign_keys=[table_id])
expression = Column(Text)
description = Column(Text)

Expand All @@ -528,7 +534,8 @@ class TableColumn(Model, AuditMixinNullable):
__tablename__ = 'table_columns'
id = Column(Integer, primary_key=True)
table_id = Column(Integer, ForeignKey('tables.id'))
table = relationship('Table', backref='columns', foreign_keys=[table_id])
table = relationship(
'SqlaTable', backref='columns', foreign_keys=[table_id])
column_name = Column(String(256))
is_dttm = Column(Boolean, default=True)
is_active = Column(Boolean, default=True)
Expand Down
2 changes: 1 addition & 1 deletion panoramix/static/panoramix.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ form div {
font-size: 80px;
}
.index .carousel-caption p {
font-size: 25px;
font-size: 20px;
}
.index div.carousel-caption{
background: rgba(0,0,0,0.5);
Expand Down
2 changes: 1 addition & 1 deletion panoramix/static/panoramix.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function initializeDatasourceView() {
}
})
add_filter();
$("#druidify").click(druidify);
$(".druidify").click(druidify);

function create_choices(term, data) {
var filtered = $(data).filter(function() {
Expand Down
10 changes: 5 additions & 5 deletions panoramix/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ <h1>Panoramix</h1>
<div class="item">
<img src="{{ url_for("static", filename="img/bubble.png") }}">
<div class="carousel-caption">
<h1>Explore your data
</h1>
<h2>Explore your data
</h2>
<p>
Intuitively navigate your data while slicing, dicing, and
visualizing through a rich set of widgets
Expand All @@ -39,21 +39,21 @@ <h1>Explore your data
<div class="item">
<img src="{{ url_for("static", filename="img/dash.png") }}">
<div class="carousel-caption">
<h1>Create and share dashboards</h1>
<h2>Create and share dashboards</h2>
<p>Assemble many data visualization "slices" into a rich collection</p>
</div>
</div>
<div class="item">
<img src="{{ url_for("static", filename="img/cloud.png") }}">
<div class="carousel-caption">
<h1>Extend</h1>
<h2>Extend</h2>
<p>Join the community and take part in extending the widget library</p>
</div>
</div>
<div class="item">
<img src="{{ url_for("static", filename="img/servers.jpg") }}">
<div class="carousel-caption">
<h1>Connect</h1>
<h2>Connect</h2>
<p>
Access data from MySql, Presto.db, Postgres, RedShift, Oracle, MsSql,
SQLite, and more through the SqlAlchemy integration. You can also
Expand Down
15 changes: 10 additions & 5 deletions panoramix/templates/panoramix/datasource.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
<h3>
{{ datasource.name }}
{% if datasource.description %}
<i class="fa fa-info-circle" data-toggle="tooltip" data-placement="bottom" title="{{ datasource.description }}"></i>
<i class="fa fa-info-circle" data-toggle="tooltip" data-placement="bottom" title="{{ datasource.description }}"></i>
{% endif %}
<a href="/{{ datasource.baselink }}/edit/{{ datasource.id }}">
<i class="fa fa-edit"></i>
</a>
<div class="btn-group pull-right" role="group" >
<a class="btn btn-default" href="/{{ datasource.baselink }}/edit/{{ datasource.id }}" data-toggle="tooltip" title="Edit datasource">
<i class="fa fa-edit"></i>
</a>
<button type="button" class="btn btn-default druidify" data-toggle="tooltip" title="Druidify!">
<i class="fa fa-bolt"></i>
</button>
</div>
</h3>

<hr>
Expand Down Expand Up @@ -68,7 +73,7 @@ <h4>Filters</h4>
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
<hr>
<button type="button" class="btn btn-primary" id="druidify">
<button type="button" class="btn btn-primary druidify">
<i class="fa fa-bolt"></i>
Druidify!
</button>
Expand Down
56 changes: 54 additions & 2 deletions panoramix/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from datetime import date, datetime, timedelta
from datetime import datetime
from dateutil.parser import parse
import hashlib
from sqlalchemy.types import TypeDecorator, TEXT
import json
import parsedatetime
import functools
from panoramix import db


class memoized(object):
Expand Down Expand Up @@ -62,6 +63,12 @@ def dttm_from_timtuple(d):
d.tm_year, d.tm_mon, d.tm_mday, d.tm_hour, d.tm_min, d.tm_sec)


def merge_perm(sm, permission_name, view_menu_name):
pv = sm.find_permission_view_menu(permission_name, view_menu_name)
if not pv:
sm.add_permission_view_menu(permission_name, view_menu_name)


def parse_human_timedelta(s):
"""
Use the parsedatetime lib to return ``datetime.datetime`` from human
Expand All @@ -78,7 +85,6 @@ def parse_human_timedelta(s):
return d - dttm



class JSONEncodedDict(TypeDecorator):
"""Represents an immutable structure as a json-encoded string."""
impl = TEXT
Expand All @@ -93,6 +99,7 @@ def process_result_value(self, value, dialect):
value = json.loads(value)
return value


def color(s):
"""
Get a consistent color from the same string using a hash function
Expand All @@ -109,3 +116,48 @@ def color(s):
h = hashlib.md5(s)
i = int(h.hexdigest(), 16)
return colors[i % len(colors)]


def init():
"""
Inits the Panoramix application with security roles and such
"""
from panoramix import appbuilder
from panoramix import models
from flask_appbuilder.security.sqla import models as ab_models
sm = appbuilder.sm
alpha = sm.add_role("Alpha")

merge_perm(sm, 'all_datasource_access', 'all_datasource_access')

perms = db.session.query(ab_models.PermissionView).all()
for perm in perms:
if perm.view_menu.name not in (
'UserDBModelView', 'RoleModelView', 'ResetPasswordView',
'Security'):
sm.add_permission_role(alpha, perm)
gamma = sm.add_role("Gamma")
for perm in perms:
s = perm.permission.name
if(
perm.view_menu.name not in (
'UserDBModelView',
'RoleModelView',
'ResetPasswordView',
'Security') and
perm.permission.name not in (
'can_edit',
'can_add',
'can_save',
'can_download',
'muldelete',
'all_datasource_access',
)):
sm.add_permission_role(gamma, perm)
session = db.session()
table_perms = [
table.perm for table in session.query(models.SqlaTable).all()]
table_perms += [
table.perm for table in session.query(models.Datasource).all()]
for table_perm in table_perms:
merge_perm(sm, 'datasource_access', table.perm)
Loading