From 9eab5be9a33e7dab46e7de4692ca788868d816dc Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Sun, 21 Aug 2011 22:35:01 +0200 Subject: [PATCH] RequestRedirect uses a HTTP 301. We need 303. This is mainly because 301 is cacheable whereas 303 (See other) isn't. The redirect response given by the app when trying to connect to a project (via /project_name) while not authenticated was to permanently redirect to /authenticate. Once authenticated, the browser was redirected to the /project_name, that was cached, leading to an endless loop. 303 see other allows to solve this problem. --- budget/utils.py | 33 +++++++++++---------------------- budget/web.py | 7 +++---- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/budget/utils.py b/budget/utils.py index 77c4ad6..262ebfe 100644 --- a/budget/utils.py +++ b/budget/utils.py @@ -1,5 +1,6 @@ from functools import wraps from flask import redirect, url_for, session, request +from werkzeug.routing import HTTPException, RoutingException from models import Bill, Project from forms import BillForm @@ -19,29 +20,17 @@ def get_billform_for(project, set_default=True): form.set_default() return form -def requires_auth(f): - """Decorator checking that the user do have access to the given project id. +class Redirect303(HTTPException, RoutingException): + """Raise if the map requests a redirect. This is for example the case if + `strict_slashes` are activated and an url that requires a trailing slash. - If not, redirects to an authentication page, otherwise display the requested - page. + The attribute `new_url` contains the absolute destination url. """ + code = 303 - @wraps(f) - def decorator(*args, **kwargs): - # if a project id is specified in kwargs, check we have access to it - # get the password matching this project id - # pop project_id out of the kwargs - project_id = kwargs.pop('project_id') - project = Project.query.get(project_id) - if not project: - return redirect(url_for("create_project", project_id=project_id)) + def __init__(self, new_url): + RoutingException.__init__(self, new_url) + self.new_url = new_url - if project.id in session and session[project.id] == project.password: - # add project into kwargs and call the original function - kwargs['project'] = project - return f(*args, **kwargs) - else: - # redirect to authentication page - return redirect(url_for("authenticate", - project_id=project.id, redirect_url=request.url)) - return decorator + def get_response(self, environ): + return redirect(self.new_url, 303) diff --git a/budget/web.py b/budget/web.py index fa3f485..78886d8 100644 --- a/budget/web.py +++ b/budget/web.py @@ -2,12 +2,11 @@ from collections import defaultdict from flask import * from flaskext.mail import Mail, Message -from werkzeug.routing import RequestRedirect # local modules from models import db, Project, Person, Bill from forms import ProjectForm, AuthenticationForm, BillForm, MemberForm, InviteForm -from utils import get_billform_for, requires_auth +from utils import get_billform_for, Redirect303 # create the application, initialize stuff app = Flask(__name__) @@ -40,13 +39,13 @@ def pull_project(endpoint, values): if project_id: project = Project.query.get(project_id) if not project: - raise RequestRedirect(url_for("create_project", project_id=project_id)) + raise Redirect303(url_for("create_project", project_id=project_id)) if project.id in session and session[project.id] == project.password: # add project into kwargs and call the original function g.project = project else: # redirect to authentication page - raise RequestRedirect( + raise Redirect303( url_for("authenticate", project_id=project_id)) @app.route("/authenticate", methods=["GET", "POST"])