diff --git a/bootstrap/data_static/Role.csv b/bootstrap/data_static/Role.csv new file mode 100644 index 0000000..934edc8 --- /dev/null +++ b/bootstrap/data_static/Role.csv @@ -0,0 +1 @@ +name \ No newline at end of file diff --git a/bootstrap/data_static/User.csv b/bootstrap/data_static/User.csv new file mode 100644 index 0000000..a1d1331 --- /dev/null +++ b/bootstrap/data_static/User.csv @@ -0,0 +1 @@ +email,password,role_names \ No newline at end of file diff --git a/bootstrap/data_static/_process_order b/bootstrap/data_static/_process_order index e69de29..942a1ea 100644 --- a/bootstrap/data_static/_process_order +++ b/bootstrap/data_static/_process_order @@ -0,0 +1,2 @@ +Role +User \ No newline at end of file diff --git a/oshipka/persistance/__init__.py b/oshipka/persistance/__init__.py index 3ae9748..5274de7 100644 --- a/oshipka/persistance/__init__.py +++ b/oshipka/persistance/__init__.py @@ -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() diff --git a/oshipka/webapp/views.py b/oshipka/webapp/views.py index 128535e..c9ab271 100644 --- a/oshipka/webapp/views.py +++ b/oshipka/webapp/views.py @@ -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: diff --git a/vm_gen/templates/routes_py b/vm_gen/templates/routes_py index c91d29b..82e6c73 100644 --- a/vm_gen/templates/routes_py +++ b/vm_gen/templates/routes_py @@ -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 %] diff --git a/vm_gen/vm_gen.py b/vm_gen/vm_gen.py index 5aa87ca..c13e9f9 100644 --- a/vm_gen/vm_gen.py +++ b/vm_gen/vm_gen.py @@ -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