permissions improvement

This commit is contained in:
Daniel Tsvetkov 2021-05-08 15:52:52 +02:00
parent 699c8910a2
commit 9d85c95584
18 changed files with 73 additions and 22 deletions

View File

@ -184,7 +184,9 @@ user_datastore = SQLAlchemyUserDatastore(db, User, Role)
def register_filters(app): def register_filters(app):
# register jinja filters
_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}') _paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
from oshipka.webapp.views import MODEL_VIEWS
@app.template_filter('nl2br') @app.template_filter('nl2br')
def nl2br(text): def nl2br(text):
@ -234,6 +236,26 @@ def register_filters(app):
def bool_filter(v): def bool_filter(v):
return bool(v) return bool(v)
def has_permission(model, verb, instance=None):
acl = MODEL_VIEWS.get(model, {}).model.acls.get(verb)
# 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 role in ['owner']:
return instance.user == current_user
if current_user.has_role(role):
return True
return False
app.jinja_env.globals.update(has_permission=has_permission)
class Proxy(object): class Proxy(object):
def __init__(self, proxied): def __init__(self, proxied):

View File

@ -6,7 +6,8 @@ class [[ name ]](db.Model, ModelController[% for inherit in interits %], [[ inhe
[%- include "_model_choice_header_py" %] [%- include "_model_choice_header_py" %]
[%- include "_model_searchable_header_py" %] [%- include "_model_searchable_header_py" %]
_file_columns = [ [%- for column in columns %][%- if column.is_file %]"[[ column.name ]]"[%- endif %] [%- endfor %] ] _file_columns = [ [%- for column in columns %][%- if column.is_file %]"[[ column.name ]]", [%- endif %] [%- endfor %] ]
acls = [[ acls ]]
[%- for column in columns %] [%- for column in columns %]
[%- if column._type == 'relationship' %] [%- if column._type == 'relationship' %]

View File

@ -0,0 +1,3 @@
{% if has_permission('[[ name|camel_to_snake ]]', 'create', instance) %}
<a href="{{ url_for('create_[[ name|camel_to_snake ]]') }}">{{ _("Create") }}</a>
{% endif %}

View File

@ -1 +1,3 @@
{% if has_permission('[[ name|camel_to_snake ]]', 'delete', instance) %}
<a href="{{ url_for('delete_[[ name|camel_to_snake ]]', uuid=instance.id, _next=request.path) }}">x</a> <a href="{{ url_for('delete_[[ name|camel_to_snake ]]', uuid=instance.id, _next=request.path) }}">x</a>
{% endif %}

View File

@ -1 +1,3 @@
{% if has_permission('[[ name|camel_to_snake ]]', 'update', instance) %}
<a href="{{ url_for('update_[[ name|camel_to_snake ]]', uuid=instance.id, _next=request.path) }}">e</a> <a href="{{ url_for('update_[[ name|camel_to_snake ]]', uuid=instance.id, _next=request.path) }}">e</a>
{% endif %}

View File

@ -0,0 +1,3 @@
{% if has_permission('[[ name|camel_to_snake ]]', 'list') %}
<a href="{{ url_for('list_[[ name|camel_to_snake ]]') }}">{{ _("list") }}</a>
{% endif %}

View File

@ -0,0 +1,6 @@
{% if has_permission('[[ name|camel_to_snake ]]', 'search') %}
<form action="{{ url_for('search_[[ name|camel_to_snake ]]') }}">
<input type="text" name="q" value="{{ request.args.q }}" placeholder="search [[ name|pluralize ]]">
<input type="submit">
</form>
{% endif %}

View File

@ -0,0 +1,3 @@
{% if has_permission('[[ name|camel_to_snake ]]', 'table') %}
<a href="{{ url_for('table_[[ name|camel_to_snake ]]') }}">{{ _("table") }}</a>
{% endif %}

View File

@ -1,4 +1,7 @@
{% if not has_permission('[[ name|camel_to_snake ]]', 'update', instance) and not has_permission('[[ name|camel_to_snake ]]', 'delete', instance) %}
{% else %}
[ [
{% include "[[ name|camel_to_snake ]]/_action_edit.html" %} | {% include "[[ name|camel_to_snake ]]/_action_edit.html" %} |
{% include "[[ name|camel_to_snake ]]/_action_delete.html" %} {% include "[[ name|camel_to_snake ]]/_action_delete.html" %}
] ]
{% endif %}

View File

@ -0,0 +1,2 @@
{% include "[[ name|camel_to_snake ]]/_action_list.html" %} |
{% include "[[ name|camel_to_snake ]]/_action_table.html" %}

View File

@ -1,5 +1,10 @@
<li> <li>
{% if has_permission('[[ name|camel_to_snake ]]', 'get') %}
<a href="{{ url_for('get_[[ name|camel_to_snake ]]', uuid=instance.id) }}"> <a href="{{ url_for('get_[[ name|camel_to_snake ]]', uuid=instance.id) }}">
{% include "[[ name|camel_to_snake ]]/_title.html" %}</a> {% endif %}
{% include "[[ name|camel_to_snake ]]/_title.html" %}
{% if has_permission('[[ name|camel_to_snake ]]', 'get') %}
</a>
{% endif %}
{% include "[[ name|camel_to_snake ]]/_actions.html" %} {% include "[[ name|camel_to_snake ]]/_actions.html" %}
</li> </li>

View File

@ -3,9 +3,6 @@
<a href="{{ url_for('get_[[ name|camel_to_snake ]]', uuid=instance.id) }}"> <a href="{{ url_for('get_[[ name|camel_to_snake ]]', uuid=instance.id) }}">
{% include "[[ name|camel_to_snake ]]/_title.html" %}</a> {% include "[[ name|camel_to_snake ]]/_title.html" %}</a>
| |
[ {% include "[[ name|camel_to_snake ]]/_actions.html" %}
<a href="{{ url_for('update_[[ name|camel_to_snake ]]', uuid=instance.id) }}">e</a> |
<a href="{{ url_for('delete_[[ name|camel_to_snake ]]', uuid=instance.id) }}">x</a>
]
</li> </li>
{% endfor %} {% endfor %}

View File

@ -28,8 +28,7 @@
{% endif %} {% endif %}
[%- endfor %] [%- endfor %]
<td> <td>
<a href="{{ url_for('update_[[ name|camel_to_snake ]]', uuid=instance.id, _next=request.path) }}">e</a> | {% include "[[ name|camel_to_snake ]]/_action.html" %} |
<a href="{{ url_for('delete_[[ name|camel_to_snake ]]', uuid=instance.id, _next=request.path) }}">x</a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -1,9 +1,8 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block content %} {% block content %}
<a href="{{ url_for('list_[[ name|camel_to_snake ]]', uuid=instance.id) }}">{{ _("list") }}</a> | {% include "[[ name|camel_to_snake ]]/_actions_multiple.html" %}
<h2>{% include "[[ name|camel_to_snake ]]/_title.html" %}</h2> <h2>{% include "[[ name|camel_to_snake ]]/_title.html" %}</h2>
<a href="{{ url_for('update_[[ name|camel_to_snake ]]', uuid=instance.id, _next=request.path) }}">{{ _("edit") }}</a> | {% include "[[ name|camel_to_snake ]]/_actions.html" %}
<a href="{{ url_for('delete_[[ name|camel_to_snake ]]', uuid=instance.id) }}">{{ _("delete") }}</a>
{% include "[[ name|camel_to_snake ]]/_get.html" %} {% include "[[ name|camel_to_snake ]]/_get.html" %}
{% endblock %} {% endblock %}

View File

@ -2,7 +2,8 @@
{% block content %} {% block content %}
<h2>{{ _("[[ name|pluralize ]]") }}</h2> <h2>{{ _("[[ name|pluralize ]]") }}</h2>
<a href="{{ url_for('create_[[ name|camel_to_snake ]]') }}">{{ _("Create") }}</a> {% include "[[ name|camel_to_snake ]]/_action_search.html" %}
{% include "[[ name|camel_to_snake ]]/_action_create.html" %}
<br> <br>
{% include "[[ name|camel_to_snake ]]/_list.html" %} {% include "[[ name|camel_to_snake ]]/_list.html" %}
{% endblock %} {% endblock %}

View File

@ -1,7 +1,7 @@
<a href="{{ url_for('home') }}">{{ _("Home") }}</a> | <a href="{{ url_for('home') }}">{{ _("Home") }}</a> |
[%- for nav in navigation_items %] [%- for nav in navigation_items %]
[%- if nav._verbs.list.is_login_required %] [%- if nav._verbs.list.is_login_required %]
{% if current_user.is_authenticated %} {% if has_permission('[[ nav.name|camel_to_snake ]]', 'list') %}
[%- endif %] [%- endif %]
<a href="{{ url_for('list_[[ nav.name|camel_to_snake ]]') }}">{{ _("[[ nav.name ]]") }}</a>[%- if not loop.last %]|[%- endif %] <a href="{{ url_for('list_[[ nav.name|camel_to_snake ]]') }}">{{ _("[[ nav.name ]]") }}</a>[%- if not loop.last %]|[%- endif %]
[%- if nav._verbs.list.is_login_required %] [%- if nav._verbs.list.is_login_required %]

View File

@ -2,7 +2,7 @@
{% block content %} {% block content %}
<h2>{{ _("[[ name|pluralize ]]") }}</h2> <h2>{{ _("[[ name|pluralize ]]") }}</h2>
<a href="{{ url_for('create_[[ name|camel_to_snake ]]') }}">{{ _("Create") }}</a> {% include "[[ name|camel_to_snake ]]/_action_create.html" %}
<br> <br>
{% include "[[ name|camel_to_snake ]]/_table.html" %} {% include "[[ name|camel_to_snake ]]/_table.html" %}
{% endblock %} {% endblock %}

View File

@ -135,13 +135,13 @@ def enrich_view_model(view_model):
if verb == 'all': if verb == 'all':
for verb in VERBS: for verb in VERBS:
view_model['_verbs'][verb]['is_login_required'] = acl.get('login_required') view_model['_verbs'][verb]['is_login_required'] = acl.get('login_required')
view_model['_verbs'][verb]['the_roles_required'] = acl.get('roles_required') view_model['_verbs'][verb]['the_roles_required'] = acl.get('roles_required', [])
else: else:
is_login_required = acl.get('login_required') is_login_required = acl.get('login_required')
if is_login_required is False: # overrides all if is_login_required is False: # overrides all
view_model['_verbs'][verb]['the_roles_required'] = acl.get('login_required') 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]['is_login_required'] = is_login_required
view_model['_verbs'][verb]['the_roles_required'] = acl.get('roles_required') view_model['_verbs'][verb]['the_roles_required'] = acl.get('roles_required', [])
return view_model return view_model
@ -159,6 +159,9 @@ def process_navigation(view_models):
def process_model(view_model): def process_model(view_model):
template = env.get_template('model_py') template = env.get_template('model_py')
view_model['acls'] = {}
for verb, acl in view_model['_verbs'].items():
view_model['acls'][verb] = {'authn': acl['is_login_required'], 'authz': acl['the_roles_required']}
model = autopep8.fix_code(template.render(**view_model), options=pep_options) model = autopep8.fix_code(template.render(**view_model), options=pep_options)
_model_name = view_model.get('name') _model_name = view_model.get('name')