enable ACLs - users and roles

This commit is contained in:
Daniel Tsvetkov 2020-06-25 14:28:20 +02:00
parent 93cee58cfb
commit 61743a5050
7 changed files with 102 additions and 13 deletions

View File

@ -0,0 +1 @@
name
1 name

View File

@ -0,0 +1 @@
email,password,role_names
1 email password role_names

View File

@ -0,0 +1,2 @@
Role
User

View File

@ -13,6 +13,7 @@ from flask_migrate import upgrade as migrate_upgrade
from flask_migrate import init as migrate_init
from flask_security import RoleMixin, UserMixin
from flask_security import Security, SQLAlchemyUserDatastore
from flask_security.utils import encrypt_password, hash_password
from flask_sqlalchemy import SQLAlchemy
from markupsafe import escape, Markup
from sqlalchemy import Boolean
@ -266,15 +267,38 @@ def init_db(app):
return rv
SENSITIVE_PREFIX = "__SENSITIVE__."
def populate_static(app):
with app.app_context():
models = import_module("webapp.models")
sensitive = import_module("sensitive")
ordered_model_names = order_from_process_order('csv', STATIC_DATA_DIR)
for model_name in ordered_model_names:
model = getattr(models, model_name)
if model_name in ['User', 'Role']:
model = eval(model_name)
else:
model = getattr(models, model_name)
with open(os.path.join(STATIC_DATA_DIR, "{}.csv".format(model_name))) as f:
reader = csv.DictReader(f)
for row in reader:
instance = model(**row)
db.session.add(instance)
row_updates = dict()
for key, value in row.items():
if value.startswith(SENSITIVE_PREFIX):
sensitive_key = SENSITIVE_PREFIX.join(value.split(SENSITIVE_PREFIX)[1:])
sensitive_value = getattr(sensitive, sensitive_key)
row_updates[key] = sensitive_value
if row_updates:
row.update(row_updates)
if model_name == "User":
role_names = row.pop('role_names')
row['password'] = hash_password(row['password'])
user = user_datastore.create_user(**row)
for role_name in role_names.split(';'):
role = Role.query.filter_by(name=role_name).first()
user_datastore.add_role_to_user(user, role)
else:
instance = model(**row)
db.session.add(instance)
db.session.commit()

View File

@ -1,9 +1,10 @@
import importlib
from copy import deepcopy, copy
from copy import copy
from functools import wraps
import inflect
from flask import flash, render_template, redirect, request, url_for, jsonify
from flask_security import login_required, roles_required
from oshipka.persistance import db
from oshipka.util.strings import camel_case_to_snake_case
@ -172,8 +173,9 @@ class ViewContext(object):
self.redirect_next = None
def create_view(model_view, view_context, **kwargs):
def create_view(model_view, view_context, is_login_required=False, the_roles_required=None, **kwargs):
view_context.model_view = model_view
the_roles_required = [] if not the_roles_required else the_roles_required
def return_json_or_template():
if view_context.is_json:
@ -212,6 +214,10 @@ def create_view(model_view, view_context, **kwargs):
return view_context.redirect_func(view_context)
return return_json_or_template()
if is_login_required:
if the_roles_required:
inner = roles_required(*the_roles_required)(inner)
return login_required(inner)
return inner
@ -269,7 +275,7 @@ def catch_flash(f):
return f(*args, **kwargs)
except Exception as e:
flash(str(e), "error")
serialized_form = serialize_form()
serialized_form = {k: v for k, v in request.form.items()}
if '_next' in serialized_form:
_next = serialized_form.pop('_next')
elif request.referrer and request.referrer != request.path:

View File

@ -10,10 +10,12 @@ from webapp.models import [[ name ]]
from webapp.routes.[[ name|camel_to_snake ]]_hooks import *
[[ name|camel_to_snake ]] = ModelView(app, [[name]])
[[ name|camel_to_snake ]].register_verb(view_context=get_view_context, verb="get", per_item=True)
[[ name|camel_to_snake ]].register_verb(view_context=list_view_context, verb="list")
[[ name|camel_to_snake ]].register_verb(view_context=table_view_context, verb="table")
[[ name|camel_to_snake ]].register_verb(view_context=search_view_context, verb="search")
[[ name|camel_to_snake ]].register_verb(view_context=create_view_context, verb="create", methods=["GET", "POST"])
[[ name|camel_to_snake ]].register_verb(view_context=update_view_context, verb="update", methods=["GET", "POST"], per_item=True)
[[ name|camel_to_snake ]].register_verb(view_context=delete_view_context, verb="delete", methods=["GET", "POST"] , per_item=True)
[% for verb, verb_values in _verbs.items() %]
[[ name|camel_to_snake ]].register_verb(view_context=[[ verb ]]_view_context,
verb="[[ verb ]]",
methods=[[ verb_values.methods ]],
per_item=[[ verb_values.per_item ]],
is_login_required=[[ verb_values.is_login_required if verb_values.is_login_required else 'False' ]],
the_roles_required=[[ verb_values.the_roles_required if verb_values.the_roles_required else '[]' ]],
)
[% endfor %]

View File

@ -37,6 +37,38 @@ def process_secondary(view_model, column_name):
}
VERBS = {
'get': {
'per_item': 'True',
'methods': ['GET'],
},
'list': {
'per_item': 'False',
'methods': ['GET'],
},
'table': {
'per_item': 'False',
'methods': ['GET'],
},
'search': {
'per_item': 'False',
'methods': ['GET'],
},
'create': {
'per_item': 'False',
'methods': ['GET', 'POST'],
},
'update': {
'per_item': 'True',
'methods': ['GET', 'POST'],
},
'delete': {
'per_item': 'True',
'methods': ['GET', 'POST'],
},
}
def enrich_view_model(view_model):
columns = []
for column in view_model.get('columns', {}):
@ -48,6 +80,8 @@ def enrich_view_model(view_model):
_column_type = 'db.Integer'
elif column_type in ['bool', ] or column_name.startswith('is_'):
_column_type = 'LiberalBoolean'
elif column_type in ['datetime', ] or column_name.endswith('_dt'):
_column_type = 'db.UnicodeText'
elif column_type in ['relationship', ]:
_column_type = 'relationship'
multiple = column.get('multiple')
@ -68,6 +102,25 @@ def enrich_view_model(view_model):
column.update({'_type': _column_type})
columns.append(column)
view_model['columns'] = columns
view_model['_verbs'] = {}
for verb, verb_default in VERBS.items():
view_model['_verbs'][verb] = verb_default
access = view_model.get('access')
if access:
for acl in access:
verb = acl.get('verb')
if verb == 'all':
for verb in VERBS:
view_model['_verbs'][verb]['is_login_required'] = acl.get('login_required')
view_model['_verbs'][verb]['the_roles_required'] = acl.get('roles_required')
else:
is_login_required = acl.get('login_required')
if is_login_required is False: # overrides all
view_model['_verbs'][verb]['the_roles_required'] = acl.get('login_required')
view_model['_verbs'][verb]['is_login_required'] = is_login_required
view_model['_verbs'][verb]['the_roles_required'] = acl.get('roles_required')
return view_model