Fixes #12061: Improve handling of insufficient permissions for widget content

This commit is contained in:
jeremystretch 2023-03-30 10:03:41 -04:00
parent 06dec6a2d9
commit 424b336536
3 changed files with 32 additions and 10 deletions

View File

@ -11,6 +11,7 @@ from django.urls import NoReverseMatch, reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from utilities.forms import BootstrapMixin from utilities.forms import BootstrapMixin
from utilities.permissions import get_permission_for_model
from utilities.templatetags.builtins.filters import render_markdown from utilities.templatetags.builtins.filters import render_markdown
from utilities.utils import content_type_identifier, content_type_name, get_viewname from utilities.utils import content_type_identifier, content_type_name, get_viewname
from .utils import register_widget from .utils import register_widget
@ -107,8 +108,12 @@ class ObjectCountsWidget(DashboardWidget):
for content_type_id in self.config['models']: for content_type_id in self.config['models']:
app_label, model_name = content_type_id.split('.') app_label, model_name = content_type_id.split('.')
model = ContentType.objects.get_by_natural_key(app_label, model_name).model_class() model = ContentType.objects.get_by_natural_key(app_label, model_name).model_class()
object_count = model.objects.restrict(request.user, 'view').count permission = get_permission_for_model(model, 'view')
counts.append((model, object_count)) if request.user.has_perm(permission):
object_count = model.objects.restrict(request.user, 'view').count
counts.append((model, object_count))
else:
counts.append((model, None))
return render_to_string(self.template_name, { return render_to_string(self.template_name, {
'counts': counts, 'counts': counts,
@ -136,15 +141,21 @@ class ObjectListWidget(DashboardWidget):
def render(self, request): def render(self, request):
app_label, model_name = self.config['model'].split('.') app_label, model_name = self.config['model'].split('.')
content_type = ContentType.objects.get_by_natural_key(app_label, model_name) model = ContentType.objects.get_by_natural_key(app_label, model_name).model_class()
viewname = get_viewname(content_type.model_class(), action='list') viewname = get_viewname(model, action='list')
# Evaluate user's permission. Note that this controls only whether the HTMX element is
# embedded on the page: The view itself will also evaluate permissions separately.
permission = get_permission_for_model(model, 'view')
has_permission = request.user.has_perm(permission)
try: try:
htmx_url = reverse(viewname) htmx_url = reverse(viewname)
except NoReverseMatch: except NoReverseMatch:
htmx_url = None htmx_url = None
return render_to_string(self.template_name, { return render_to_string(self.template_name, {
'viewname': viewname, 'viewname': viewname,
'has_permission': has_permission,
'htmx_url': htmx_url, 'htmx_url': htmx_url,
'page_size': self.config.get('page_size'), 'page_size': self.config.get('page_size'),
}) })

View File

@ -3,12 +3,19 @@
{% if counts %} {% if counts %}
<div class="list-group list-group-flush"> <div class="list-group list-group-flush">
{% for model, count in counts %} {% for model, count in counts %}
<a href="{% url model|viewname:"list" %}" class="list-group-item list-group-item-action"> {% if count != None %}
<div class="d-flex w-100 justify-content-between align-items-center"> <a href="{% url model|viewname:"list" %}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between align-items-center">
{{ model|meta:"verbose_name_plural"|bettertitle }}
<h6 class="mb-1">{{ count }}</h6>
</div>
</a>
{% else %}
<div class="list-group-item list-group-item-action d-flex w-100 justify-content-between align-items-center">
{{ model|meta:"verbose_name_plural"|bettertitle }} {{ model|meta:"verbose_name_plural"|bettertitle }}
<h6 class="mb-1">{{ count }}</h6> <h6 class="mb-1" title="No permission"><i class="mdi mdi-lock-outline"></i></h6>
</div> </div>
</a> {% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}

View File

@ -1,5 +1,9 @@
{% if htmx_url %} {% if htmx_url and has_permission %}
<div class="htmx-container" hx-get="{{ htmx_url }}{% if page_size %}?per_page={{ page_size }}{% endif %}" hx-trigger="load"></div> <div class="htmx-container" hx-get="{{ htmx_url }}{% if page_size %}?per_page={{ page_size }}{% endif %}" hx-trigger="load"></div>
{% elif htmx_url %}
<div class="text-muted text-center">
<i class="mdi mdi-lock-outline"></i> No permission to view this content.
</div>
{% else %} {% else %}
<div class="text-danger text-center"> <div class="text-danger text-center">
<i class="mdi mdi-alert"></i> Unable to load content. Invalid view name: <span class="font-monospace">{{ viewname }}</span> <i class="mdi mdi-alert"></i> Unable to load content. Invalid view name: <span class="font-monospace">{{ viewname }}</span>