diff --git a/manage.py b/manage.py
index a00bdad3c..3b90cd59a 100755
--- a/manage.py
+++ b/manage.py
@@ -81,19 +81,20 @@ def drop_tables():
@users_manager.option('--admin', dest='is_admin', action="store_true", default=False, help="set user as admin")
@users_manager.option('--google', dest='google_auth', action="store_true", default=False, help="user uses Google Auth to login")
@users_manager.option('--password', dest='password', default=None, help="Password for users who don't use Google Auth (leave blank for prompt).")
-@users_manager.option('--permissions', dest='permissions', default=models.User.DEFAULT_PERMISSIONS, help="Comma seperated list of permissions (leave blank for default).")
-def create(email, name, permissions, is_admin=False, google_auth=False, password=None):
+@users_manager.option('--groups', dest='groups', default=['default'], help="Comma seperated list of groups (leave blank for default).")
+def create(email, name, groups, is_admin=False, google_auth=False, password=None):
print "Creating user (%s, %s)..." % (email, name)
print "Admin: %r" % is_admin
print "Login with Google Auth: %r\n" % google_auth
- if isinstance(permissions, basestring):
- permissions = permissions.split(',')
- permissions.remove('') # in case it was empty string
+ if isinstance(groups, basestring) and len(groups) > 0:
+ groups = groups.split(',')
+ else:
+ groups = ['default']
if is_admin:
- permissions += ['admin']
+ groups += ['admin']
- user = models.User(email=email, name=name, permissions=permissions)
+ user = models.User(email=email, name=name, groups=groups)
if not google_auth:
password = password or prompt_pass("Password")
user.hash_password(password)
diff --git a/migrations/permissions_migration.py b/migrations/permissions_migration.py
new file mode 100644
index 000000000..5a91621ac
--- /dev/null
+++ b/migrations/permissions_migration.py
@@ -0,0 +1,24 @@
+from playhouse.migrate import Migrator
+from redash import db
+from redash import models
+
+
+if __name__ == '__main__':
+ db.connect_db()
+ migrator = Migrator(db.database)
+
+ if not models.Group.table_exists():
+ print "Creating groups table..."
+ models.Group.create_table()
+
+ with db.database.transaction():
+ models.Group.insert(name='admin', permissions=['admin'], tables=['*']).execute()
+ models.Group.insert(name='default', permissions=models.Group.DEFAULT_PERMISSIONS, tables=['*']).execute()
+
+ migrator.drop_column(models.User, 'permissions')
+ migrator.add_column(models.User, models.User.groups, 'groups')
+
+ models.User.update(groups=['admin', 'default']).where(models.User.is_admin == True).execute()
+ models.User.update(groups=['default']).where(models.User.is_admin == False).execute()
+
+ db.close_db(None)
diff --git a/rd_ui/app/index.html b/rd_ui/app/index.html
index 67cac81dd..fae145fc2 100644
--- a/rd_ui/app/index.html
+++ b/rd_ui/app/index.html
@@ -36,7 +36,7 @@
- -
+
-
diff --git a/rd_ui/bower.json b/rd_ui/bower.json
index 5f9c86d88..97492c9b4 100644
--- a/rd_ui/bower.json
+++ b/rd_ui/bower.json
@@ -20,7 +20,8 @@
"cornelius": "https://github.com/restorando/cornelius.git",
"gridster": "0.2.0",
"mousetrap": "~1.4.6",
- "angular-ui-select2": "~0.0.5"
+ "angular-ui-select2": "~0.0.5",
+ "jquery-ui": "~1.10.4"
},
"devDependencies": {
"angular-mocks": "~1.0.7",
diff --git a/redash/authentication.py b/redash/authentication.py
index fe3258f97..a240c626a 100644
--- a/redash/authentication.py
+++ b/redash/authentication.py
@@ -79,7 +79,8 @@ def create_and_login_user(app, user):
except models.User.DoesNotExist:
logger.debug("Creating user object (%r)", user.name)
user_object = models.User.create(name=user.name, email=user.email,
- is_admin=(user.email in settings.ADMINS))
+ is_admin=(user.email in settings.ADMINS),
+ groups = ['admin', 'default'] if (user.email in settings.ADMINS) else ['default'])
login_user(user_object, remember=True)
diff --git a/redash/controllers.py b/redash/controllers.py
index 7ebde0a84..886288d8b 100644
--- a/redash/controllers.py
+++ b/redash/controllers.py
@@ -10,6 +10,7 @@ import json
import numbers
import cStringIO
import datetime
+import itertools
from flask import render_template, send_from_directory, make_response, request, jsonify, redirect, \
session, url_for
@@ -47,7 +48,8 @@ def index(**kwargs):
'id': current_user.id,
'name': current_user.name,
'email': current_user.email,
- 'permissions': current_user.permissions
+ 'groups': current_user.groups,
+ 'permissions': list(itertools.chain(*[g.permissions for g in models.Group.select().where(models.Group.name << current_user.groups)]))
}
return render_template("index.html", user=json.dumps(user), name=settings.NAME,
diff --git a/redash/models.py b/redash/models.py
index 44c313819..a5d4001c5 100644
--- a/redash/models.py
+++ b/redash/models.py
@@ -31,16 +31,39 @@ class ApiUser(UserMixin):
return ['view_query']
-class User(BaseModel, UserMixin):
+class Group(BaseModel):
DEFAULT_PERMISSIONS = ['create_dashboard', 'create_query', 'edit_dashboard', 'edit_query',
'view_query', 'view_source', 'execute_query']
+
+ id = peewee.PrimaryKeyField()
+ name = peewee.CharField(max_length=100)
+ permissions = ArrayField(peewee.CharField, default=DEFAULT_PERMISSIONS)
+ tables = ArrayField(peewee.CharField)
+ created_at = peewee.DateTimeField(default=datetime.datetime.now)
+ class Meta:
+ db_table = 'groups'
+
+ def to_dict(self):
+ return {
+ 'id': self.id,
+ 'name': self.name,
+ 'permissions': self.permissions,
+ 'tables': self.tables,
+ 'created_at': self.created_at
+ }
+
+ def __unicode__(self):
+ return unicode(self.id)
+
+
+class User(BaseModel, UserMixin):
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)
- permissions = ArrayField(peewee.CharField, default=DEFAULT_PERMISSIONS)
+ groups = ArrayField(peewee.CharField, default=['default'])
class Meta:
db_table = 'users'
@@ -87,7 +110,6 @@ class ActivityLog(BaseModel):
def __unicode__(self):
return unicode(self.id)
-
class DataSource(BaseModel):
id = peewee.PrimaryKeyField()
name = peewee.CharField()
@@ -350,7 +372,7 @@ class Widget(BaseModel):
def __unicode__(self):
return u"%s" % self.id
-all_models = (DataSource, User, QueryResult, Query, Dashboard, Visualization, Widget, ActivityLog)
+all_models = (DataSource, User, QueryResult, Query, Dashboard, Visualization, Widget, ActivityLog, Group)
def create_db(create_tables, drop_tables):