fix a few major permission holes
This commit is contained in:
@@ -608,6 +608,10 @@ class QuestionClone(AnatomyQuestionCreateBase):
|
||||
def get_initial(self):
|
||||
# print(self.request)
|
||||
old_object = get_object_or_404(AnatomyQuestion, pk=self.kwargs["pk"])
|
||||
|
||||
if self.request.user not in old_object.get_author_objects() and not self.request.user.is_superuser:
|
||||
raise PermissionDenied() # or Http404
|
||||
|
||||
initial_data = model_to_dict(old_object, exclude=["id"])
|
||||
|
||||
# We don't want to clone the exam details
|
||||
|
||||
+5
-1
@@ -37,9 +37,13 @@ def user_is_author_or_atlas_series_checker(function):
|
||||
return wrap
|
||||
|
||||
|
||||
def user_is_author_or_atlas_editor_or_atlas_marker(function):
|
||||
def user_has_case_view_access(function):
|
||||
def wrap(request, *args, **kwargs):
|
||||
atlas = Case.objects.get(pk=kwargs["pk"])
|
||||
|
||||
# If open access everyone can view
|
||||
if atlas.open_access:
|
||||
return function(request, *args, **kwargs)
|
||||
if (
|
||||
request.user in atlas.author.all()
|
||||
or request.user.groups.filter(name="atlas_editor").exists()
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ from .models import (
|
||||
Subspecialty,
|
||||
)
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
def get_authors(request):
|
||||
@@ -118,7 +119,7 @@ class CaseFilter(django_filters.FilterSet):
|
||||
):
|
||||
if not request.user.groups.filter(name="atlas_editor").exists():
|
||||
queryset = queryset.prefetch_related("author").filter(
|
||||
author__id=request.user.id
|
||||
(Q(author__id=request.user.id) | Q(open_access=True))
|
||||
)
|
||||
super(CaseFilter, self).__init__(
|
||||
data=data, queryset=queryset, prefix=prefix, request=request
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{% extends 'atlas/base.html' %}
|
||||
|
||||
{% load render_table from django_tables2 %}
|
||||
{% block css %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="view-filter-options">
|
||||
<h3>Filter Long </h3>
|
||||
<form action="" method="get">
|
||||
{{ filter.form }}
|
||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||
</form>
|
||||
</div>
|
||||
{% render_table table %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{% endblock %}
|
||||
@@ -15,7 +15,7 @@
|
||||
{% block content %}
|
||||
<h2>Add/Edit Condition</h2>
|
||||
<p>Use this form to create or edit a condition.</p>
|
||||
<p>Please check if it already exists before doing so!</p>
|
||||
<p>Please check if it already <a href='{% url "atlas:condition_view" %}'>exists</a> before doing so!</p>
|
||||
<form action="" method="post" enctype="multipart/form-data" id="condition-form">
|
||||
{% csrf_token %}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<h2>Atlas</h2>
|
||||
|
||||
<a href="{% url 'atlas:user_collections' %}">Available collections</a><br/>
|
||||
View my <a href='{% url "atlas:case_view" %}?author={{request.user.id}}'>cases</a>.<br/>
|
||||
<a href="{% url 'atlas:user_uploads' %}">My uploads</a>
|
||||
|
||||
{% endblock %}
|
||||
@@ -18,6 +18,7 @@
|
||||
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
|
||||
</form>
|
||||
</div>
|
||||
View my <a href='{% url "atlas:case_view" %}?author={{request.user.id}}'>cases</a>.
|
||||
</details>
|
||||
{% render_table table %}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
import json
|
||||
import pytest
|
||||
|
||||
# from django.contrib.auth.models import User
|
||||
from django.urls import reverse
|
||||
|
||||
from rich.pretty import pprint
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
|
||||
|
||||
+30
-10
@@ -99,12 +99,11 @@ from autocomplete import HTMXAutoComplete
|
||||
|
||||
from .decorators import (
|
||||
user_is_author_or_atlas_editor,
|
||||
user_is_author_or_atlas_editor_or_atlas_marker,
|
||||
user_has_case_view_access,
|
||||
user_is_author_or_atlas_series_checker_or_atlas_marker,
|
||||
user_is_atlas_editor,
|
||||
user_is_author_or_atlas_series_checker,
|
||||
user_is_atlas_marker,
|
||||
user_is_author_or_atlas_editor_or_atlas_marker,
|
||||
user_is_collection_author_or_atlas_editor,
|
||||
)
|
||||
|
||||
@@ -148,9 +147,18 @@ class AuthorOrCheckerRequiredMixin(object):
|
||||
raise PermissionDenied() # or Http404
|
||||
return obj
|
||||
|
||||
class AtlasEditorRequiredMixin(object):
|
||||
def get_object(self, *args, **kwargs):
|
||||
obj = super().get_object(*args, **kwargs)
|
||||
if (
|
||||
self.request.user.groups.filter(name="atlas_editor").exists()
|
||||
or self.request.user.is_superuser
|
||||
):
|
||||
return obj
|
||||
raise PermissionDenied("You must be an atlas editor to do this.") # or Http404
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_editor_or_atlas_marker
|
||||
@user_has_case_view_access
|
||||
def case_detail(request, pk):
|
||||
case = get_object_or_404(Case, pk=pk)
|
||||
|
||||
@@ -417,31 +425,31 @@ class SeriesFindingDelete(RevisionMixin, PermissionRequiredMixin, DeleteView):
|
||||
return reverse("atlas:series_detail", kwargs={"pk": series_id})
|
||||
|
||||
|
||||
class SeriesDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||
class SeriesDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView):
|
||||
model = Series
|
||||
template_name = "confirm_delete.html"
|
||||
success_url = reverse_lazy("atlas:series_view")
|
||||
|
||||
|
||||
class CaseCollectionDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||
class CaseCollectionDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView):
|
||||
model = CaseCollection
|
||||
template_name = "confirm_delete.html"
|
||||
success_url = reverse_lazy("atlas:collection_view")
|
||||
|
||||
|
||||
class ConditionDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||
class ConditionDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView):
|
||||
model = Condition
|
||||
template_name = "confirm_delete.html"
|
||||
success_url = reverse_lazy("atlas:condition_view")
|
||||
|
||||
|
||||
class FindingDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||
class FindingDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView):
|
||||
model = Finding
|
||||
template_name = "confirm_delete.html"
|
||||
success_url = reverse_lazy("atlas:finding_view")
|
||||
|
||||
|
||||
class StructureDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||
class StructureDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView):
|
||||
model = Structure
|
||||
template_name = "confirm_delete.html"
|
||||
success_url = reverse_lazy("atlas:structure_view")
|
||||
@@ -867,7 +875,7 @@ def add_collection_to_case_form(request, case_id):
|
||||
# return reverse("atlas:case_detail", kwargs={"pk": pk})
|
||||
|
||||
|
||||
class AtlasClone(AtlasCreateBase, CreateView):
|
||||
class AtlasClone(AtlasCreateBase, AuthorOrCheckerRequiredMixin, CreateView):
|
||||
"""Clones a existing atlas"""
|
||||
|
||||
# fields = '__all__'
|
||||
@@ -877,11 +885,23 @@ class AtlasClone(AtlasCreateBase, CreateView):
|
||||
def get_initial(self):
|
||||
# print(self.request)
|
||||
old_object = get_object_or_404(Case, pk=self.kwargs["pk"])
|
||||
initial_data = model_to_dict(old_object, exclude=["id"])
|
||||
|
||||
if (
|
||||
self.request.user.groups.filter(name="atlas_editor").exists()
|
||||
or self.request.user.is_superuser
|
||||
):
|
||||
pass
|
||||
else:
|
||||
if self.request.user not in old_object.get_author_objects():
|
||||
raise PermissionDenied() # or Http404
|
||||
else:
|
||||
pass
|
||||
|
||||
initial_data = model_to_dict(old_object, exclude=["id"])
|
||||
return initial_data
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_editor
|
||||
def atlas_scrap(request, pk):
|
||||
|
||||
@@ -2327,6 +2327,10 @@ class GenericViewBase:
|
||||
class ExamCloneMixin:
|
||||
def get_initial(self):
|
||||
old_object = get_object_or_404(self.model, pk=self.kwargs["exam_id"])
|
||||
|
||||
if self.request.user not in old_object.get_author_objects() and not self.request.user.is_superuser:
|
||||
raise PermissionDenied() # or Http404
|
||||
|
||||
initial_data = model_to_dict(old_object, exclude=["id"])
|
||||
|
||||
# We manually transfer the forign keys / m2m relationships
|
||||
|
||||
@@ -271,6 +271,9 @@ def long_clone(request, pk):
|
||||
new_item.pk = None # autogen a new pk (item_id)
|
||||
# new_item.name = "Copy of " + new_item.name #need to change uniques
|
||||
|
||||
if request.user not in new_item.get_author_objects() and not request.user.is_superuser:
|
||||
raise PermissionDenied() # or Http404
|
||||
|
||||
form = LongForm(request.POST or None, instance=new_item)
|
||||
|
||||
series_formset = SeriesFormSet()
|
||||
|
||||
+2
-1
@@ -519,7 +519,8 @@ def page_not_found(request, exception):
|
||||
|
||||
|
||||
def page_forbidden(request, exception):
|
||||
response = render(request, "403.html")
|
||||
|
||||
response = render(request, "403.html", context={"exception": exception})
|
||||
|
||||
response.status_code = 403
|
||||
|
||||
|
||||
@@ -212,8 +212,12 @@ def rapid_clone(request, pk):
|
||||
new_item.pk = None # autogen a new pk (item_id)
|
||||
# new_item.name = "Copy of " + new_item.name #need to change uniques
|
||||
|
||||
if request.user not in new_item.get_author_objects() and not request.user.is_superuser:
|
||||
raise PermissionDenied() # or Http404
|
||||
|
||||
form = RapidForm(request.POST or None, instance=new_item)
|
||||
|
||||
|
||||
image_formset = ImageFormSet()
|
||||
answer_formset = AnswerFormSet()
|
||||
|
||||
|
||||
+5
-1
@@ -4,7 +4,11 @@
|
||||
{% load static %}
|
||||
<div class="">
|
||||
<h2>403 error</h2>
|
||||
{% if exception and not exception.strip %}
|
||||
<p>{{ exception }}</p>
|
||||
{% else %}
|
||||
<p>Forbidden</p>
|
||||
{% endif %}
|
||||
<img src="{% static 'img/x-ray-radiation-no-unauthorised.png' %}" width="500px">
|
||||
Forbidden
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user