WIP: public: add error403 view and make publish_page use it (#8122) #234
|
@ -30,8 +30,8 @@ from django.http import (
|
|||
from django.urls import reverse
|
||||
from django.utils.encoding import force_str
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic import View
|
||||
from rest_framework import permissions
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.generics import GenericAPIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
@ -55,74 +55,68 @@ def dashboard_success(request, dashboard, cell_data):
|
|||
return HttpResponseRedirect(dashboard_url)
|
||||
|
||||
|
||||
class DashboardAddTileView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
raise PermissionDenied()
|
||||
@api_view(['GET'])
|
||||
def dashboard_add_tile(request, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
raise PermissionDenied()
|
||||
|
||||
dashboard = DashboardCell.objects.filter(page__snapshot__isnull=True).first()
|
||||
if dashboard is None:
|
||||
raise Http404()
|
||||
cell = CellBase.get_cell(kwargs['cell_reference'])
|
||||
if not cell.page.is_visible(request.user):
|
||||
raise PermissionDenied()
|
||||
if not cell.is_visible(request):
|
||||
raise PermissionDenied()
|
||||
cell.pk = None
|
||||
cell.page = dashboard.page
|
||||
cell.placeholder = '_dashboard'
|
||||
cell.save()
|
||||
dashboard = DashboardCell.objects.filter(page__snapshot__isnull=True).first()
|
||||
if dashboard is None:
|
||||
raise Http404()
|
||||
cell = CellBase.get_cell(kwargs['cell_reference'])
|
||||
if not cell.page.is_visible(request.user):
|
||||
raise PermissionDenied()
|
||||
if not cell.is_visible(request):
|
||||
raise PermissionDenied()
|
||||
cell.pk = None
|
||||
cell.page = dashboard.page
|
||||
cell.placeholder = '_dashboard'
|
||||
cell.save()
|
||||
|
||||
tile = Tile(dashboard=dashboard, cell=cell, user=request.user, order=0)
|
||||
if settings.COMBO_DASHBOARD_NEW_TILE_POSITION == 'first':
|
||||
order = (
|
||||
Tile.objects.filter(dashboard=dashboard, user=request.user)
|
||||
.aggregate(Min('order'))
|
||||
.get('order__min')
|
||||
)
|
||||
tile.order = order - 1 if order is not None else 0
|
||||
elif settings.COMBO_DASHBOARD_NEW_TILE_POSITION == 'last':
|
||||
order = (
|
||||
Tile.objects.filter(dashboard=dashboard, user=request.user)
|
||||
.aggregate(Max('order'))
|
||||
.get('order__max')
|
||||
)
|
||||
tile.order = order + 1 if order is not None else 0
|
||||
tile.save()
|
||||
|
||||
cell_data = get_cell_data(cell)
|
||||
cell_data['remove_url'] = reverse(
|
||||
'combo-dashboard-remove-tile', kwargs={'cell_reference': cell.get_reference()}
|
||||
tile = Tile(dashboard=dashboard, cell=cell, user=request.user, order=0)
|
||||
if settings.COMBO_DASHBOARD_NEW_TILE_POSITION == 'first':
|
||||
order = (
|
||||
Tile.objects.filter(dashboard=dashboard, user=request.user)
|
||||
.aggregate(Min('order'))
|
||||
.get('order__min')
|
||||
)
|
||||
|
||||
return dashboard_success(request, dashboard, cell_data)
|
||||
|
||||
|
||||
dashboard_add_tile = DashboardAddTileView.as_view()
|
||||
|
||||
|
||||
class DashboardRemoveTileView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
cell = CellBase.get_cell(kwargs['cell_reference'])
|
||||
try:
|
||||
tile = Tile.get_by_cell(cell)
|
||||
except Tile.DoesNotExist:
|
||||
raise Http404()
|
||||
if tile.user != request.user:
|
||||
raise PermissionDenied()
|
||||
dashboard = tile.dashboard
|
||||
cell_data = get_cell_data(cell)
|
||||
tile.delete()
|
||||
|
||||
# do not remove cell so it can directly be added back
|
||||
cell_data['add_url'] = reverse(
|
||||
'combo-dashboard-add-tile', kwargs={'cell_reference': cell.get_reference()}
|
||||
tile.order = order - 1 if order is not None else 0
|
||||
elif settings.COMBO_DASHBOARD_NEW_TILE_POSITION == 'last':
|
||||
order = (
|
||||
Tile.objects.filter(dashboard=dashboard, user=request.user)
|
||||
.aggregate(Max('order'))
|
||||
.get('order__max')
|
||||
)
|
||||
tile.order = order + 1 if order is not None else 0
|
||||
tile.save()
|
||||
|
||||
return dashboard_success(request, dashboard, cell_data)
|
||||
cell_data = get_cell_data(cell)
|
||||
cell_data['remove_url'] = reverse(
|
||||
'combo-dashboard-remove-tile', kwargs={'cell_reference': cell.get_reference()}
|
||||
)
|
||||
|
||||
return dashboard_success(request, dashboard, cell_data)
|
||||
|
||||
|
||||
dashboard_remove_tile = DashboardRemoveTileView.as_view()
|
||||
@api_view(['GET'])
|
||||
def dashboard_remove_tile(request, **kwargs):
|
||||
cell = CellBase.get_cell(kwargs['cell_reference'])
|
||||
try:
|
||||
tile = Tile.get_by_cell(cell)
|
||||
except Tile.DoesNotExist:
|
||||
raise Http404()
|
||||
if tile.user != request.user:
|
||||
raise PermissionDenied()
|
||||
dashboard = tile.dashboard
|
||||
cell_data = get_cell_data(cell)
|
||||
tile.delete()
|
||||
|
||||
# do not remove cell so it can directly be added back
|
||||
cell_data['add_url'] = reverse(
|
||||
'combo-dashboard-add-tile', kwargs={'cell_reference': cell.get_reference()}
|
||||
)
|
||||
|
||||
return dashboard_success(request, dashboard, cell_data)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
|
@ -162,6 +156,7 @@ def dashboard_auto_tile(request, *args, **kwargs):
|
|||
return response
|
||||
|
||||
|
||||
@api_view(['GET'])
|
||||
def dashboard_reorder_tiles(request, *args, **kwargs):
|
||||
new_order = request.GET['order'].split(',')
|
||||
tiles = {str(x.id): x for x in Tile.objects.filter(id__in=new_order)}
|
||||
|
|
|
@ -92,9 +92,9 @@ class TrackingCodeView(View):
|
|||
|
||||
try:
|
||||
url = self.search(code, request, wcs_site=cell.wcs_site)
|
||||
except PermissionDenied:
|
||||
except PermissionDenied as expt:
|
||||
if redirect_to_other_domain:
|
||||
raise
|
||||
return HttpResponseForbidden('%s' % expt)
|
||||
messages.error(self.request, _('Looking up tracking code is currently rate limited.'))
|
||||
else:
|
||||
if url:
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
{% extends "combo/page_template.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block combo-content %}
|
||||
<div>
|
||||
<h2>{% trans "You do not have sufficient rights to view this page" %}</h2>
|
||||
<p>
|
||||
{% trans "Maybe you can" %}
|
||||
<a href="{% url 'auth_logout' %}?next={{ login_url | urlencode }}">
|
||||
{% trans "authenticate yourself using another account ?" %}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -44,7 +44,7 @@ from django.utils.encoding import force_str
|
|||
from django.utils.http import urlencode
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.defaults import page_not_found
|
||||
from django.views.defaults import page_not_found, permission_denied
|
||||
|
||||
from combo import utils
|
||||
from combo.apps.assets.models import Asset
|
||||
|
@ -120,23 +120,23 @@ def ajax_page_cell(request, page_pk, cell_reference):
|
|||
raise Http404()
|
||||
# as it's from a snapshot access is limited to managers
|
||||
if not (request.user and request.user.is_superuser):
|
||||
raise PermissionDenied()
|
||||
return permission_denied(request, exception=PermissionDenied)
|
||||
|
||||
if not page.is_visible(request.user):
|
||||
raise PermissionDenied()
|
||||
return permission_denied(request, exception=PermissionDenied)
|
||||
try:
|
||||
cell = CellBase.get_cell(cell_reference, page_id=page_pk)
|
||||
except ObjectDoesNotExist:
|
||||
raise Http404()
|
||||
|
||||
if not cell.is_visible(request):
|
||||
raise PermissionDenied()
|
||||
return permission_denied(request, exception=PermissionDenied)
|
||||
|
||||
exception = None
|
||||
action_response = None
|
||||
if request.method == 'POST':
|
||||
if not hasattr(cell, 'post'):
|
||||
raise PermissionDenied()
|
||||
return permission_denied(request, exception=PermissionDenied)
|
||||
try:
|
||||
action_response = cell.post(request)
|
||||
except PostException as e:
|
||||
|
@ -198,7 +198,10 @@ def render_cell(request, cell):
|
|||
# Cell can pass data through its own __dict__
|
||||
cell.modify_global_context(context, request)
|
||||
|
||||
response_content = engines['django'].from_string('{% render_cell cell %}').render(context, request)
|
||||
try:
|
||||
response_content = engines['django'].from_string('{% render_cell cell %}').render(context, request)
|
||||
except PermissionDenied:
|
||||
return permission_denied(request, exception=PermissionDenied)
|
||||
response = HttpResponse(response_content, content_type='text/html')
|
||||
if hasattr(request, 'page_title_from_cell'):
|
||||
# cell request a change to page title, pass info in response header
|
||||
|
@ -272,7 +275,7 @@ def skeleton(request):
|
|||
# {% endblock %}
|
||||
# {% endblock %}
|
||||
if 'source' not in request.GET:
|
||||
raise PermissionDenied()
|
||||
return permission_denied(request, exception=PermissionDenied)
|
||||
source = request.GET['source']
|
||||
|
||||
if 'Accept-Language' in request.headers:
|
||||
|
@ -335,7 +338,7 @@ def skeleton(request):
|
|||
continue
|
||||
break
|
||||
else:
|
||||
raise PermissionDenied()
|
||||
return permission_denied(request, exception=PermissionDenied)
|
||||
|
||||
# add default ParentContentCells to the page
|
||||
cells = []
|
||||
|
@ -544,10 +547,6 @@ def publish_page(request, page, status=200, template_name=None):
|
|||
pages = page.get_parents_and_self()
|
||||
|
||||
if not page.is_visible(request.user):
|
||||
if not request.user.is_authenticated:
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
|
||||
return redirect_to_login(request.build_absolute_uri())
|
||||
raise PermissionDenied()
|
||||
|
||||
if page.redirect_url:
|
||||
|
@ -626,6 +625,23 @@ def publish_page(request, page, status=200, template_name=None):
|
|||
return HttpResponse(response_content, status=status)
|
||||
|
||||
|
||||
def error403(request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
|
||||
return redirect_to_login(request.build_absolute_uri())
|
||||
try:
|
||||
page = Page.objects.get(slug='403')
|
||||
template_name = None
|
||||
except Page.DoesNotExist:
|
||||
page = Page.objects.filter(slug='index', parent=None).first() or Page()
|
||||
page.redirect_url = None
|
||||
page.public = True
|
||||
page.template_name = 'standard'
|
||||
template_name = 'combo/403.html'
|
||||
return publish_page(request, page, status=403, template_name=template_name)
|
||||
|
||||
|
||||
def error404(request, *args, **kwargs):
|
||||
if not args and 'exception' not in kwargs:
|
||||
# happens when /404 is called on portal agent
|
||||
|
|
|
@ -23,17 +23,19 @@ from django.views.i18n import JavaScriptCatalog
|
|||
|
||||
from . import plugins
|
||||
from .manager.urls import urlpatterns as combo_manager_urls
|
||||
from .public.views import error404, login, logout, mellon_page_hook
|
||||
from .public.views import error403, error404, login, logout, mellon_page_hook
|
||||
from .urls_utils import decorated_includes, manager_required
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^manage/', decorated_includes(manager_required, include(combo_manager_urls))),
|
||||
re_path(r'^logout/$', logout, name='auth_logout'),
|
||||
re_path(r'^login/$', login, name='auth_login'),
|
||||
re_path(r'^403$', error403),
|
||||
re_path(r'^404$', error404),
|
||||
re_path(r'^jsi18n$', JavaScriptCatalog.as_view(), name='javascript-catalog'),
|
||||
]
|
||||
|
||||
handler403 = error403
|
||||
handler404 = error404
|
||||
|
||||
if 'mellon' in settings.INSTALLED_APPS:
|
||||
|
|
|
@ -1358,7 +1358,7 @@ def test_chartng_cell_view(app, normal_user, statistics):
|
|||
|
||||
page.public = False
|
||||
page.save()
|
||||
resp = app.get(location + '?width=400', status=403)
|
||||
resp = app.get(location + '?width=400', status=302)
|
||||
|
||||
page.public = True
|
||||
page.save()
|
||||
|
@ -1367,10 +1367,10 @@ def test_chartng_cell_view(app, normal_user, statistics):
|
|||
cell.public = False
|
||||
cell.groups.set([group])
|
||||
cell.save()
|
||||
resp = app.get(location + '?width=400', status=403)
|
||||
resp = app.get(location + '?width=400', status=302)
|
||||
|
||||
app = login(app, username='normal-user', password='normal-user')
|
||||
resp = app.get(location + '?width=400', status=403)
|
||||
resp = app.get(location + '?width=400', status=403) # once logged in we got 403
|
||||
|
||||
normal_user.groups.set([group])
|
||||
normal_user.save()
|
||||
|
|
|
@ -352,6 +352,30 @@ def test_page_private_logged_in(app, admin_user):
|
|||
app.get('/', status=200)
|
||||
|
||||
|
||||
def test_page_private_logged_in_no_perm(app, normal_user):
|
||||
group_ok = Group.objects.create(name='g1')
|
||||
Page.objects.all().delete()
|
||||
Page.objects.create(title='index', slug='index', public=True)
|
||||
|
||||
priv = Page.objects.create(title='index2', slug='priv', public=False)
|
||||
priv.groups.set([group_ok])
|
||||
normal_user.groups.set([])
|
||||
|
||||
app = login(app, username='normal-user', password='normal-user')
|
||||
|
||||
# Falling on default small 403 template proposing to switch account
|
||||
resp = app.get('/priv/', status=403)
|
||||
assert 'You do not have sufficient rights to view this page' in resp
|
||||
assert 'authenticate yourself using another account ?' in resp
|
||||
|
||||
# using a custom empty page as 403 error page
|
||||
Page.objects.create(title='Title of the custom 403 page', slug='403', public=True)
|
||||
resp = app.get('/priv/', status=403)
|
||||
assert 'You do not have sufficient rights to this page' not in resp
|
||||
assert 'authenticate yourself using another account ?' not in resp
|
||||
assert 'Title of the custom 403 page' in resp
|
||||
|
||||
|
||||
def test_page_skeleton(app):
|
||||
Page.objects.all().delete()
|
||||
page = Page(
|
||||
|
|
Loading…
Reference in New Issue