From 650d8316eb0fd4d971df158d806b52e710c73a22 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Fri, 15 Nov 2013 16:09:05 +0000 Subject: [PATCH] Make the submit tool use the access token framework with a bit of backwards compatibility glue - Legacy-Id: 6717 --- ietf/submit/admin.py | 2 +- ietf/submit/mail.py | 10 ++-- ietf/submit/models.py | 7 ++- ietf/submit/urls.py | 6 +-- ietf/submit/views.py | 46 +++++++++++-------- ietf/templates/submit/approval_request.txt | 2 +- ietf/templates/submit/approvals.html | 2 +- ietf/templates/submit/confirm_submission.html | 2 +- 8 files changed, 44 insertions(+), 33 deletions(-) diff --git a/ietf/submit/admin.py b/ietf/submit/admin.py index e870b0659..19e7a8113 100644 --- a/ietf/submit/admin.py +++ b/ietf/submit/admin.py @@ -13,7 +13,7 @@ class SubmissionAdmin(admin.ModelAdmin): def status_link(self, instance): url = urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=instance.pk, - access_key=instance.access_key)) + access_token=instance.access_token())) return '%s' % (url, instance.state) status_link.allow_tags = True diff --git a/ietf/submit/mail.py b/ietf/submit/mail.py index bfe0f8584..42ddeff3f 100644 --- a/ietf/submit/mail.py +++ b/ietf/submit/mail.py @@ -8,6 +8,7 @@ from ietf.doc.models import Document from ietf.person.models import Person from ietf.group.models import Role from ietf.message.models import Message +from ietf.utils.accesstoken import generate_access_token def submission_confirmation_email_list(submission): try: @@ -25,8 +26,8 @@ def send_submission_confirmation(request, submission): from_email = settings.IDSUBMIT_FROM_EMAIL to_email = submission_confirmation_email_list(submission) - confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_confirm_submission', kwargs=dict(submission_id=submission.pk, auth_key=submission.auth_key)) - status_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_key=submission.access_key)) + confirm_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_confirm_submission', kwargs=dict(submission_id=submission.pk, auth_token=generate_access_token(submission.auth_key))) + status_url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_token=submission.access_token())) send_mail(request, to_email, from_email, subject, 'submit/confirm_submission.txt', { 'submission': submission, @@ -40,9 +41,8 @@ def send_full_url(request, submission): subject = 'Full URL for managing submission of draft %s' % submission.name from_email = settings.IDSUBMIT_FROM_EMAIL to_email = submission_confirmation_email_list(submission) - url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', - kwargs=dict(submission_id=submission.pk, - access_key=submission.access_key)) + url = settings.IDTRACKER_BASE_URL + urlreverse('submit_submission_status_by_hash', kwargs=dict(submission_id=submission.pk, access_token=submission.access_token())) + send_mail(request, to_email, from_email, subject, 'submit/full_url.txt', { 'submission': submission, 'url': url, diff --git a/ietf/submit/models.py b/ietf/submit/models.py index d523f88fb..27184fe45 100644 --- a/ietf/submit/models.py +++ b/ietf/submit/models.py @@ -6,7 +6,7 @@ from django.db import models from ietf.person.models import Person from ietf.group.models import Group from ietf.name.models import DraftSubmissionStateName -from ietf.utils.uniquekey import generate_unique_key +from ietf.utils.accesstoken import generate_random_key, generate_access_token def parse_email_line(line): @@ -21,7 +21,7 @@ class Submission(models.Model): state = models.ForeignKey(DraftSubmissionStateName) remote_ip = models.CharField(max_length=100, blank=True) - access_key = models.CharField(max_length=255, default=generate_unique_key) + access_key = models.CharField(max_length=255, default=generate_random_key) auth_key = models.CharField(max_length=255, blank=True) # draft metadata @@ -59,6 +59,9 @@ class Submission(models.Model): def submitter_parsed(self): return parse_email_line(self.submitter) + def access_token(self): + return generate_access_token(self.access_key) + class SubmissionEvent(models.Model): submission = models.ForeignKey(Submission) diff --git a/ietf/submit/urls.py b/ietf/submit/urls.py index acf6091e9..049cbb0f5 100644 --- a/ietf/submit/urls.py +++ b/ietf/submit/urls.py @@ -6,9 +6,9 @@ urlpatterns = patterns('ietf.submit.views', url(r'^status/$', 'search_submission', name='submit_search_submission'), url(r'^status/(?P\d+)/$', 'submission_status', name='submit_submission_status'), url(r'^status/(?P\d+)/edit/$', 'edit_submission', name='submit_edit_submission'), - url(r'^status/(?P\d+)/confirm/(?P[a-f\d]+)/$', 'confirm_submission', name='submit_confirm_submission'), - url(r'^status/(?P\d+)/(?P[a-f\d]*)/$', 'submission_status', name='submit_submission_status_by_hash'), - url(r'^status/(?P\d+)/(?P[a-f\d]+)/edit/$', 'edit_submission', name='submit_edit_submission_by_hash'), + url(r'^status/(?P\d+)/confirm/(?P[a-f\d]+)/$', 'confirm_submission', name='submit_confirm_submission'), + url(r'^status/(?P\d+)/(?P[a-f\d]*)/$', 'submission_status', name='submit_submission_status_by_hash'), + url(r'^status/(?P\d+)/(?P[a-f\d]+)/edit/$', 'edit_submission', name='submit_edit_submission_by_hash'), url(r'^note-well/$', 'note_well', name='submit_note_well'), url(r'^tool-instructions/$', 'tool_instructions', name='submit_tool_instructions'), diff --git a/ietf/submit/views.py b/ietf/submit/views.py index edce05cd7..1878610d7 100644 --- a/ietf/submit/views.py +++ b/ietf/submit/views.py @@ -21,7 +21,7 @@ from ietf.submit.utils import approvable_submissions_for_user, preapprovals_for_ from ietf.submit.utils import check_idnits, found_idnits, validate_submission, create_submission_event from ietf.submit.utils import post_submission, cancel_submission, rename_submission_files from ietf.submit.mail import send_full_url, send_approval_request_to_group, send_submission_confirmation, submission_confirmation_email_list, send_manual_post_request -from ietf.utils.uniquekey import generate_unique_key +from ietf.utils.accesstoken import generate_random_key, generate_access_token def upload_submission(request): if request.method == 'POST': @@ -89,7 +89,7 @@ def upload_submission(request): create_submission_event(request, submission, desc="Uploaded submission") - return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_key=submission.access_key) + return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_token=submission.access_token()) except IOError as e: if "read error" in str(e): # The server got an IOError when trying to read POST data form = UploadForm(request=request) @@ -128,23 +128,26 @@ def search_submission(request): 'name': name}, context_instance=RequestContext(request)) -def can_edit_submission(request, submission, access_key): - key_matched = access_key and submission.access_key == access_key +def can_edit_submission(request, submission, access_token): + key_matched = access_token and submission.access_token() == access_token + if not key_matched: key_matched = submission.access_key == access_token # backwards-compat return key_matched or has_role(request.user, "Secretariat") -def submission_status(request, submission_id, access_key=None, message=None): +def submission_status(request, submission_id, access_token=None): submission = get_object_or_404(Submission, pk=submission_id) - if access_key and submission.access_key != access_key: + + key_matched = access_token and submission.access_token() == access_token + if not key_matched: key_matched = submission.access_key == access_token # backwards-compat + if access_token and not key_matched: raise Http404 errors = validate_submission(submission) passes_idnits = found_idnits(submission.idnits_message) - key_matched = access_key and submission.access_key == access_key is_secretariat = has_role(request.user, "Secretariat") is_chair = submission.group and submission.group.has_role(request.user, "chair") - can_edit = can_edit_submission(request, submission, access_key) and submission.state_id == "uploaded" + can_edit = can_edit_submission(request, submission, access_token) and submission.state_id == "uploaded" can_cancel = (key_matched or is_secretariat) and submission.state.next_states.filter(slug="cancel") can_group_approve = (is_secretariat or is_chair) and submission.state_id == "grp-appr" can_force_post = is_secretariat and submission.state.next_states.filter(slug="posted") @@ -161,8 +164,10 @@ def submission_status(request, submission_id, access_key=None, message=None): requires_prev_authors_approval = Document.objects.filter(name=submission.name) + message = None + if submission.state_id == "cancel": - message = ('error', 'This submission has been cancelled, modification is no longer possible.') + message = ('error', 'This submission has been canceled, modification is no longer possible.') elif submission.state_id == "auth": message = ('success', u'The submission is pending email authentication. An email has been sent to: %s' % ",".join(confirmation_list)) elif submission.state_id == "grp-appr": @@ -192,7 +197,7 @@ def submission_status(request, submission_id, access_key=None, message=None): desc = "sent approval email to group chairs: %s" % u", ".join(sent_to) else: - submission.auth_key = generate_unique_key() + submission.auth_key = generate_random_key() if requires_prev_authors_approval: submission.state = DraftSubmissionStateName.objects.get(slug="aut-appr") else: @@ -208,11 +213,11 @@ def submission_status(request, submission_id, access_key=None, message=None): create_submission_event(request, submission, u"Set submitter to \"%s\" and %s" % (submission.submitter, desc)) - return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_key=access_key) + return redirect("submit_submission_status_by_hash", submission_id=submission.pk, access_token=access_token) elif action == "edit" and submission.state_id == "uploaded": - if access_key: - return redirect("submit_edit_submission_by_hash", submission_id=submission.pk, access_key=access_key) + if access_token: + return redirect("submit_edit_submission_by_hash", submission_id=submission.pk, access_token=access_token) else: return redirect("submit_edit_submission", submission_id=submission.pk) @@ -229,7 +234,7 @@ def submission_status(request, submission_id, access_key=None, message=None): cancel_submission(submission) - create_submission_event(request, submission, "Cancelled submission") + create_submission_event(request, submission, "Canceled submission") return redirect("submit_submission_status", submission_id=submission_id) @@ -284,10 +289,10 @@ def submission_status(request, submission_id, access_key=None, message=None): context_instance=RequestContext(request)) -def edit_submission(request, submission_id, access_key=None): +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_key): + if not can_edit_submission(request.user, submission, access_token): return HttpResponseForbidden('You do not have permission to access this page') errors = validate_submission(submission) @@ -360,10 +365,13 @@ def edit_submission(request, submission_id, access_key=None): context_instance=RequestContext(request)) -def confirm_submission(request, submission_id, auth_key): +def confirm_submission(request, submission_id, auth_token): submission = get_object_or_404(Submission, pk=submission_id) - if request.method == 'POST' and submission.state_id in ("auth", "aut-appr") and auth_key == submission.auth_key: + key_matched = submission.auth_key and auth_token == generate_access_token(submission.auth_key) + if not key_matched: key_matched = auth_token == submission.auth_key # backwards-compat + + if request.method == 'POST' and submission.state_id in ("auth", "aut-appr") and key_matched: post_submission(request, submission) create_submission_event(request, submission, "Confirmed and posted submission") @@ -372,7 +380,7 @@ def confirm_submission(request, submission_id, auth_key): return render_to_response('submit/confirm_submission.html', { 'submission': submission, - 'auth_key': auth_key, + 'key_matched': key_matched, }, context_instance=RequestContext(request)) diff --git a/ietf/templates/submit/approval_request.txt b/ietf/templates/submit/approval_request.txt index 46e647cd7..3eaa989fe 100644 --- a/ietf/templates/submit/approval_request.txt +++ b/ietf/templates/submit/approval_request.txt @@ -4,7 +4,7 @@ Hi, Chair approval is needed for posting of {{ submission.name }}-{{ submission.rev }}. To approve the draft, go to this URL (note: you need to login to be able to approve): - https://{{ domain }}/submit/status/{{ submission.pk }}/{{ submission.access_key }}/ + https://{{ domain }}{% url submit_submission_status_by_hash submission_id=submission.pk access_token=submission.access_token %} File name : {{ submission.name }} Revision : {{ submission.rev }} diff --git a/ietf/templates/submit/approvals.html b/ietf/templates/submit/approvals.html index cfecabd85..960eaa61b 100644 --- a/ietf/templates/submit/approvals.html +++ b/ietf/templates/submit/approvals.html @@ -28,7 +28,7 @@ table.preapprovals tr:hover td a.cancel { visibility: visible; } {% for s in approvals %} - {{ s.name }}-{{ s.rev }} + {{ s.name }}-{{ s.rev }} {{ s.submission_date }} {% endfor %} diff --git a/ietf/templates/submit/confirm_submission.html b/ietf/templates/submit/confirm_submission.html index 8c0e4b56b..ecc10973f 100644 --- a/ietf/templates/submit/confirm_submission.html +++ b/ietf/templates/submit/confirm_submission.html @@ -23,7 +23,7 @@ p.error { color: red; font-weight: bold; font-size: 1.5em; } {% endif %} {% else %} - {% if auth_key != submission.auth_key %} + {% if not key_matched %}

Incorrect authorization key.

Double-check the link you followed. If everything fails, you can go to