Skip to content

Commit

Permalink
Merge pull request #35 from mistercrunch/roles
Browse files Browse the repository at this point in the history
A first draft on default security roles
  • Loading branch information
mistercrunch committed Oct 4, 2015
2 parents 8eb0c0a + 6ea178e commit a32cf0c
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 51 deletions.
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

0 comments on commit a32cf0c

Please sign in to comment.