initial permissions table
This commit is contained in:
parent
284cea56de
commit
878a7c5947
0
bootstrap/data_static/Permission.csv
Normal file
0
bootstrap/data_static/Permission.csv
Normal file
|
18
bootstrap/webapp/view_models/Permission.yaml
Normal file
18
bootstrap/webapp/view_models/Permission.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
name: Permission
|
||||||
|
access:
|
||||||
|
- verb: all
|
||||||
|
login_required: true
|
||||||
|
columns:
|
||||||
|
- name: subject
|
||||||
|
- name: subject_id
|
||||||
|
type: int
|
||||||
|
- name: action
|
||||||
|
- name: object
|
||||||
|
- name: subject_id
|
||||||
|
type: int
|
||||||
|
- name: is_allowed
|
||||||
|
type: boolean
|
||||||
|
display:
|
||||||
|
primary: subject
|
||||||
|
secondary: action
|
||||||
|
tertiary: object
|
@ -328,6 +328,59 @@ def init_db(app):
|
|||||||
|
|
||||||
SENSITIVE_PREFIX = "__SENSITIVE__."
|
SENSITIVE_PREFIX = "__SENSITIVE__."
|
||||||
|
|
||||||
|
"""
|
||||||
|
role,1,permission.get,models.song,,1
|
||||||
|
role,1,permission.add_user,models.song,,1
|
||||||
|
role,1,permission.add_role,models.song,,1
|
||||||
|
role,1,permission.update,models.song,,1
|
||||||
|
role,1,permission.delete_user,models.song,,1
|
||||||
|
role,1,permission.delete_user_self,models.song,,1
|
||||||
|
role,1,permission.delete_role,models.song,,1
|
||||||
|
role,1,permission.change_owner,models.song,,1
|
||||||
|
role,1,model.get,models.song,,1
|
||||||
|
role,1,model.list,models.song,,1
|
||||||
|
role,1,model.create,models.song,,1
|
||||||
|
role,1,model.update,models.song,,1
|
||||||
|
role,1,model.delete,models.song,,1
|
||||||
|
|
||||||
|
public,,column.get,columns.song.audio_filename.read,,1
|
||||||
|
public,,column.get,columns.song.audio_filename.write,,1
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEFAULT_PERMISSION_PERMISSIONS = ['get', 'add_user', 'add_role', 'delete_user', 'delete_role']
|
||||||
|
DEFAULT_MODEL_PERMISSIONS = ['get', 'list', 'create', 'update', 'delete']
|
||||||
|
DEFAULT_COLUMN_PERMISSIONS = ['read', 'write']
|
||||||
|
DEFAULT_SUBJECTS = ['public', 'logged']
|
||||||
|
|
||||||
|
|
||||||
|
def generate_permissions():
|
||||||
|
from oshipka.webapp.views import MODEL_VIEWS
|
||||||
|
with open(os.path.join(STATIC_DATA_DIR, "Permission.csv"), 'w') as f:
|
||||||
|
f.write("subject,subject_id,action,object,object_id,is_allowed\n")
|
||||||
|
for permission in DEFAULT_PERMISSION_PERMISSIONS:
|
||||||
|
f.write("role,1,permission.{},admin.permissions,,1\n".format(permission))
|
||||||
|
for model, model_view in MODEL_VIEWS.items():
|
||||||
|
if model in ['permission']:
|
||||||
|
continue
|
||||||
|
is_ownable = 'Ownable' in model_view.definitions.get('inherits', [])
|
||||||
|
subjects = DEFAULT_SUBJECTS + ['owner']if is_ownable else DEFAULT_SUBJECTS
|
||||||
|
for subject in subjects:
|
||||||
|
for permission in DEFAULT_PERMISSION_PERMISSIONS:
|
||||||
|
f.write("{},,permission.{},models.{},,0\n".format(subject, permission, model))
|
||||||
|
f.write("role,1,permission.{},models.{},,1\n".format(permission, model))
|
||||||
|
f.write("{},,permission.update,models.{},,0\n".format(subject, model))
|
||||||
|
f.write("role,1,permission.update,models.{},,1\n".format(subject, model))
|
||||||
|
f.write("{},,permission.delete_user_self,models.{},,0\n".format(subject, model))
|
||||||
|
f.write("role,1,permission.delete_user_self,models.{},,1\n".format(subject, model))
|
||||||
|
if is_ownable:
|
||||||
|
f.write("{},,permission.change_owner,models.{},,1\n".format(subject, model))
|
||||||
|
for permission in DEFAULT_MODEL_PERMISSIONS:
|
||||||
|
f.write("{},,model.{},models.{},,1\n".format(subject, permission, model))
|
||||||
|
for column in model_view.definitions.get('columns'):
|
||||||
|
column_name = column.get('name')
|
||||||
|
for permission in DEFAULT_COLUMN_PERMISSIONS:
|
||||||
|
f.write("{},,column.{}.{},columns.{},,1\n".format(subject, column_name, permission, model))
|
||||||
|
|
||||||
|
|
||||||
def populate_static(app):
|
def populate_static(app):
|
||||||
print("populating...")
|
print("populating...")
|
||||||
@ -341,10 +394,10 @@ def populate_static(app):
|
|||||||
for model_name in ordered_model_names:
|
for model_name in ordered_model_names:
|
||||||
if SECURITY_ENABLED and model_name in ['User', 'Role']:
|
if SECURITY_ENABLED and model_name in ['User', 'Role']:
|
||||||
model = eval(model_name)
|
model = eval(model_name)
|
||||||
model_acl = None
|
|
||||||
else:
|
else:
|
||||||
|
if SECURITY_ENABLED and model_name in ['Permission']:
|
||||||
|
generate_permissions()
|
||||||
model = getattr(models, model_name)
|
model = getattr(models, model_name)
|
||||||
model_acl = getattr(models, model_name + 'Acl')
|
|
||||||
with open(os.path.join(STATIC_DATA_DIR, "{}.csv".format(model_name))) as f:
|
with open(os.path.join(STATIC_DATA_DIR, "{}.csv".format(model_name))) as f:
|
||||||
if issubclass(model, Ownable):
|
if issubclass(model, Ownable):
|
||||||
user = User.query.first()
|
user = User.query.first()
|
||||||
@ -360,7 +413,7 @@ def populate_static(app):
|
|||||||
row_updates[key] = sensitive_value
|
row_updates[key] = sensitive_value
|
||||||
if row_updates:
|
if row_updates:
|
||||||
row.update(row_updates)
|
row.update(row_updates)
|
||||||
instance = create_model(model, model_acl, user, row)
|
instance = create_model(model, user, row)
|
||||||
db.session.add(instance)
|
db.session.add(instance)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
print("Finished populating")
|
print("Finished populating")
|
||||||
@ -389,8 +442,7 @@ def update_m_ns(instance, m_ns):
|
|||||||
setattr(instance, key, children)
|
setattr(instance, key, children)
|
||||||
|
|
||||||
|
|
||||||
def create_model(model, model_acl, user, serialized_args):
|
def create_model(model, user, serialized_args):
|
||||||
from oshipka.webapp.views import create_acls
|
|
||||||
m_ns, to_delete = filter_m_n(serialized_args)
|
m_ns, to_delete = filter_m_n(serialized_args)
|
||||||
for key in to_delete:
|
for key in to_delete:
|
||||||
del serialized_args[key]
|
del serialized_args[key]
|
||||||
@ -400,6 +452,4 @@ def create_model(model, model_acl, user, serialized_args):
|
|||||||
for key, ids in m_ns.items():
|
for key, ids in m_ns.items():
|
||||||
m_ns[key] = ids.split(',')
|
m_ns[key] = ids.split(',')
|
||||||
update_m_ns(instance, m_ns)
|
update_m_ns(instance, m_ns)
|
||||||
if model_acl:
|
|
||||||
create_acls(model_acl, instance, user)
|
|
||||||
return instance
|
return instance
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import urllib
|
import urllib
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from flask import send_from_directory, redirect, request, url_for, session, jsonify, abort, render_template
|
from flask import send_from_directory, redirect, request, url_for, session, jsonify, abort, render_template
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
|
from sqlalchemy import and_
|
||||||
|
|
||||||
from oshipka.util.strings import random_string_generator
|
from oshipka.util.strings import random_string_generator
|
||||||
from oshipka.webapp import oshipka_bp, app
|
from oshipka.webapp import oshipka_bp, app
|
||||||
@ -29,7 +31,8 @@ def get_media(model_name, instance_id, column, filepath):
|
|||||||
|
|
||||||
if SECURITY_ENABLED:
|
if SECURITY_ENABLED:
|
||||||
from flask_security import login_user, roles_required
|
from flask_security import login_user, roles_required
|
||||||
from oshipka.persistance import User, db
|
from oshipka.persistance import User, Role, db
|
||||||
|
from webapp.models import Permission
|
||||||
|
|
||||||
app.config['SSO_BASE_URL'] = SSO_BASE_URL
|
app.config['SSO_BASE_URL'] = SSO_BASE_URL
|
||||||
|
|
||||||
@ -102,8 +105,120 @@ if SECURITY_ENABLED:
|
|||||||
return response.text
|
return response.text
|
||||||
|
|
||||||
|
|
||||||
@oshipka_bp.route('/permissions')
|
def get_permissions_table(query):
|
||||||
|
permissions = Permission.query.filter(query).all()
|
||||||
|
permissions_table = dict(
|
||||||
|
selected_roles=list(),
|
||||||
|
selected_users=list(),
|
||||||
|
actions=set(),
|
||||||
|
table=defaultdict(dict),
|
||||||
|
)
|
||||||
|
for p in permissions:
|
||||||
|
if p.subject in ['user']:
|
||||||
|
permissions_table['selected_users'].append(p.subject_id)
|
||||||
|
if p.subject in ['role']:
|
||||||
|
permissions_table['selected_roles'].append(p.subject_id)
|
||||||
|
s = "{}_{}".format(p.subject, p.subject_id) if p.subject_id else p.subject
|
||||||
|
permissions_table["table"][s][p.action] = p.is_allowed
|
||||||
|
permissions_table["actions"].add(p.action)
|
||||||
|
permissions_table["actions"] = sorted(permissions_table["actions"])
|
||||||
|
return permissions_table
|
||||||
|
|
||||||
|
|
||||||
|
@oshipka_bp.route('/permissions', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@roles_required(*['admin'])
|
@roles_required(*['admin'])
|
||||||
def get_permissions():
|
def get_permissions():
|
||||||
return render_template('permissions.html', MODEL_VIEWS=MODEL_VIEWS, users=User.query.all())
|
admin_permissions_table = get_permissions_table(Permission.object == 'admin.permissions')
|
||||||
|
model_tables = {}
|
||||||
|
for mv in MODEL_VIEWS:
|
||||||
|
model_tables[mv] = get_permissions_table(and_(
|
||||||
|
Permission.action == 'permission.get',
|
||||||
|
Permission.object == 'models.{}'.format(mv)
|
||||||
|
))
|
||||||
|
return render_template('permissions_admin.html',
|
||||||
|
admin_permissions_table=admin_permissions_table,
|
||||||
|
model_tables=model_tables,
|
||||||
|
model_views=[k for k in MODEL_VIEWS.keys() if k not in ['permission']],
|
||||||
|
users=User.query.all(),
|
||||||
|
roles=Role.query.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_model_tables(model_name, instance_id=None):
|
||||||
|
model_view = MODEL_VIEWS.get(model_name)
|
||||||
|
if not model_view:
|
||||||
|
abort(404)
|
||||||
|
if instance_id:
|
||||||
|
model_query = and_(Permission.object == 'models.{}'.format(model_name),
|
||||||
|
Permission.object_id == instance_id)
|
||||||
|
column_query = and_(Permission.object == 'columns.{}'.format(model_name),
|
||||||
|
Permission.object_id == instance_id)
|
||||||
|
else:
|
||||||
|
model_query = Permission.object == 'models.{}'.format(model_name)
|
||||||
|
column_query = Permission.object == 'columns.{}'.format(model_name)
|
||||||
|
model_permissions_table = get_permissions_table(model_query)
|
||||||
|
column_permissions_table = get_permissions_table(column_query)
|
||||||
|
return model_permissions_table, column_permissions_table
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/permissions/<model_name>", methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def model_permissions(model_name):
|
||||||
|
if request.method == "POST":
|
||||||
|
data = {k: len(request.form.getlist(k)) - 1 for k in request.form.keys()}
|
||||||
|
for k, v in data.items():
|
||||||
|
if k in ['csrf_token']:
|
||||||
|
continue
|
||||||
|
_, subject, action = k.split('-')
|
||||||
|
sub_split, subject_id = subject.split('_'), None
|
||||||
|
if len(sub_split) == 2:
|
||||||
|
subject, subject_id = sub_split
|
||||||
|
if action.startswith('model.') or action.startswith('permission.'):
|
||||||
|
obj = "models.{}"
|
||||||
|
elif action.startswith('column.'):
|
||||||
|
obj = "columns.{}"
|
||||||
|
else:
|
||||||
|
obj = "{}"
|
||||||
|
abort(403)
|
||||||
|
if not subject_id:
|
||||||
|
existing_permission = Permission.query.filter_by(
|
||||||
|
subject=subject, action=action, object=obj.format(model_name)).first()
|
||||||
|
else:
|
||||||
|
existing_permission = Permission.query.filter_by(
|
||||||
|
subject=subject, subject_id=subject_id, action=action, object=obj.format(model_name)).first()
|
||||||
|
if not existing_permission:
|
||||||
|
existing_permission = Permission(
|
||||||
|
subject=subject, subject_id=subject_id, action=action, object=obj.format(model_name),
|
||||||
|
)
|
||||||
|
existing_permission.is_allowed = v
|
||||||
|
db.session.add(existing_permission)
|
||||||
|
db.session.commit()
|
||||||
|
return redirect(request.referrer)
|
||||||
|
model_permissions_table, column_permissions_table = get_model_tables(model_name)
|
||||||
|
return render_template("permissions_model.html",
|
||||||
|
model_permissions_table=model_permissions_table,
|
||||||
|
column_permissions_table=column_permissions_table,
|
||||||
|
model_name=model_name,
|
||||||
|
users=User.query.all(),
|
||||||
|
roles=Role.query.all(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/permissions/<model_name>/<int:instance_id>", methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def instance_permissions(model_name, instance_id):
|
||||||
|
model_view = MODEL_VIEWS.get(model_name)
|
||||||
|
if not model_view:
|
||||||
|
abort(404)
|
||||||
|
instance = model_view.model.query.filter_by(id=instance_id).first()
|
||||||
|
if not instance:
|
||||||
|
abort(404)
|
||||||
|
model_permissions_table, column_permissions_table = get_model_tables(model_name, instance_id)
|
||||||
|
|
||||||
|
return render_template("permissions_instance.html",
|
||||||
|
model_permissions_table=model_permissions_table,
|
||||||
|
column_permissions_table=column_permissions_table,
|
||||||
|
model_name=model_name,
|
||||||
|
instance=instance,
|
||||||
|
)
|
||||||
|
26
oshipka/webapp/templates/_permission_table.html
Normal file
26
oshipka/webapp/templates/_permission_table.html
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
{% for subject in instance_table['table'].keys() %}
|
||||||
|
<th>{{ subject }}</th>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% for action in instance_table['actions'] %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ action }}</td>
|
||||||
|
{% for subject in instance_table['table'].keys() %}
|
||||||
|
<td>
|
||||||
|
<input type="hidden"
|
||||||
|
name="is-{{ subject }}-{{ action }}" value="0" />
|
||||||
|
<input type="checkbox"
|
||||||
|
name="is-{{ subject }}-{{ action }}" value="1"
|
||||||
|
id="input-checkbox-{{ subject }}-{{ action }}"
|
||||||
|
{% if instance_table['table'][subject][action] %}
|
||||||
|
checked="checked"
|
||||||
|
{% endif %}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
17
oshipka/webapp/templates/_permissions.html
Normal file
17
oshipka/webapp/templates/_permissions.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
|
||||||
|
{% set selected_users = model_permissions_table['selected_users'] %}
|
||||||
|
{% set selected_roles = model_permissions_table['selected_roles'] %}
|
||||||
|
<button type="submit">{{ _("Update") }}</button>
|
||||||
|
<br><br>
|
||||||
|
{% include "_users_roles_multiselect.html" %}
|
||||||
|
<br><br>
|
||||||
|
{% set instance_table = model_permissions_table %}
|
||||||
|
{% include "_permission_table.html" %}
|
||||||
|
|
||||||
|
<h2>Instance Permissions</h2>
|
||||||
|
|
||||||
|
{% set instance_table = column_permissions_table %}
|
||||||
|
{% include "_permission_table.html" %}
|
||||||
|
<button type="submit">{{ _("Update") }}</button>
|
||||||
|
</form>
|
20
oshipka/webapp/templates/_users_roles_multiselect.html
Normal file
20
oshipka/webapp/templates/_users_roles_multiselect.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<label>Users:
|
||||||
|
<select multiple>
|
||||||
|
{% for user in users %}
|
||||||
|
<option value="{{ user.id }}"
|
||||||
|
{% if user.id in selected_users %}selected="selected"{% endif %}
|
||||||
|
>user {{ user.id }} ({{ user.username }})
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>Roles:
|
||||||
|
<select multiple>
|
||||||
|
{% for role in roles %}
|
||||||
|
<option value="{{ role.id }}"
|
||||||
|
{% if role.id in selected_roles %}selected="selected"{% endif %}
|
||||||
|
>role {{ role.id }}_({{ role.name }})
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
@ -1,12 +0,0 @@
|
|||||||
{% 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 %}
|
|
19
oshipka/webapp/templates/permissions_admin.html
Normal file
19
oshipka/webapp/templates/permissions_admin.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Admin permissions</h1>
|
||||||
|
{{ admin_permissions }}
|
||||||
|
<p>{{ _("Who can access the admin permissions page (this one!):") }}</p>
|
||||||
|
{% set selected_users = admin_permissions_table['selected_users'] %}
|
||||||
|
{% set selected_roles = admin_permissions_table['selected_roles'] %}
|
||||||
|
{% include "_users_roles_multiselect.html" %}
|
||||||
|
{% set instance_table = admin_permissions_table %}
|
||||||
|
{% include "_permission_table.html" %}
|
||||||
|
{% for mv in model_views %}
|
||||||
|
<h2><a href="{{ url_for('model_permissions', model_name=mv) }}">{{ mv }}</a></h2>
|
||||||
|
<p>{{ _("Who can access the permissions page for") }} {{ mv }}</p>
|
||||||
|
{% set selected_users = model_tables[mv]['selected_users'] %}
|
||||||
|
{% set selected_roles = model_tables[mv]['selected_roles'] %}
|
||||||
|
{% include "_users_roles_multiselect.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
7
oshipka/webapp/templates/permissions_instance.html
Normal file
7
oshipka/webapp/templates/permissions_instance.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>{{ _("Instance permissions for ") }} {{ model_name }}: {% include model_name + "/_title.html" %} </h2>
|
||||||
|
<p>{{ _("Who has specific permissions for this instance") }}</p>
|
||||||
|
{% include "_permissions.html" %}
|
||||||
|
{% endblock %}
|
7
oshipka/webapp/templates/permissions_model.html
Normal file
7
oshipka/webapp/templates/permissions_model.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>{{ _("Default permissions for") }} {{ model_name }} </h2>
|
||||||
|
<p>{{ _("Who can access the model pages for") }} {{ model_name }}</p>
|
||||||
|
{% include "_permissions.html" %}
|
||||||
|
{% endblock %}
|
@ -1,16 +0,0 @@
|
|||||||
<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>
|
|
@ -22,27 +22,7 @@ MODEL_VIEWS = dict()
|
|||||||
|
|
||||||
|
|
||||||
def check_instance_perm(model_view, verb, instance):
|
def check_instance_perm(model_view, verb, instance):
|
||||||
model_acl = model_view.model_acl
|
return True
|
||||||
# Anonymous user -> check public ACL
|
|
||||||
if current_user.is_anonymous:
|
|
||||||
instance_acl = model_acl.query.filter_by(instance=instance,
|
|
||||||
acl_type=SHARING_TYPE_TYPES_TYPE_PUBLIC).first()
|
|
||||||
else:
|
|
||||||
# Logged in user -> find (user, instance) pair
|
|
||||||
instance_acl = model_acl.query.filter_by(user=current_user, instance=instance,
|
|
||||||
acl_type=SHARING_TYPE_TYPES_TYPE_AUTHZ).first()
|
|
||||||
if not instance_acl:
|
|
||||||
# If not (user, instance) pair -> check authN ACL
|
|
||||||
instance_acl = model_acl.query.filter_by(user=current_user, instance=instance,
|
|
||||||
acl_type=SHARING_TYPE_TYPES_TYPE_AUTHN).first()
|
|
||||||
if not instance_acl:
|
|
||||||
# finally, try public
|
|
||||||
instance_acl = model_acl.query.filter_by(instance=instance,
|
|
||||||
acl_type=SHARING_TYPE_TYPES_TYPE_PUBLIC).first()
|
|
||||||
if not instance_acl:
|
|
||||||
return False
|
|
||||||
column = verb.replace('.', '__')
|
|
||||||
return getattr(instance_acl, column)
|
|
||||||
|
|
||||||
|
|
||||||
def has_permission(model, verb, instance=None):
|
def has_permission(model, verb, instance=None):
|
||||||
@ -51,20 +31,7 @@ def has_permission(model, verb, instance=None):
|
|||||||
return False
|
return False
|
||||||
if '.' in verb:
|
if '.' in verb:
|
||||||
return check_instance_perm(model_view, verb, instance)
|
return check_instance_perm(model_view, verb, instance)
|
||||||
acl = model_view.model.model_acls.get(verb)
|
return True
|
||||||
# Anonymous user -> do we require AuthN?
|
|
||||||
if current_user.is_anonymous:
|
|
||||||
return not acl.get('authn')
|
|
||||||
# Not Anonymous user -> Check roles
|
|
||||||
roles = acl.get('authz')
|
|
||||||
# No roles required -> has permission
|
|
||||||
if not roles:
|
|
||||||
return True
|
|
||||||
# One role is enough to grant permission
|
|
||||||
for role in roles:
|
|
||||||
if current_user.has_role(role):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def default_get_args_func(view_context):
|
def default_get_args_func(view_context):
|
||||||
@ -144,7 +111,7 @@ def get_filters(serialized_args):
|
|||||||
def default_search_func(vc):
|
def default_search_func(vc):
|
||||||
q = vc.serialized_args.get('q')
|
q = vc.serialized_args.get('q')
|
||||||
if hasattr(vc.model_view.model, 'search_query'):
|
if hasattr(vc.model_view.model, 'search_query'):
|
||||||
query = vc.model_view.model.search_query("*{q}*".format(q=q))
|
query = vc.model_view.model.search_query("{q}* or *{q}* or {q}*".format(q=q))
|
||||||
filters = get_filters(vc.serialized_args)
|
filters = get_filters(vc.serialized_args)
|
||||||
filtered_query = apply_filters(query, filters)
|
filtered_query = apply_filters(query, filters)
|
||||||
per_page = request.args.get('per_page')
|
per_page = request.args.get('per_page')
|
||||||
@ -175,18 +142,6 @@ def default_create_func(vc):
|
|||||||
instance = vc.instances or vc.model_view.model()
|
instance = vc.instances or vc.model_view.model()
|
||||||
vc.instances = [instance]
|
vc.instances = [instance]
|
||||||
default_update_func(vc)
|
default_update_func(vc)
|
||||||
user = current_user if not current_user.is_anonymous else None
|
|
||||||
create_acls(vc.model_view.model_acl, instance, user)
|
|
||||||
|
|
||||||
|
|
||||||
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_authz_acl = model_acl(user=user, instance=instance, acl_type=SHARING_TYPE_TYPES_TYPE_AUTHZ)
|
|
||||||
db.session.add(instance_authz_acl)
|
|
||||||
|
|
||||||
|
|
||||||
def default_delete_func(vc):
|
def default_delete_func(vc):
|
||||||
@ -308,10 +263,10 @@ def create_view(model_view, view_context_kwargs, is_login_required=False, the_ro
|
|||||||
|
|
||||||
|
|
||||||
class ModelView(object):
|
class ModelView(object):
|
||||||
def __init__(self, app, model, model_acl=None):
|
def __init__(self, app, model, definitions=None):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.model = model
|
self.model = model
|
||||||
self.model_acl = model_acl
|
self.definitions = definitions or {}
|
||||||
|
|
||||||
p = inflect.engine()
|
p = inflect.engine()
|
||||||
if hasattr(model, "__name__"):
|
if hasattr(model, "__name__"):
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
<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>
|
|
@ -1,9 +0,0 @@
|
|||||||
{% 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 %}
|
|
@ -1,9 +0,0 @@
|
|||||||
{% 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 %}
|
|
@ -1,21 +0,0 @@
|
|||||||
from sqlalchemy_utils import ChoiceType
|
|
||||||
from oshipka.persistance import db, ModelController
|
|
||||||
from oshipka.persistance import SHARING_TYPE_TYPES, SHARING_TYPE_TYPES_TYPE_PUBLIC
|
|
||||||
|
|
||||||
class [[ name ]]Acl(db.Model, ModelController):
|
|
||||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
|
||||||
user = db.relationship('User', backref=db.backref("[[ name|camel_to_snake ]]_acls"))
|
|
||||||
|
|
||||||
instance_id = db.Column(db.Integer, db.ForeignKey('[[ name|camel_to_snake ]].id'))
|
|
||||||
instance = db.relationship('[[ name ]]', backref=db.backref("[[ name|camel_to_snake|pluralize ]]"))
|
|
||||||
|
|
||||||
acl_type = db.Column(ChoiceType(SHARING_TYPE_TYPES), default=SHARING_TYPE_TYPES_TYPE_PUBLIC)
|
|
||||||
|
|
||||||
_read = db.Column(db.Boolean, default=True)
|
|
||||||
_update = db.Column(db.Boolean, default=True)
|
|
||||||
_delete = db.Column(db.Boolean, default=True)
|
|
||||||
|
|
||||||
[% for column in columns %]
|
|
||||||
[[ column.name ]]__read = db.Column(db.Boolean, default=True)
|
|
||||||
[[ column.name ]]__write = db.Column(db.Boolean, default=True)
|
|
||||||
[%- endfor %]
|
|
@ -10,17 +10,10 @@ from flask_security import login_required
|
|||||||
from oshipka.webapp import app
|
from oshipka.webapp import app
|
||||||
from oshipka.webapp.views import ModelView
|
from oshipka.webapp.views import ModelView
|
||||||
from webapp.routes.[[ name|camel_to_snake ]]_hooks import *
|
from webapp.routes.[[ name|camel_to_snake ]]_hooks import *
|
||||||
[%- if name == "User" %]
|
|
||||||
from webapp.models import [[ name ]]
|
from webapp.models import [[ name ]]
|
||||||
|
|
||||||
|
|
||||||
[[ name|camel_to_snake ]] = ModelView(app, [[name]])
|
[[ name|camel_to_snake ]] = ModelView(app, [[name]], [[ definitions ]])
|
||||||
[%- else %]
|
|
||||||
from webapp.models import [[ name ]], [[ name ]]Acl
|
|
||||||
|
|
||||||
|
|
||||||
[[ name|camel_to_snake ]] = ModelView(app, [[name]], [[ name ]]Acl)
|
|
||||||
[%- endif %]
|
|
||||||
|
|
||||||
|
|
||||||
[% for verb, verb_values in _verbs.items() %]
|
[% for verb, verb_values in _verbs.items() %]
|
||||||
@ -32,17 +25,3 @@ from webapp.models import [[ name ]], [[ name ]]Acl
|
|||||||
the_roles_required=[[ verb_values.the_roles_required if verb_values.the_roles_required else '[]' ]],
|
the_roles_required=[[ verb_values.the_roles_required if verb_values.the_roles_required else '[]' ]],
|
||||||
)
|
)
|
||||||
[% endfor %]
|
[% 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)
|
|
@ -174,23 +174,15 @@ def process_model(view_model):
|
|||||||
with open(os.path.join(MODELS_PATH, "_{}.py".format(model_snake)), 'w+') as f:
|
with open(os.path.join(MODELS_PATH, "_{}.py".format(model_snake)), 'w+') as f:
|
||||||
f.write(model)
|
f.write(model)
|
||||||
|
|
||||||
if model_camel not in ['User']:
|
|
||||||
template_acl = env.get_template('model_acl_py')
|
|
||||||
model_acl = autopep8.fix_code(template_acl.render(**view_model), options=pep_options)
|
|
||||||
with open(os.path.join(MODELS_PATH, "_{}_acl.py".format(model_snake)), 'w+') as f:
|
|
||||||
f.write(model_acl)
|
|
||||||
|
|
||||||
public_model = os.path.join(MODELS_PATH, "{}.py".format(model_snake))
|
public_model = os.path.join(MODELS_PATH, "{}.py".format(model_snake))
|
||||||
if not os.path.exists(public_model):
|
if not os.path.exists(public_model):
|
||||||
with open(public_model, 'w+') as f:
|
with open(public_model, 'w+') as f:
|
||||||
if model_camel not in ['User']:
|
|
||||||
f.write("from webapp.models._{}_acl import {}Acl\n".format(model_snake, model_camel))
|
|
||||||
f.write("from webapp.models._{} import {}\n".format(model_snake, model_camel))
|
f.write("from webapp.models._{} import {}\n".format(model_snake, model_camel))
|
||||||
|
|
||||||
|
|
||||||
def process_routes(view_model):
|
def process_routes(view_model):
|
||||||
template = env.get_template('routes_py')
|
template = env.get_template('routes_py')
|
||||||
model = autopep8.fix_code(template.render(**view_model), options=pep_options)
|
model = autopep8.fix_code(template.render(definitions=view_model, **view_model), options=pep_options)
|
||||||
_model_name = view_model.get('name')
|
_model_name = view_model.get('name')
|
||||||
model_name_snake = camel_case_to_snake_case(_model_name.split('.yaml')[0])
|
model_name_snake = camel_case_to_snake_case(_model_name.split('.yaml')[0])
|
||||||
filename = "{}.py".format(model_name_snake)
|
filename = "{}.py".format(model_name_snake)
|
||||||
@ -233,11 +225,7 @@ def main():
|
|||||||
process_routes(view_model)
|
process_routes(view_model)
|
||||||
view_model_name = view_model.get('name', '')
|
view_model_name = view_model.get('name', '')
|
||||||
model_snake_name = camel_case_to_snake_case(view_model_name)
|
model_snake_name = camel_case_to_snake_case(view_model_name)
|
||||||
if view_model_name not in ['User']:
|
all_model_imports.append('from webapp.models.{s} import {c}'.format(
|
||||||
all_model_imports.append('from webapp.models.{s} import {c}, {c}Acl'.format(
|
|
||||||
s=model_snake_name, c=view_model_name))
|
|
||||||
else:
|
|
||||||
all_model_imports.append('from webapp.models.{s} import {c}'.format(
|
|
||||||
s=model_snake_name, c=view_model_name))
|
s=model_snake_name, c=view_model_name))
|
||||||
process_html_templates(view_model)
|
process_html_templates(view_model)
|
||||||
all_route_imports.append('from webapp.routes.{} import *'.format(
|
all_route_imports.append('from webapp.routes.{} import *'.format(
|
||||||
|
Loading…
Reference in New Issue
Block a user