',
@@ -322,9 +323,9 @@ def add_comment(request, object_id):
@can_submit_liaison_required
def liaison_add(request, type=None, **kwargs):
if type == 'incoming' and not can_add_incoming_liaison(request.user):
- return HttpResponseForbidden("Restricted to users who are authorized to submit incoming liaison statements")
+ permission_denied(request, "Restricted to users who are authorized to submit incoming liaison statements.")
if type == 'outgoing' and not can_add_outgoing_liaison(request.user):
- return HttpResponseForbidden("Restricted to users who are authorized to submit outgoing liaison statements")
+ permission_denied(request, "Restricted to users who are authorized to submit outgoing liaison statements.")
if request.method == 'POST':
form = liaison_form_factory(request, data=request.POST.copy(),
@@ -372,7 +373,7 @@ def liaison_delete_attachment(request, object_id, attach_id):
liaison = get_object_or_404(LiaisonStatement, pk=object_id)
attach = get_object_or_404(LiaisonStatementAttachment, pk=attach_id)
if not can_edit_liaison(request.user, liaison):
- return HttpResponseForbidden("You are not authorized for this action")
+ permission_denied(request, "You are not authorized for this action.")
# FIXME: this view should use POST instead of GET when deleting
attach.removed = True
@@ -430,7 +431,7 @@ def liaison_detail(request, object_id):
def liaison_edit(request, object_id):
liaison = get_object_or_404(LiaisonStatement, pk=object_id)
if not can_edit_liaison(request.user, liaison):
- return HttpResponseForbidden('You do not have permission to edit this liaison statement')
+ permission_denied(request, 'You do not have permission to edit this liaison statement.')
return liaison_add(request, instance=liaison)
def liaison_edit_attachment(request, object_id, doc_id):
@@ -438,7 +439,7 @@ def liaison_edit_attachment(request, object_id, doc_id):
liaison = get_object_or_404(LiaisonStatement, pk=object_id)
doc = get_object_or_404(Document, pk=doc_id)
if not can_edit_liaison(request.user, liaison):
- return HttpResponseForbidden("You are not authorized for this action")
+ permission_denied(request, "You are not authorized for this action.")
if request.method == 'POST':
form = EditAttachmentForm(request.POST)
@@ -480,8 +481,8 @@ def liaison_list(request, state='posted'):
# check authorization for pending and dead tabs
if state in ('pending','dead') and not can_add_liaison(request.user):
- msg = "Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities"
- return HttpResponseForbidden(msg)
+ msg = "Restricted to participants who are authorized to submit liaison statements on behalf of the various IETF entities."
+ permission_denied(request, msg)
if 'tags' in request.GET:
value = request.GET.get('tags')
diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py
index f6146646f..a990478cb 100644
--- a/ietf/meeting/views.py
+++ b/ietf/meeting/views.py
@@ -27,7 +27,7 @@ import debug # pyflakes:ignore
from django import forms
from django.shortcuts import render, redirect, get_object_or_404
-from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, Http404
+from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
@@ -86,10 +86,11 @@ from ietf.utils.decorators import require_api_key
from ietf.utils.history import find_history_replacements_active_at
from ietf.utils.log import assertion
from ietf.utils.mail import send_mail_message, send_mail_text
+from ietf.utils.mime import get_mime_type
from ietf.utils.pipe import pipe
from ietf.utils.pdf import pdf_pages
+from ietf.utils.response import permission_denied
from ietf.utils.text import xslugify
-from ietf.utils.mime import get_mime_type
from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm,
InterimCancelForm, InterimSessionInlineFormSet, FileUploadForm, RequestMinutesForm,)
@@ -462,7 +463,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
if not can_see:
if request.method == 'POST':
- return HttpResponseForbidden("Can't view this schedule")
+ permission_denied(request, "Can't view this schedule.")
# FIXME: check this
return render(request, "meeting/private_schedule.html",
@@ -491,7 +492,7 @@ def edit_meeting_schedule(request, num=None, owner=None, name=None):
if request.method == 'POST':
if not can_edit:
- return HttpResponseForbidden("Can't edit this schedule")
+ permission_denied(request, "Can't edit this schedule.")
action = request.POST.get('action')
@@ -859,7 +860,7 @@ def edit_schedule_properties(request, num=None, owner=None, name=None):
cansee, canedit, secretariat = schedule_permissions(meeting, schedule, request.user)
if not (canedit or has_role(request.user,'Secretariat')):
- return HttpResponseForbidden("You may not edit this schedule")
+ permission_denied(request, "You may not edit this schedule.")
else:
if request.method == 'POST':
form = SchedulePropertiesForm(instance=schedule,data=request.POST)
@@ -1704,12 +1705,12 @@ def upload_session_bluesheets(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to upload bluesheets for this session.")
+ permission_denied(request, "You don't have permission to upload bluesheets for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
if session.meeting.type.slug == 'ietf' and not has_role(request.user, 'Secretariat'):
- return HttpResponseForbidden('Restricted to role Secretariat')
+ permission_denied(request, 'Restricted to role Secretariat')
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
@@ -1801,9 +1802,9 @@ def upload_session_minutes(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to upload minutes for this session.")
+ permission_denied(request, "You don't have permission to upload minutes for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
@@ -1901,9 +1902,9 @@ def upload_session_agenda(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to upload an agenda for this session.")
+ permission_denied(request, "You don't have permission to upload an agenda for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
@@ -2015,9 +2016,9 @@ def upload_session_slides(request, session_id, num, name):
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to upload slides for this session.")
+ permission_denied(request, "You don't have permission to upload slides for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
@@ -2111,7 +2112,7 @@ def upload_session_slides(request, session_id, num, name):
def propose_session_slides(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(session.meeting.number,session.group.acronym)
@@ -2175,9 +2176,9 @@ def remove_sessionpresentation(request, session_id, num, name):
sp = get_object_or_404(SessionPresentation,session_id=session_id,document__name=name)
session = sp.session
if not session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to manage materials for this session.")
+ permission_denied(request, "You don't have permission to manage materials for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
if request.method == 'POST':
session.sessionpresentation_set.filter(pk=sp.pk).delete()
c = DocEvent(type="added_comment", doc=sp.document, rev=sp.document.rev, by=request.user.person)
@@ -2191,9 +2192,9 @@ def ajax_add_slides_to_session(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to upload slides for this session.")
+ permission_denied(request, "You don't have permission to upload slides for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
if request.method != 'POST' or not request.POST:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json')
@@ -2224,9 +2225,9 @@ def ajax_remove_slides_from_session(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to upload slides for this session.")
+ permission_denied(request, "You don't have permission to upload slides for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
if request.method != 'POST' or not request.POST:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json')
@@ -2262,9 +2263,9 @@ def ajax_reorder_slides_in_session(request, session_id, num):
session = get_object_or_404(Session,pk=session_id)
if not session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to upload slides for this session.")
+ permission_denied(request, "You don't have permission to upload slides for this session.")
if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
if request.method != 'POST' or not request.POST:
return HttpResponse(json.dumps({ 'success' : False, 'error' : 'No data submitted or not POST' }),content_type='application/json')
@@ -2341,13 +2342,13 @@ def delete_schedule(request, num, owner, name):
schedule = get_schedule_by_name(meeting, person, name)
if schedule.name=='Empty-Schedule':
- return HttpResponseForbidden('You may not delete the default empty schedule')
+ permission_denied(request, 'You may not delete the default empty schedule')
if schedule == meeting.schedule:
- return HttpResponseForbidden('You may not delete the official schedule for %s'%meeting)
+ permission_denied(request, 'You may not delete the official schedule for %s'%meeting)
if not ( has_role(request.user, 'Secretariat') or person.user == request.user ):
- return HttpResponseForbidden("You may not delete other user's schedules")
+ permission_denied(request, "You may not delete other user's schedules")
if request.method == 'POST':
schedule.delete()
@@ -2466,7 +2467,7 @@ def interim_skip_announcement(request, number):
def interim_pending(request):
if not can_manage_some_groups(request.user):
- return HttpResponseForbidden()
+ permission_denied(request, "You are not authorized to access this view")
'''View which shows interim meeting requests pending approval'''
meetings = data_for_meetings_overview(Meeting.objects.filter(type='interim').order_by('date'), interim_status='apprw')
@@ -2489,7 +2490,7 @@ def interim_pending(request):
def interim_request(request):
if not can_manage_some_groups(request.user):
- return HttpResponseForbidden("You don't have permission to request any interims")
+ permission_denied(request, "You don't have permission to request any interims")
'''View for requesting an interim meeting'''
SessionFormset = inlineformset_factory(
@@ -2582,7 +2583,7 @@ def interim_request_cancel(request, number):
first_session = meeting.session_set.first()
group = first_session.group
if not can_manage_group(request.user, group):
- return HttpResponseForbidden("You do not have permissions to cancel this meeting request")
+ permission_denied(request, "You do not have permissions to cancel this meeting request")
session_status = current_session_status(first_session)
if request.method == 'POST':
@@ -2622,7 +2623,7 @@ def interim_request_details(request, number):
meeting = get_object_or_404(Meeting, number=number)
group = meeting.session_set.first().group
if not can_manage_group(request.user, group):
- return HttpResponseForbidden("You do not have permissions to manage this meeting request")
+ permission_denied(request, "You do not have permissions to manage this meeting request")
sessions = meeting.session_set.all()
can_edit = can_edit_interim_request(meeting, request.user)
can_approve = can_approve_interim_request(meeting, request.user)
@@ -2668,7 +2669,7 @@ def interim_request_edit(request, number):
'''Edit details of an interim meeting reqeust'''
meeting = get_object_or_404(Meeting, number=number)
if not can_edit_interim_request(meeting, request.user):
- return HttpResponseForbidden("You do not have permissions to edit this meeting request")
+ permission_denied(request, "You do not have permissions to edit this meeting request")
SessionFormset = inlineformset_factory(
Meeting,
@@ -3067,7 +3068,8 @@ def api_upload_bluesheet(request):
# group: acronym or special, i.e., 'quic' or 'plenary'
# item: '1', '2', '3' (the group's first, second, third etc.
# session during the week)
- # bluesheet: json blob with [{'name': 'Name', 'affiliation': 'Organization', }, ...]
+ # bluesheet: json blob with
+ # [{'name': 'Name', 'affiliation': 'Organization', }, ...]
for item in ['meeting', 'group', 'item', 'bluesheet',]:
value = request.POST.get(item)
if not value:
@@ -3214,9 +3216,9 @@ class ApproveSlidesForm(forms.Form):
def approve_proposed_slides(request, slidesubmission_id, num):
submission = get_object_or_404(SlideSubmission,pk=slidesubmission_id)
if not submission.session.can_manage_materials(request.user):
- return HttpResponseForbidden("You don't have permission to manage slides for this session.")
+ permission_denied(request, "You don't have permission to manage slides for this session.")
if submission.session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"):
- return HttpResponseForbidden("The materials cutoff for this session has passed. Contact the secretariat for further action.")
+ permission_denied(request, "The materials cutoff for this session has passed. Contact the secretariat for further action.")
session_number = None
sessions = get_sessions(submission.session.meeting.number,submission.session.group.acronym)
diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py
index 38438184e..568671d21 100644
--- a/ietf/nomcom/views.py
+++ b/ietf/nomcom/views.py
@@ -12,7 +12,7 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import AnonymousUser
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.forms.models import modelformset_factory, inlineformset_factory
-from django.http import Http404, HttpResponseRedirect, HttpResponseForbidden
+from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404, redirect
from django.template.loader import render_to_string
from django.urls import reverse
@@ -42,6 +42,7 @@ from ietf.nomcom.utils import (get_nomcom_by_year, store_nomcom_private_key,
from ietf.ietfauth.utils import role_required
from ietf.person.models import Person
from ietf.utils import log
+from ietf.utils.response import permission_denied
import debug # pyflakes:ignore
@@ -661,20 +662,20 @@ def private_questionnaire(request, year):
def process_nomination_status(request, year, nominee_position_id, state, date, hash):
valid = get_hash_nominee_position(date, nominee_position_id) == hash
if not valid:
- return HttpResponseForbidden("Bad hash!")
+ permission_denied(request, "Bad hash!")
expiration_days = getattr(settings, 'DAYS_TO_EXPIRE_NOMINATION_LINK', None)
if expiration_days:
request_date = datetime.date(int(date[:4]), int(date[4:6]), int(date[6:]))
if datetime.date.today() > (request_date + datetime.timedelta(days=settings.DAYS_TO_EXPIRE_NOMINATION_LINK)):
- return HttpResponseForbidden("Link expired")
+ permission_denied(request, "Link expired.")
need_confirmation = True
nomcom = get_nomcom_by_year(year)
if nomcom.group.state_id == 'conclude':
- return HttpResponseForbidden("This nomcom is concluded.")
+ permission_denied(request, "This nomcom is concluded.")
nominee_position = get_object_or_404(NomineePosition, id=nominee_position_id)
if nominee_position.state.slug != "pending":
- return HttpResponseForbidden("The nomination already was %s" % nominee_position.state)
+ permission_denied(request, "The nomination already was %s" % nominee_position.state)
state = get_object_or_404(NomineePositionStateName, slug=state)
messages.info(request, "Click on 'Save' to set the state of your nomination to %s to %s (this is not a final commitment - you can notify us later if you need to change this)." % (nominee_position.position.name, state.name))
@@ -791,7 +792,7 @@ def view_feedback(request, year):
def view_feedback_pending(request, year):
nomcom = get_nomcom_by_year(year)
if nomcom.group.state_id == 'conclude':
- return HttpResponseForbidden("This nomcom is concluded.")
+ permission_denied(request, "This nomcom is concluded.")
extra_ids = None
FeedbackFormSet = modelformset_factory(Feedback,
form=PendingFeedbackForm,
@@ -987,7 +988,7 @@ def edit_nomcom(request, year):
if request.method == 'POST':
if nomcom.group.state_id=='conclude':
- return HttpResponseForbidden('This nomcom is closed.')
+ permission_denied(request, 'This nomcom is closed.')
formset = ReminderDateInlineFormSet(request.POST, instance=nomcom)
form = EditNomcomForm(request.POST,
@@ -1069,7 +1070,7 @@ def list_positions(request, year):
def remove_position(request, year, position_id):
nomcom = get_nomcom_by_year(year)
if nomcom.group.state_id=='conclude':
- return HttpResponseForbidden('This nomcom is closed.')
+ permission_denied(request, 'This nomcom is closed.')
try:
position = nomcom.position_set.get(id=position_id)
except Position.DoesNotExist:
@@ -1091,7 +1092,7 @@ def edit_position(request, year, position_id=None):
nomcom = get_nomcom_by_year(year)
if nomcom.group.state_id=='conclude':
- return HttpResponseForbidden('This nomcom is closed.')
+ permission_denied(request, 'This nomcom is closed.')
if position_id:
try:
@@ -1136,7 +1137,7 @@ def list_topics(request, year):
def remove_topic(request, year, topic_id):
nomcom = get_nomcom_by_year(year)
if nomcom.group.state_id=='conclude':
- return HttpResponseForbidden('This nomcom is closed.')
+ permission_denied(request, 'This nomcom is closed.')
try:
topic = nomcom.topic_set.get(id=topic_id)
except Topic.DoesNotExist:
@@ -1158,7 +1159,7 @@ def edit_topic(request, year, topic_id=None):
nomcom = get_nomcom_by_year(year)
if nomcom.group.state_id=='conclude':
- return HttpResponseForbidden('This nomcom is closed.')
+ permission_denied(request, 'This nomcom is closed.')
if topic_id:
try:
@@ -1194,7 +1195,7 @@ def edit_members(request, year):
nomcom = get_nomcom_by_year(year)
if nomcom.group.state_id=='conclude':
- return HttpResponseForbidden('This nomcom is closed.')
+ permission_denied(request, 'This nomcom is closed.')
old_members_email = [r.email for r in nomcom.group.role_set.filter(name='member')]
diff --git a/ietf/person/models.py b/ietf/person/models.py
index c7aad0ed2..a197d5000 100644
--- a/ietf/person/models.py
+++ b/ietf/person/models.py
@@ -351,6 +351,7 @@ PERSON_API_KEY_VALUES = [
("/api/v2/person/person", "/api/v2/person/person", "Secretariat"),
("/api/meeting/session/video/url", "/api/meeting/session/video/url", "Recording Manager"),
("/api/notify/meeting/registration", "/api/notify/meeting/registration", "Robot"),
+ ("/api/notify/meeting/bluesheet", "/api/notify/meeting/bluesheet", "Recording Manager"),
]
PERSON_API_KEY_ENDPOINTS = [ (v, n) for (v, n, r) in PERSON_API_KEY_VALUES ]
diff --git a/ietf/secr/announcement/views.py b/ietf/secr/announcement/views.py
index a75c32731..42de089c5 100644
--- a/ietf/secr/announcement/views.py
+++ b/ietf/secr/announcement/views.py
@@ -1,6 +1,8 @@
+# Copyright The IETF Trust 2013-2020, All Rights Reserved
+
+
from django.contrib import messages
from django.contrib.auth.decorators import login_required
-from django.http import HttpResponseForbidden
from django.shortcuts import render, redirect
from ietf.group.models import Role
@@ -9,6 +11,8 @@ from ietf.ietfauth.utils import has_role
from ietf.secr.announcement.forms import AnnounceForm
from ietf.secr.utils.decorators import check_for_cancel
from ietf.utils.mail import send_mail_text
+from ietf.utils.response import permission_denied
+
# -------------------------------------------------
# Helper Functions
@@ -49,7 +53,7 @@ def main(request):
and send.
'''
if not check_access(request.user):
- return HttpResponseForbidden('Restricted to: Secretariat, IAD, or chair of IETF, IAB, RSOC, RSE, IAOC, ISOC, NomCom.')
+ permission_denied(request, 'Restricted to: Secretariat, IAD, or chair of IETF, IAB, RSOC, RSE, IAOC, ISOC, NomCom.')
form = AnnounceForm(request.POST or None,user=request.user)
@@ -74,7 +78,7 @@ def main(request):
def confirm(request):
if not check_access(request.user):
- return HttpResponseForbidden('Restricted to: Secretariat, IAD, or chair of IETF, IAB, RSOC, RSE, IAOC, ISOC, NomCom.')
+ permission_denied(request, 'Restricted to: Secretariat, IAD, or chair of IETF, IAB, RSOC, RSE, IAOC, ISOC, NomCom.')
if request.method == 'POST':
form = AnnounceForm(request.POST, user=request.user)
diff --git a/ietf/secr/utils/decorators.py b/ietf/secr/utils/decorators.py
index 450a0cb56..1fa4075bd 100644
--- a/ietf/secr/utils/decorators.py
+++ b/ietf/secr/utils/decorators.py
@@ -1,9 +1,10 @@
+# Copyright The IETF Trust 2013-2020, All Rights Reserved
from functools import wraps
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import ObjectDoesNotExist
-from django.http import HttpResponseRedirect, HttpResponseForbidden
+from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.utils.http import urlquote
@@ -12,6 +13,7 @@ from ietf.doc.models import Document
from ietf.group.models import Group, Role
from ietf.meeting.models import Session
from ietf.secr.utils.meeting import get_timeslot
+from ietf.utils.response import permission_denied
def check_for_cancel(redirect_url):
@@ -61,7 +63,7 @@ def check_permissions(func):
try:
login = request.user.person
except ObjectDoesNotExist:
- return HttpResponseForbidden("User not authorized to access group: %s" % group.acronym)
+ permission_denied(request, "User not authorized to access group: %s" % group.acronym)
groups = [group]
if group.parent:
@@ -78,7 +80,7 @@ def check_permissions(func):
return func(request, *args, **kwargs)
# if we get here access is denied
- return HttpResponseForbidden("User not authorized to access group: %s" % group.acronym)
+ permission_denied(request, "User not authorized to access group: %s" % group.acronym)
return wraps(func)(wrapper)
diff --git a/ietf/stats/views.py b/ietf/stats/views.py
index 6e0e65bf5..54b1621e3 100644
--- a/ietf/stats/views.py
+++ b/ietf/stats/views.py
@@ -15,7 +15,7 @@ from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.cache import cache
from django.db.models import Count, Q
-from django.http import HttpResponseRedirect, HttpResponseForbidden
+from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse as urlreverse
from django.utils.safestring import mark_safe
@@ -39,6 +39,7 @@ from ietf.stats.models import MeetingRegistration, CountryAlias
from ietf.stats.utils import get_aliased_affiliations, get_aliased_countries, compute_hirsch_index
from ietf.ietfauth.utils import has_role
from ietf.utils.log import log
+from ietf.utils.response import permission_denied
def stats_index(request):
return render(request, "stats/index.html")
@@ -1080,7 +1081,7 @@ def review_stats(request, stats_type=None, acronym=None):
reviewer_only_access.add(r.group_id)
if not secr_access and not reviewer_only_access:
- return HttpResponseForbidden("You do not have the necessary permissions to view this page")
+ permission_denied(request, "You do not have the necessary permissions to view this page")
teams = [t for t in teams if t.pk in secr_access or t.pk in reviewer_only_access]
diff --git a/ietf/submit/views.py b/ietf/submit/views.py
index 7759c3f93..83f6b3283 100644
--- a/ietf/submit/views.py
+++ b/ietf/submit/views.py
@@ -43,6 +43,7 @@ from ietf.stats.utils import clean_country_name
from ietf.utils.accesstoken import generate_access_token
from ietf.utils.log import log
from ietf.utils.mail import parseaddr, send_mail_message
+from ietf.utils.response import permission_denied
def upload_submission(request):
if request.method == 'POST':
@@ -298,7 +299,7 @@ def submission_status(request, submission_id, access_token=None):
action = request.POST.get('action')
if action == "autopost" and submission.state_id == "uploaded":
if not can_edit:
- return HttpResponseForbidden("You do not have permission to perform this action")
+ permission_denied(request, "You do not have permission to perform this action")
submitter_form = SubmitterForm(request.POST, prefix="submitter")
replaces_form = ReplacesForm(request.POST, name=submission.name)
@@ -313,7 +314,7 @@ def submission_status(request, submission_id, access_token=None):
if approvals_received:
if not is_secretariat:
- return HttpResponseForbidden('You do not have permission to perform this action')
+ permission_denied(request, 'You do not have permission to perform this action')
# go directly to posting submission
docevent_from_submission(request, submission, desc="Uploaded new revision")
@@ -362,7 +363,7 @@ def submission_status(request, submission_id, access_token=None):
elif action == "cancel" and submission.state.next_states.filter(slug="cancel"):
if not can_cancel:
- return HttpResponseForbidden('You do not have permission to perform this action')
+ permission_denied(request, 'You do not have permission to perform this action.')
cancel_submission(submission)
@@ -373,7 +374,7 @@ def submission_status(request, submission_id, access_token=None):
elif action == "approve" and submission.state_id == "grp-appr":
if not can_group_approve:
- return HttpResponseForbidden('You do not have permission to perform this action')
+ permission_denied(request, 'You do not have permission to perform this action.')
post_submission(request, submission, "WG -00 approved", "Approved and posted submission")
@@ -382,7 +383,7 @@ def submission_status(request, submission_id, access_token=None):
elif action == "forcepost" and submission.state.next_states.filter(slug="posted"):
if not can_force_post:
- return HttpResponseForbidden('You do not have permission to perform this action')
+ permission_denied(request, 'You do not have permission to perform this action.')
if submission.state_id == "manual":
desc = "Posted submission manually"
@@ -424,7 +425,7 @@ def edit_submission(request, submission_id, access_token=None):
submission = get_object_or_404(Submission, pk=submission_id, state="uploaded")
if not can_edit_submission(request.user, submission, access_token):
- return HttpResponseForbidden('You do not have permission to access this page')
+ permission_denied(request, 'You do not have permission to access this page.')
errors = validate_submission(submission)
form_errors = False
@@ -638,7 +639,7 @@ def cancel_waiting_for_draft(request):
can_cancel = has_role(request.user, "Secretariat")
if not can_cancel:
- return HttpResponseForbidden('You do not have permission to perform this action')
+ permission_denied(request, 'You do not have permission to perform this action.')
submission_id = request.POST.get('submission_id', '')
access_token = request.POST.get('access_token', '')
diff --git a/ietf/sync/views.py b/ietf/sync/views.py
index 9a5b797be..d6fc505c2 100644
--- a/ietf/sync/views.py
+++ b/ietf/sync/views.py
@@ -7,11 +7,11 @@ import subprocess
import os
import json
-from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect, Http404
-from django.shortcuts import render
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
+from django.http import HttpResponse, HttpResponseRedirect, Http404
+from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from ietf.doc.models import DeletedEvent, StateDocEvent, DocEvent
@@ -19,6 +19,8 @@ from ietf.ietfauth.utils import role_required, has_role
from ietf.sync.discrepancies import find_discrepancies
from ietf.utils.serialize import object_as_shallow_dict
from ietf.utils.log import log
+from ietf.utils.response import permission_denied
+
SYNC_BIN_PATH = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../bin"))
@@ -51,7 +53,7 @@ def notify(request, org, notification):
if username != None and password != None:
if settings.SERVER_MODE == "production" and not request.is_secure():
- return HttpResponseForbidden("You must use HTTPS when sending username/password")
+ permission_denied(request, "You must use HTTPS when sending username/password.")
if not user.is_authenticated:
try:
@@ -63,7 +65,7 @@ def notify(request, org, notification):
return HttpResponse("Invalid username/password")
if not has_role(user, ("Secretariat", known_orgs[org])):
- return HttpResponseForbidden("You do not have the necessary permissions to view this page")
+ permission_denied(request, "You do not have the necessary permissions to view this page.")
known_notifications = {
"protocols": "an added reference to an RFC at the IANA protocols page" % settings.IANA_SYNC_PROTOCOLS_URL,
diff --git a/ietf/templates/403.html b/ietf/templates/403.html
index 8acf070f1..e117dbf17 100644
--- a/ietf/templates/403.html
+++ b/ietf/templates/403.html
@@ -9,9 +9,6 @@
Restricted Access.
-
- The page you tried to reach is not generally avaiable.
-
{{ exception }}
If you think this is a server error, please contact {{ bugreport_email }}.
diff --git a/ietf/utils/decorators.py b/ietf/utils/decorators.py
index 9030af073..694e989f8 100644
--- a/ietf/utils/decorators.py
+++ b/ietf/utils/decorators.py
@@ -55,7 +55,7 @@ def require_api_key(f, request, *args, **kwargs):
# Check hash
key = PersonalApiKey.validate_key(force_bytes(hash))
if not key:
- return err(400, "Invalid apikey")
+ return err(403, "Invalid apikey")
# Check endpoint
urlpath = request.META.get('PATH_INFO')
if not (urlpath and urlpath == key.endpoint):
diff --git a/ietf/utils/response.py b/ietf/utils/response.py
index e6b1ed2a2..3321e99fe 100644
--- a/ietf/utils/response.py
+++ b/ietf/utils/response.py
@@ -6,5 +6,6 @@ from django.utils.safestring import mark_safe
def permission_denied(request, msg):
"A wrapper around the PermissionDenied exception"
- msg += "
You can Log in if you have that role but aren't logged in." % request.path
+ if not request.user.is_authenticated:
+ msg += "
You may want to Log in if you have a datatracker role that lets you access this page." % request.path
raise PermissionDenied(mark_safe(msg))