-
Notifications
You must be signed in to change notification settings - Fork 378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Ballot return to url via url params rather than session #7788
Changes from 3 commits
dcc9e26
88d661e
a7cb583
5d6d12a
353e9c7
0b7817f
4177021
23354a9
0a7605f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,9 +12,11 @@ | |
from django.shortcuts import render, get_object_or_404, redirect | ||
from django.template.defaultfilters import striptags | ||
from django.template.loader import render_to_string | ||
from django.urls import reverse as urlreverse | ||
from django.urls import reverse as urlreverse, resolve as urlresolve, Resolver404 | ||
from django.views.decorators.csrf import csrf_exempt | ||
from django.utils.html import escape | ||
from urllib.parse import urlencode as urllib_urlencode | ||
|
||
|
||
import debug # pyflakes:ignore | ||
|
||
|
@@ -185,9 +187,9 @@ def edit_position(request, name, ballot_id): | |
|
||
balloter = login = request.user.person | ||
|
||
if 'ballot_edit_return_point' in request.session: | ||
return_to_url = request.session['ballot_edit_return_point'] | ||
else: | ||
return_to_url = request.GET.get("ballot_edit_return_point") | ||
|
||
if return_to_url is None: | ||
return_to_url = urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name, ballot_id=ballot_id)) | ||
|
||
# if we're in the Secretariat, we can select a balloter to act as stand-in for | ||
|
@@ -209,9 +211,14 @@ def edit_position(request, name, ballot_id): | |
save_position(form, doc, ballot, balloter, login, send_mail) | ||
|
||
if send_mail: | ||
qstr="" | ||
query=dict() | ||
if request.GET.get('balloter'): | ||
qstr += "?balloter=%s" % request.GET.get('balloter') | ||
query['balloter'] = request.GET.get('balloter') | ||
if request.GET.get('ballot_edit_return_point'): | ||
query['ballot_edit_return_point'] = request.GET.get('ballot_edit_return_point') | ||
qstr = "" | ||
if len(query) > 0: | ||
qstr = "?" + urllib_urlencode(query, safe='/') | ||
return HttpResponseRedirect(urlreverse('ietf.doc.views_ballot.send_ballot_comment', kwargs=dict(name=doc.name, ballot_id=ballot_id)) + qstr) | ||
elif request.POST.get("Defer") and doc.stream.slug != "irtf": | ||
return redirect('ietf.doc.views_ballot.defer_ballot', name=doc) | ||
|
@@ -337,11 +344,24 @@ def send_ballot_comment(request, name, ballot_id): | |
|
||
balloter = request.user.person | ||
|
||
if 'ballot_edit_return_point' in request.session: | ||
return_to_url = request.session['ballot_edit_return_point'] | ||
else: | ||
return_to_url = urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name, ballot_id=ballot_id)) | ||
return_to_url = request.GET.get('ballot_edit_return_point') | ||
|
||
if return_to_url is not None: | ||
# we need to ensure return_to_url isn't used for phishing attacks. | ||
# return_to_url is used in HttpResponseRedirect() which could redirect to Datatracker or offsite. | ||
# Eg http://datatracker.ietf.org/?ballot_edit_return_point=https://example.com/phishing-attack | ||
# offsite links could be phishing attempts so let's reject them all, and require valid Datatracker | ||
# routes | ||
try: | ||
urlresolve(return_to_url) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should instead (or maybe in addition) check that the URL is not absolute? I think that'd prevent the phishing issue. I wonder if it'd be worth putting an even tighter check here and only redirect to the view that we want. E.g., someone could make a nuisance link that'd cause logout (at least, until we disable log out by GET). I worry that there might be more harmful games possible with this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hmm... I thought about checking whether it was a relative vs absolute URL through checking if it started with a
there are currently 5 views that assign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ^^ working on this now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Thoughts? |
||
# if it doesn't throw then it's a valid route | ||
pass | ||
except Resolver404: | ||
raise ValueError(f"Invalid ballot_edit_return_point doesn't match a route: {return_to_url}") | ||
|
||
if return_to_url is None: | ||
return_to_url = urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name, ballot_id=ballot_id)) | ||
|
||
if 'HTTP_REFERER' in request.META: | ||
back_url = request.META['HTTP_REFERER'] | ||
else: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module.exports = { | ||
login: async (page, baseURL, username, password) => { | ||
await page.goto('/accounts/login/') | ||
await page.getByLabel('Username').fill(username) | ||
await page.getByLabel('Password').fill(password) | ||
await page.getByRole('button', { name: 'Sign in' }).click() | ||
// Wait until the page receives the cookies. | ||
// | ||
// Theoretically login flow could set cookies in the process of several | ||
// redirects. | ||
// Wait for the final URL to ensure that the cookies are actually set. | ||
await page.waitForURL( | ||
new URL('/accounts/profile/', baseURL).toString() | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
const { test, expect } = require('@playwright/test') | ||
Check failure on line 1 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (chromium)[chromium] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 1 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (chromium)[chromium] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 1 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (chromium)[chromium] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 1 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (firefox)[firefox] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 1 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (firefox)[firefox] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 1 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (firefox)[firefox] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
|
||
const viewports = require('../helpers/viewports') | ||
const session = require('../helpers/session') | ||
|
||
// ==================================================================== | ||
// BALLOT - EDITING | ||
// ==================================================================== | ||
|
||
test.describe('ballot edit position ', () => { | ||
test.beforeEach(async ({ page, baseURL }) => { | ||
await page.setViewportSize({ | ||
width: viewports.desktop[0], | ||
height: viewports.desktop[1] | ||
}) | ||
// Is there an AD user for local testing? | ||
await session.login(page, baseURL, '[email protected]', 'password') | ||
}) | ||
|
||
test('redirect back to ballot_edit_return_point param' , async ({ page, baseURL }, workerInfo) => { | ||
await page.goto('/doc/draft-ietf-opsawg-ipfix-tcpo-v6eh/ballot/') | ||
const editPositionButton = await page.getByRole('link', { name: 'Edit position' }) | ||
const href = await editPositionButton.getAttribute('href') | ||
// The href's query param 'ballot_edit_return_point' should point to the current page | ||
const hrefUrl = new URL(href, baseURL) | ||
|
||
const entryPageUrl = new URL(page.url(), baseURL) | ||
await expect(hrefUrl.searchParams.get('ballot_edit_return_point')).toBe(entryPageUrl.pathname) | ||
await editPositionButton.click() | ||
await page.waitForURL('**/position') | ||
Check failure on line 29 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (chromium)[chromium] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 29 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (chromium)[chromium] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 29 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (chromium)[chromium] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 29 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (firefox)[firefox] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 29 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (firefox)[firefox] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
Check failure on line 29 in playwright/tests-legacy/ballot-edit.spec.js GitHub Actions / tests / Playwright Legacy Tests (firefox)[firefox] › ballot-edit.spec.js:19:3 › ballot edit position › redirect back to ballot_edit_return_point param
|
||
|
||
// The position page has several ways through it so we'll test each one to see if 'ballot_edit_return_point' works | ||
|
||
// Option 1. Try the default 'Save' button | ||
await page.getByRole('button', { name: 'Save' }) | ||
await page.waitForURL(entryPageUrl.pathname) | ||
|
||
// Option 2. Try the 'Save & send email' button | ||
await page.getByRole('link', { name: 'Edit position' }).click() | ||
await page.waitForURL('**/position') | ||
await page.getByRole('button', { name: 'Save & send email' }).click() | ||
await page.waitForURL('**/emailposition/') | ||
await page.getByRole('button', { name: 'Send' }).click() | ||
await page.waitForURL(entryPageUrl.pathname) | ||
|
||
// TODO: Option 3. Try the 'Defer ballot' button | ||
// This doens't yet work. | ||
// await page.getByRole('link', { name: 'Edit position' }).click() | ||
// await page.waitForURL('**/position') | ||
// await page.getByRole('button', { name: 'Defer ballot' }).click() | ||
// await page.waitForURL('**/deferballot/') | ||
}) | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax nit:
query = dict()
(orquery = {}
is perhaps trivially more efficient); either way, style is to have spaces around the=