Change roles to permissions

This commit is contained in:
Arik Fraimovich 2014-03-12 12:45:12 +02:00
parent cff710ee52
commit 3d95d6b8c9
5 changed files with 53 additions and 33 deletions

View File

@ -7,7 +7,7 @@ if __name__ == '__main__':
db.connect_db()
migrator = Migrator(db.database)
with db.database.transaction():
migrator.add_column(models.User, models.User.roles, 'roles')
models.User.update(roles=['admin', 'viewer', 'editor']).where(models.User.is_admin == True).execute()
migrator.add_column(models.User, models.User.permissions, 'permissions')
models.User.update(permissions=['admin'] + models.User.DEFAULT_PERMISSIONS).where(models.User.is_admin == True).execute()
db.close_db(None)

View File

@ -1,16 +1,18 @@
import functools
import hashlib
import hmac
from flask import current_app, request, make_response, g, redirect, url_for
from flask.ext.googleauth import GoogleAuth, login
from flask.ext.login import LoginManager, login_user, current_user, AnonymousUserMixin
import time
import logging
from flask.ext.restful import abort
from flask import request, make_response, redirect, url_for
from flask.ext.googleauth import GoogleAuth, login
from flask.ext.login import LoginManager, login_user, current_user
from werkzeug.contrib.fixers import ProxyFix
from models import AnonymousUser
from redash import models, settings
login_manager = LoginManager()
logger = logging.getLogger('authentication')
@ -88,29 +90,6 @@ def load_user(user_id):
return models.User.select().where(models.User.id == user_id).first()
def requires_role(role):
return requires_roles((role,))
class requires_roles(object):
def __init__(self, roles):
self.roles = roles
def __call__(self, fn):
@functools.wraps(fn)
def decorated(*args, **kwargs):
has_roles = reduce(lambda a, b: a and b,
map(lambda role: role in current_user.roles, self.roles),
True)
if has_roles:
return fn(*args, **kwargs)
else:
abort(403)
return decorated
def setup_authentication(app):
if settings.GOOGLE_OPENID_ENABLED:
openid_auth = GoogleAuth(app, url_prefix="/google_auth")

View File

@ -17,7 +17,7 @@ from flask.ext.restful import Resource, abort
from flask_login import current_user, login_user, logout_user
import sqlparse
from authentication import requires_role
from permissions import require_permission
from redash import settings, utils
from redash import data
@ -84,7 +84,7 @@ def logout():
@app.route('/status.json')
@auth.required
@requires_role('admin')
@require_permission('admin')
def status_api():
status = {}
info = redis_connection.info()
@ -133,6 +133,7 @@ class DashboardListAPI(BaseResource):
return dashboards
@require_permission('create_dashboard')
def post(self):
dashboard_properties = request.get_json(force=True)
dashboard = models.Dashboard(name=dashboard_properties['name'],
@ -151,6 +152,7 @@ class DashboardAPI(BaseResource):
return dashboard.to_dict(with_widgets=True)
@require_permission('edit_dashboard')
def post(self, dashboard_slug):
dashboard_properties = request.get_json(force=True)
# TODO: either convert all requests to use slugs or ids
@ -161,6 +163,7 @@ class DashboardAPI(BaseResource):
return dashboard.to_dict(with_widgets=True)
@require_permission('edit_dashboard')
def delete(self, dashboard_slug):
dashboard = models.Dashboard.get_by_slug(dashboard_slug)
dashboard.is_archived = True
@ -171,6 +174,7 @@ api.add_resource(DashboardAPI, '/api/dashboards/<dashboard_slug>', endpoint='das
class WidgetListAPI(BaseResource):
@require_permission('edit_dashboard')
def post(self):
widget_properties = request.get_json(force=True)
widget_properties['options'] = json.dumps(widget_properties['options'])
@ -202,6 +206,7 @@ class WidgetListAPI(BaseResource):
class WidgetAPI(BaseResource):
@require_permission('edit_dashboard')
def delete(self, widget_id):
widget = models.Widget.get(models.Widget.id == widget_id)
# TODO: reposition existing ones
@ -218,6 +223,7 @@ api.add_resource(WidgetAPI, '/api/widgets/<int:widget_id>', endpoint='widget')
class QueryListAPI(BaseResource):
@require_permission('create_query')
def post(self):
query_def = request.get_json(force=True)
# id, created_at, api_key
@ -237,6 +243,7 @@ class QueryListAPI(BaseResource):
class QueryAPI(BaseResource):
@require_permission('edit_query')
def post(self, query_id):
query_def = request.get_json(force=True)
for field in ['id', 'created_at', 'api_key', 'visualizations', 'latest_query_data', 'user']:
@ -263,6 +270,7 @@ api.add_resource(QueryAPI, '/api/queries/<query_id>', endpoint='query')
class VisualizationListAPI(BaseResource):
@require_permission('edit_query')
def post(self):
kwargs = request.get_json(force=True)
kwargs['options'] = json.dumps(kwargs['options'])
@ -275,6 +283,7 @@ class VisualizationListAPI(BaseResource):
class VisualizationAPI(BaseResource):
@require_permission('edit_query')
def post(self, visualization_id):
kwargs = request.get_json(force=True)
if 'options' in kwargs:
@ -288,6 +297,7 @@ class VisualizationAPI(BaseResource):
return vis.to_dict(with_query=False)
@require_permission('edit_query')
def delete(self, visualization_id):
vis = models.Visualization.get(models.Visualization.id == visualization_id)
vis.delete_instance()
@ -297,6 +307,7 @@ api.add_resource(VisualizationAPI, '/api/visualizations/<visualization_id>', end
class QueryResultListAPI(BaseResource):
@require_permission('execute_query')
def post(self):
params = request.json

View File

@ -18,17 +18,20 @@ class BaseModel(db.Model):
class AnonymousUser(AnonymousUserMixin):
@property
def roles(self):
def permissions(self):
return []
class User(BaseModel, UserMixin):
DEFAULT_PERMISSIONS = ['create_dashboard', 'create_query', 'edit_dashboard', 'edit_query',
'execute_query']
id = peewee.PrimaryKeyField()
name = peewee.CharField(max_length=320)
email = peewee.CharField(max_length=320, index=True, unique=True)
password_hash = peewee.CharField(max_length=128, null=True)
is_admin = peewee.BooleanField(default=False)
roles = ArrayField(peewee.CharField, default=['editor', 'viewer'])
permissions = ArrayField(peewee.CharField, default=DEFAULT_PERMISSIONS)
class Meta:
db_table = 'users'

27
redash/permissions.py Normal file
View File

@ -0,0 +1,27 @@
import functools
from flask.ext.login import current_user
from flask.ext.restful import abort
class require_permissions(object):
def __init__(self, permissions):
self.permissions = permissions
def __call__(self, fn):
@functools.wraps(fn)
def decorated(*args, **kwargs):
has_permissions = reduce(lambda a, b: a and b,
map(lambda permission: permission in current_user.permissions,
self.permissions),
True)
if has_permissions:
return fn(*args, **kwargs)
else:
abort(403)
return decorated
def require_permission(permission):
return require_permissions((permission,))