hierarchical permissions bootstrap

This commit is contained in:
Daniel Tsvetkov 2021-05-10 00:22:29 +02:00
parent 79d02aee72
commit 284cea56de
12 changed files with 129 additions and 5 deletions

View File

@ -0,0 +1,2 @@
name
admin
1 name
2 admin

View File

@ -0,0 +1,2 @@
username
daniel
1 username
2 daniel

View File

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

View File

@ -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)

View File

@ -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())

View File

@ -0,0 +1,12 @@
{% extends "layout.html" %}
{% block content %}
<h1>Admin permissions</h1>
<p>{{ _("Who can access the admin permissions page (this one!):") }}</p>
{% include "users_roles_multiselect.html" %}
{% for mv in MODEL_VIEWS %}
<h2><a href="{{ url_for(mv + '_model_permissions') }}">{{ mv }}</a></h2>
<p>{{ _("Who can access the permissions page for") }} {{ mv }}</p>
{% include "users_roles_multiselect.html" %}
{% endfor %}
{% endblock %}

View File

@ -0,0 +1,16 @@
<form>
<label>Users:
<select multiple>
{% for user in users %}
<option value="{{ user.id }}">{{ user.username }}</option>
{% endfor %}
</select>
</label>
<label>Roles:
<select multiple>
{% for user in roles %}
<option value="{{ user.id }}">{{ user.username }}</option>
{% endfor %}
</select>
</label>
</form>

View File

@ -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)

View File

@ -0,0 +1,42 @@
<table>
<tr>
<th></th>
<th>{{ _("Public") }}</th>
<th>{{ _("Logged") }}</th>
[%- if 'Ownable' in inherits %]
<th>{{ _("Owner") }}</th>
[%- endif %]
</tr>
{% for verb in ['list', 'get', 'update', 'delete'] %}
<tr>
<td>{{ verb }}</td>
{% for scope in ['public', 'logged'[%- if 'Ownable' in inherits %], 'owner'[%- endif %] ] %}
<td><input type="checkbox" id="[[ name ]]-{{ scope }}-{{ verb }}" checked="checked"/></td>
{% endfor %}
</tr>
{% endfor %}
</table>
<h2>Instance Permissions</h2>
<table>
<tr>
<th></th>
<th>{{ _("Public") }}</th>
<th>{{ _("Logged") }}</th>
[%- if 'Ownable' in inherits %]
<th>{{ _("Owner") }}</th>
[%- endif %]
</tr>
{% for column in [[ columns ]] %}
<tr>
<td>{{ column.name }}</td>
{% for scope in ['public', 'logged'[%- if 'Ownable' in inherits %], 'owner'[%- endif %] ] %}
<td>
<input type="checkbox" id="[[ name ]]-{{ column }}-{{ scope }}-read" checked="checked"/> r
<input type="checkbox" id="[[ name ]]-{{ column }}-{{ scope }}-write" checked="checked"/> w
</td>
{% endfor %}
</tr>
{% endfor %}
</table>

View File

@ -0,0 +1,9 @@
{% extends "layout.html" %}
{% block content %}
<h2>{{ _("Instance permissions for ") }} {{ _("[[ name ]]") }}: {% include "[[ name|camel_to_snake ]]/_title.html" %} </h2>
<p>{{ _("Who has specific permissions for this instance") }}</p>
{% include "users_roles_multiselect.html" %}
<br><br>
{% include "[[ name|camel_to_snake ]]/_permissions.html" %}
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends "layout.html" %}
{% block content %}
<h2>{{ _("Default permissions for") }} {{ _("[[ name ]]") }} </h2>
<p>{{ _("Who can access the model pages for") }} {{ _("[[ name ]]") }}</p>
{% include "users_roles_multiselect.html" %}
<br><br>
{% include "[[ name|camel_to_snake ]]/_permissions.html" %}
{% endblock %}

View File

@ -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 ]]/<int:instance_id>/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)