From 284cea56de5403439c7497fda02415e56efe530a Mon Sep 17 00:00:00 2001 From: Daniel Tsvetkov Date: Mon, 10 May 2021 00:22:29 +0200 Subject: [PATCH] hierarchical permissions bootstrap --- bootstrap/data_static/Role.csv | 2 + bootstrap/data_static/User.csv | 2 + bootstrap/data_static/_process_order | 2 + oshipka/persistance/__init__.py | 7 +++- oshipka/webapp/default_routes.py | 12 +++++- oshipka/webapp/templates/permissions.html | 12 ++++++ .../templates/users_roles_multiselect.html | 16 +++++++ oshipka/webapp/views.py | 4 +- vm_gen/templates/html/_permissions.html | 42 +++++++++++++++++++ .../templates/html/permissions_instance.html | 9 ++++ vm_gen/templates/html/permissions_model.html | 9 ++++ vm_gen/templates/routes_py | 17 ++++++++ 12 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 bootstrap/data_static/Role.csv create mode 100644 bootstrap/data_static/User.csv create mode 100644 oshipka/webapp/templates/permissions.html create mode 100644 oshipka/webapp/templates/users_roles_multiselect.html create mode 100644 vm_gen/templates/html/_permissions.html create mode 100644 vm_gen/templates/html/permissions_instance.html create mode 100644 vm_gen/templates/html/permissions_model.html diff --git a/bootstrap/data_static/Role.csv b/bootstrap/data_static/Role.csv new file mode 100644 index 0000000..fa554c4 --- /dev/null +++ b/bootstrap/data_static/Role.csv @@ -0,0 +1,2 @@ +name +admin \ 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..8e2670a --- /dev/null +++ b/bootstrap/data_static/User.csv @@ -0,0 +1,2 @@ +username +daniel \ 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 4b7c0b9..bc7a9d9 100644 --- a/oshipka/persistance/__init__.py +++ b/oshipka/persistance/__init__.py @@ -187,6 +187,8 @@ if SECURITY_ENABLED: name = db.Column(db.Unicode) profile_image_url = db.Column(db.String) + _m_n_table_roles = 'Role' + roles = db.relationship('Role', secondary=roles_users, backref=db.backref('users', lazy='dynamic')) @@ -379,7 +381,10 @@ def update_m_ns(instance, m_ns): instance = instance for key, ids in m_ns.items(): child_rel = getattr(instance, "_m_n_table_{}".format(key)) - child_table = getattr(webapp_models, child_rel) + if key not in ['roles']: + child_table = getattr(webapp_models, child_rel) + else: + child_table = Role children = db.session.query(child_table).filter(child_table.id.in_(ids)).all() setattr(instance, key, children) diff --git a/oshipka/webapp/default_routes.py b/oshipka/webapp/default_routes.py index 4ea1daf..e799f9e 100644 --- a/oshipka/webapp/default_routes.py +++ b/oshipka/webapp/default_routes.py @@ -1,8 +1,8 @@ import urllib import requests -from flask import send_from_directory, redirect, request, url_for, session, jsonify, abort -from flask_security import login_user +from flask import send_from_directory, redirect, request, url_for, session, jsonify, abort, render_template +from flask_login import login_required, current_user from oshipka.util.strings import random_string_generator from oshipka.webapp import oshipka_bp, app @@ -28,6 +28,7 @@ def get_media(model_name, instance_id, column, filepath): if SECURITY_ENABLED: + from flask_security import login_user, roles_required from oshipka.persistance import User, db app.config['SSO_BASE_URL'] = SSO_BASE_URL @@ -99,3 +100,10 @@ if SECURITY_ENABLED: del session['oidc_state'] return redirect(redirect_uri) return response.text + + + @oshipka_bp.route('/permissions') + @login_required + @roles_required(*['admin']) + def get_permissions(): + return render_template('permissions.html', MODEL_VIEWS=MODEL_VIEWS, users=User.query.all()) diff --git a/oshipka/webapp/templates/permissions.html b/oshipka/webapp/templates/permissions.html new file mode 100644 index 0000000..9a5761b --- /dev/null +++ b/oshipka/webapp/templates/permissions.html @@ -0,0 +1,12 @@ +{% extends "layout.html" %} + +{% block content %} +

Admin permissions

+

{{ _("Who can access the admin permissions page (this one!):") }}

+ {% include "users_roles_multiselect.html" %} + {% for mv in MODEL_VIEWS %} +

{{ mv }}

+

{{ _("Who can access the permissions page for") }} {{ mv }}

