Skip to content

Commit

Permalink
Checking user permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
morlandi committed Sep 24, 2018
1 parent 59f5433 commit 26f970c
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 73 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 113 additions & 5 deletions docs/source/topics/frontend_generic_helpers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ e riscrivere il template piu' semplicemente come segue::
Deleting an object
------------------

.. figure:: /_static/images/objects_table.png

Associamo all'url::

path('object/<str:app_label>/<str:model_name>/<uuid:pk>/delete/', views.delete_object, name="object-delete"),
Expand Down Expand Up @@ -220,7 +222,7 @@ la conferma dell'utente:

.. code:: javascript
function deleteRemoteObject(url, title, afterObjectDeleteCallback) {
function confirmRemoteAction(url, title, afterObjectDeleteCallback) {
var modal = $('#modal_confirm');
modal.find('.modal-title').text(title);
modal.find('.btn-yes').off().on('click', function() {
Expand All @@ -242,7 +244,7 @@ la conferma dell'utente:
e quindi, nel template::

<a href=""
onclick="deleteRemoteObject('{{object|delete_object_url}}', 'Deleting {{object}}', afterObjectDelete); return false;">
onclick="confirmRemoteAction('{{object|delete_object_url}}', 'Deleting {{object}}', afterObjectDelete); return false;">
<i class="fa fa-eraser"></i> Delete
</a>

Expand Down Expand Up @@ -290,14 +292,120 @@ Qui stiamo supponendo che il Model metta a disposizione un opportuno metodo **cl
obj.save()
return obj
La stessa funzione javascript deleteRemoteObject() utilizzata in precedenza puo' essere
La stessa funzione javascript confirmRemoteAction() utilizzata in precedenza puo' essere
invocata anche qui per richiedere la conferma dell'utente prima dell'esecuzione::

<a href=""
onclick="deleteRemoteObject('{{object|clone_object_url}}', 'Duplicating {{object}}', afterObjectClone); return false;">
onclick="confirmRemoteAction('{{object|clone_object_url}}', 'Duplicating {{object}}', afterObjectClone); return false;">
<i class="fa fa-clone"></i> Duplicate
</a>


.. figure:: /_static/images/objects_table.png
Checking user permissions
-------------------------

Tutte le viste utilizzate sin qui per manipolare i Models sono gia' protette in
termine di permissions accordate all'utente; in caso di violazione, viene lanciata l'eccezione PermissionDenied, e il front-end
visualizza un server error.

In alternativa, possiamo inibire o nascondere i controlli di editing dalla pagina
quanto l'utente loggato non e' autorizzato alle operazioni.

Il seguente template tag consente di verificare se l'utente e' autorizzato o meno
ad eseguire le azioni:

- add
- change
- delete
- view (Django >= 2.1 only)

.. code:: python
@register.simple_tag(takes_context=True)
def testhasperm(context, model, action):
"""
Returns True iif the user have the specified permission over the model.
"""
user = context['request'].user
app_label = model._meta.app_label
model_name = model._meta.model_name
required_permission = '%s.%s_%s' % (app_label, action, model_name)
return user.is_authenticated and user.has_perm(required_permission)
e puo' essere utilizzata assegnato il valore calcolato a una variabile::

{% testhasperm model 'view' as can_view_objects %}
{% if not can_view_objects %}
<h2>Sorry, you have no permission to view these objects</h2>
{% endif %}

Un'altra possibilita' e' quella di utilizzare un template tag "ishasperm" per
condizionare l'inclusione del controllo::

{% ifhasperm model 'change' %}
<a href=""
data-action="{{model|change_model_url:object.id}}"
onclick="openModalDialogWithForm(event, '#modal_generic', null, afterObjectChangeSuccess); return false;"
data-title="Update {{ model|model_verbose_name }}: {{ object }}">
<i class="fa fa-edit"></i> Edit
</a>
|
{% endifhasperm %}

dove:

.. code:: python
@register.tag
def ifhasperm(parser, token):
"""
Check user permission over specified model.
(You can specify either a model or an object).
"""
# Separating the tag name from the parameters
try:
tag, model, action = token.contents.split()
except (ValueError, TypeError):
raise template.TemplateSyntaxError(
"'%s' tag takes three parameters" % tag)
default_states = ['ifhasperm', 'else']
end_tag = 'endifhasperm'
# Place to store the states and their values
states = {}
# Let's iterate over our context and find our tokens
while token.contents != end_tag:
current = token.contents
states[current.split()[0]] = parser.parse(default_states + [end_tag])
token = parser.next_token()
model_var = parser.compile_filter(model)
action_var = parser.compile_filter(action)
return CheckPermNode(states, model_var, action_var)
class CheckPermNode(template.Node):
def __init__(self, states, model_var, action_var):
self.states = states
self.model_var = model_var
self.action_var = action_var
def render(self, context):
# Resolving variables passed by the user
model = self.model_var.resolve(context)
action = self.action_var.resolve(context)
# Check user permission
if testhasperm(context, model, action):
html = self.states['ifhasperm'].render(context)
else:
html = self.states['else'].render(context) if 'else' in self.states else ''
return html
.. figure:: /_static/images/check_user_permissions.png

19 changes: 0 additions & 19 deletions sample_project/frontend/static/frontend/js/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,3 @@
$(document).ready(function() {
});


function deleteRemoteObject(url, title, afterObjectDeleteCallback) {
var modal = $('#modal_confirm');
modal.find('.modal-title').text(title);
modal.find('.btn-yes').off().on('click', function() {
// User selected "Yes", so proceed with remote call
$.ajax({
type: 'GET',
url: url
}).done(function(data) {
if (afterObjectDeleteCallback) {
afterObjectDeleteCallback(data);
}
}).fail(function(jqXHR, textStatus, errorThrown) {
alert('SERVER ERROR: ' + errorThrown);
});
});
modal.modal('show');
}
32 changes: 32 additions & 0 deletions sample_project/frontend/static/frontend/js/modals.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,35 @@ function openModalDialogWithForm(event, modal, cbAfterLoad, cbAfterSuccess) {
});
}


/**
* Invoke remote action upon user confirmation.
*
* Display a dialog to ask for user confirmation, then invoke remote action;
* after successfull execution, call supplied callback with server result.
*
* @param {string} url Server action to be invoked.
* @param {string} title Title for confirmation modal dialog.
* @param {afterDoneCallback} [function] Callback to be invoked after successfull execution.
*
* @return {none}
*/

function confirmRemoteAction(url, title, afterDoneCallback) {
var modal = $('#modal_confirm');
modal.find('.modal-body p').text(title);
modal.find('.btn-yes').off().on('click', function() {
// User selected "Yes", so proceed with remote call
$.ajax({
type: 'GET',
url: url
}).done(function(data) {
if (afterDoneCallback) {
afterDoneCallback(data);
}
}).fail(function(jqXHR, textStatus, errorThrown) {
alert('SERVER ERROR: ' + errorThrown);
});
});
modal.modal('show');
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
<span class="sr-only">{% trans 'Close' %}</span>
</button>
<div class="title-wrapper">
<span class="modal-title"></span>
<i class="fa fa-question-circle modal-icon"></i>
<span class="modal-title">{% trans 'Confirmation required' %}</span>
</div>
<small class="modal-subtitle font-bold"></small>
</div>
<div class="modal-body">
<h1>Are you sure ?</h1>
<p></p>
<h1>{% trans 'Are you sure ?' %}</h1>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-white" data-dismiss="modal">{% trans 'No' %}</button>
Expand Down
137 changes: 90 additions & 47 deletions sample_project/frontend/templates/frontend/objects_table.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,105 @@

{% block content %}


<div class="row">

{% block pagetitle %}
{% endblock pagetitle %}

<div class="col-sm-12">
<h1>{{ model|model_verbose_name_plural }}</h1>
{% if not objects %}
<div>No objects available yet</div>
{% else %}
<table class="table table-striped">
<thead>
<tr>
<th>Tools</th>
{% block table_headers %}
{% endblock table_headers %}
</tr>
</thead>
<tbody>
{% for object in objects %}
<tr>
<td style="white-space: nowrap;">
<a href=""
data-action="{{model|change_model_url:object.id}}"
onclick="openModalDialogWithForm(event, '#modal_generic', null, afterObjectChangeSuccess); return false;"
data-title="Update {{ model|model_verbose_name }}: {{ object }}">
<i class="fa fa-edit"></i> Edit
</a>
|
<a href=""
onclick="deleteRemoteObject('{{object|delete_object_url}}', 'Deleting {{object}}', afterObjectDelete); return false;">
<i class="fa fa-eraser"></i> Delete
</a>
|
<a href=""
onclick="deleteRemoteObject('{{object|clone_object_url}}', 'Duplicating {{object}}', afterObjectClone); return false;">
<i class="fa fa-clone"></i> Duplicate
</a>
</td>
{% block table_row %}
{% endblock table_row %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}

<div>
<button
href=""
data-action="{{model|add_model_url}}"
data-title="New {{ model|model_verbose_name }}"
onclick="openModalDialogWithForm(event, '#modal_generic', null, afterObjectAddSuccess); return false;"
type="button"class="btn btn-primary">
New
</button>
{% ifhasperm model 'view' %}
<div style="color: #090">User can view objects</div>
{% else %}
<div style="color: #900">User cannot view objects</div>
{% endifhasperm %}

{% ifhasperm model 'add' %}
<div style="color: #090">User can add objects</div>
{% else %}
<div style="color: #900">User cannot add objects</div>
{% endifhasperm %}

{% ifhasperm model 'change' %}
<div style="color: #090">User can change objects</div>
{% else %}
<div style="color: #900">User cannot change objects</div>
{% endifhasperm %}

{% ifhasperm model 'delete' %}
<div style="color: #090">User can delete objects</div>
{% else %}
<div style="color: #900">User cannot delete objects</div>
{% endifhasperm %}
</div>


{% testhasperm model 'view' as can_view_objects %}
{% if not can_view_objects %}
<h2>Sorry, you have no permission to view these objects</h2>
{% else %}
{% if not objects %}
<h2>No objects available yet</h2>
{% else %}
<table class="table table-striped">
<thead>
<tr>
<th>Tools</th>
{% block table_headers %}
{% endblock table_headers %}
</tr>
</thead>
<tbody>
{% for object in objects %}
<tr>
<td style="white-space: nowrap;">
{% ifhasperm model 'change' %}
<a href=""
data-action="{{model|change_model_url:object.id}}"
onclick="openModalDialogWithForm(event, '#modal_generic', null, afterObjectChangeSuccess); return false;"
data-title="Update {{ model|model_verbose_name }}: {{ object }}">
<i class="fa fa-edit"></i> Edit
</a>
|
{% endifhasperm %}
{% ifhasperm model 'delete' %}
<a href=""
onclick="confirmRemoteAction('{{object|delete_object_url}}', 'Deleting {{object|escapejs}}', afterObjectDelete); return false;">
<i class="fa fa-eraser"></i> Delete
</a>
|
{% endifhasperm %}
{% ifhasperm model 'add' %}
<a href=""
onclick="confirmRemoteAction('{{object|clone_object_url}}', 'Duplicating {{object|escapejs}}', afterObjectClone); return false;">
<i class="fa fa-clone"></i> Duplicate
</a>
|
{% endifhasperm %}
</td>
{% block table_row %}
{% endblock table_row %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
<div>
{% ifhasperm model 'add' %}
<button
href=""
data-action="{{model|add_model_url}}"
data-title="New {{ model|model_verbose_name }}"
onclick="openModalDialogWithForm(event, '#modal_generic', null, afterObjectAddSuccess); return false;"
type="button"class="btn btn-primary">
New
</button>
{% endifhasperm %}
</div>
{% endif %}
</div>
</div>
{% endblock content %}
Expand Down

0 comments on commit 26f970c

Please sign in to comment.