+ {% include "users_roles_multiselect.html" %} + {% endfor %} +{% endblock %} \ No newline at end of file diff --git a/oshipka/webapp/templates/users_roles_multiselect.html b/oshipka/webapp/templates/users_roles_multiselect.html new file mode 100644 index 0000000..badf046 --- /dev/null +++ b/oshipka/webapp/templates/users_roles_multiselect.html @@ -0,0 +1,16 @@ +
+ + +
\ No newline at end of file diff --git a/oshipka/webapp/views.py b/oshipka/webapp/views.py index d9dfca3..bdc2b7c 100644 --- a/oshipka/webapp/views.py +++ b/oshipka/webapp/views.py @@ -182,10 +182,10 @@ def default_create_func(vc): def create_acls(model_acl, instance, user): instance_public_acl = model_acl(user=user, instance=instance, acl_type=SHARING_TYPE_TYPES_TYPE_PUBLIC) db.session.add(instance_public_acl) + instance_authn_acl = model_acl(instance=instance, acl_type=SHARING_TYPE_TYPES_TYPE_AUTHN) + db.session.add(instance_authn_acl) if user: - instance_authn_acl = model_acl(user=user, instance=instance, acl_type=SHARING_TYPE_TYPES_TYPE_AUTHN) instance_authz_acl = model_acl(user=user, instance=instance, acl_type=SHARING_TYPE_TYPES_TYPE_AUTHZ) - db.session.add(instance_authn_acl) db.session.add(instance_authz_acl) diff --git a/vm_gen/templates/html/_permissions.html b/vm_gen/templates/html/_permissions.html new file mode 100644 index 0000000..a30f8e9 --- /dev/null +++ b/vm_gen/templates/html/_permissions.html @@ -0,0 +1,42 @@ + + + + + + [%- if 'Ownable' in inherits %] + + [%- endif %] + + {% for verb in ['list', 'get', 'update', 'delete'] %} + + + {% for scope in ['public', 'logged'[%- if 'Ownable' in inherits %], 'owner'[%- endif %] ] %} + + {% endfor %} + + {% endfor %} +
{{ _("Public") }}{{ _("Logged") }}{{ _("Owner") }}
{{ verb }}
+ +

Instance Permissions

+ + + + + + + [%- if 'Ownable' in inherits %] + + [%- endif %] + + {% for column in [[ columns ]] %} + + + {% for scope in ['public', 'logged'[%- if 'Ownable' in inherits %], 'owner'[%- endif %] ] %} + + {% endfor %} + + {% endfor %} +
{{ _("Public") }}{{ _("Logged") }}{{ _("Owner") }}
{{ column.name }} + r + w +
\ No newline at end of file diff --git a/vm_gen/templates/html/permissions_instance.html b/vm_gen/templates/html/permissions_instance.html new file mode 100644 index 0000000..a957558 --- /dev/null +++ b/vm_gen/templates/html/permissions_instance.html @@ -0,0 +1,9 @@ +{% extends "layout.html" %} + +{% block content %} +

{{ _("Instance permissions for ") }} {{ _("[[ name ]]") }}: {% include "[[ name|camel_to_snake ]]/_title.html" %}

+

{{ _("Who has specific permissions for this instance") }}

+ {% include "users_roles_multiselect.html" %} +

+ {% include "[[ name|camel_to_snake ]]/_permissions.html" %} +{% endblock %} \ No newline at end of file diff --git a/vm_gen/templates/html/permissions_model.html b/vm_gen/templates/html/permissions_model.html new file mode 100644 index 0000000..449e451 --- /dev/null +++ b/vm_gen/templates/html/permissions_model.html @@ -0,0 +1,9 @@ +{% extends "layout.html" %} + +{% block content %} +

{{ _("Default permissions for") }} {{ _("[[ name ]]") }}

+

{{ _("Who can access the model pages for") }} {{ _("[[ name ]]") }}

+ {% include "users_roles_multiselect.html" %} +

+ {% include "[[ name|camel_to_snake ]]/_permissions.html" %} +{% endblock %} \ No newline at end of file diff --git a/vm_gen/templates/routes_py b/vm_gen/templates/routes_py index 7acd12b..9221c79 100644 --- a/vm_gen/templates/routes_py +++ b/vm_gen/templates/routes_py @@ -4,6 +4,9 @@ Edit the hooks in webapp/routes/[[ name|camel_to_snake ]]_hooks.py instead """ +from flask import render_template +from flask_security import login_required + from oshipka.webapp import app from oshipka.webapp.views import ModelView from webapp.routes.[[ name|camel_to_snake ]]_hooks import * @@ -29,3 +32,17 @@ from webapp.models import [[ name ]], [[ name ]]Acl the_roles_required=[[ verb_values.the_roles_required if verb_values.the_roles_required else '[]' ]], ) [% endfor %] + +@app.route("/[[ name|camel_to_snake|pluralize ]]/permissions") +@login_required +def [[ name|camel_to_snake ]]_model_permissions(): + return render_template("[[ name|camel_to_snake ]]/permissions_model.html") + + +@app.route("/[[ name|camel_to_snake|pluralize ]]//permissions") +@login_required +def [[ name|camel_to_snake ]]_instance_permissions(instance_id): + instance = [[ name ]].query.filter_by(id=instance_id).first() + if not instance: + abort(404) + return render_template("[[ name|camel_to_snake ]]/permissions_instance.html", instance=instance) \ No newline at end of file