New submission from timor <ti...@cyhex.com>:
the repoze.who-friendlyform does not check for max_age value in the form.
fix:
add those lines to: friendlyform.py:129
#####
max_age = form.get('max_age', None)
if credentials and max_age:
credentials['max_age'] = max_age
######
cheers
timor
----------
files: friendlyform.py
messages: 271
nosy: cyhex
priority: bug
status: unread
title: max_age in repoze.who-friendlyform
__________________________________
Repoze Bugs <b...@bugs.repoze.org>
<http://bugs.repoze.org/issue99>
__________________________________
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2009, Gustavo Narea <m...@gustavonarea.net>.
# All Rights Reserved.
#
# This software is subject to the provisions of the BSD-like license at
# http://www.repoze.org/LICENSE.txt. A copy of the license should accompany
# this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
# FITNESS FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Collection of :mod:`repoze.who` friendly forms"""
from urlparse import urlparse, urlunparse
from urllib import urlencode
try:
from urlparse import parse_qs
except ImportError:
from cgi import parse_qs
from paste.httpexceptions import HTTPFound, HTTPUnauthorized
from paste.request import construct_url, parse_dict_querystring, parse_formvars
from paste.response import replace_header, header_value
from zope.interface import implements
from repoze.who.interfaces import IChallenger, IIdentifier
__all__ = ['FriendlyFormPlugin']
class FriendlyFormPlugin(object):
"""
:class:`RedirectingFormPlugin
<repoze.who.plugins.form.RedirectingFormPlugin>`-like form plugin with
more features.
It is like ``RedirectingFormPlugin``, but provides us with the following
features:
* Users are not challenged on logout, unless the referrer URL is a
private one (but that's up to the application).
* Developers may define post-login and/or post-logout pages.
* In the login URL, the amount of failed logins is available in the
environ. It's also increased by one on every login try. This counter
will allow developers not using a post-login page to handle logins that
fail/succeed.
You should keep in mind that if you're using a post-login or a post-logout
page, that page will receive the referrer URL as a query string variable
whose name is "came_from".
"""
implements(IChallenger, IIdentifier)
def __init__(self, login_form_url, login_handler_path, post_login_url,
logout_handler_path, post_logout_url, rememberer_name,
login_counter_name=None):
"""
:param login_form_url: The URL/path where the login form is located.
:type login_form_url: str
:param login_handler_path: The URL/path where the login form is
submitted to (where it is processed by this plugin).
:type login_handler_path: str
:param post_login_url: The URL/path where the user should be redirected
to after login (even if wrong credentials were provided).
:type post_login_url: str
:param logout_handler_path: The URL/path where the user is logged out.
:type logout_handler_path: str
:param post_logout_url: The URL/path where the user should be
redirected to after logout.
:type post_logout_url: str
:param rememberer_name: The name of the repoze.who identifier which
acts as rememberer.
:type rememberer_name: str
:param login_counter_name: The name of the query string variable which
will represent the login counter.
:type login_counter_name: str
The login counter variable's name will be set to ``__logins`` if
``login_counter_name`` equals None.
"""
self.login_form_url = login_form_url
self.login_handler_path = login_handler_path
self.post_login_url = post_login_url
self.logout_handler_path = logout_handler_path
self.post_logout_url = post_logout_url
self.rememberer_name = rememberer_name
self.login_counter_name = login_counter_name
if not login_counter_name:
self.login_counter_name = '__logins'
# IIdentifier
def identify(self, environ):
"""
Override the parent's identifier to introduce a login counter
(possibly along with a post-login page) and load the login counter into
the ``environ``.
"""
path_info = environ['PATH_INFO']
script_name = environ.get('SCRIPT_NAME') or '/'
query = parse_dict_querystring(environ)
if path_info == self.login_handler_path:
## We are on the URL where repoze.who processes authentication. ##
# Let's append the login counter to the query string of the
# "came_from" URL. It will be used by the challenge below if
# authorization is denied for this request.
form = parse_formvars(environ)
form.update(query)
try:
login = form['login']
password = form['password']
credentials = {
'login': form['login'],
'password': form['password']
}
except KeyError:
credentials = None
referer = environ.get('HTTP_REFERER', script_name)
destination = form.get('came_from', referer)
max_age = form.get('max_age', None)
if credentials and max_age:
credentials['max_age'] = max_age
if self.post_login_url:
# There's a post-login page, so we have to replace the
# destination with it.
destination = self._get_full_path(self.post_login_url,
environ)
if 'came_from' in query:
# There's a referrer URL defined, so we have to pass it to
# the post-login page as a GET variable.
destination = self._insert_qs_variable(destination,
'came_from',
query['came_from'])
failed_logins = self._get_logins(environ, True)
new_dest = self._set_logins_in_url(destination, failed_logins)
environ['repoze.who.application'] = HTTPFound(new_dest)
return credentials
elif path_info == self.logout_handler_path:
## We are on the URL where repoze.who logs the user out. ##
form = parse_formvars(environ)
form.update(query)
referer = environ.get('HTTP_REFERER', script_name)
came_from = form.get('came_from', referer)
# set in environ for self.challenge() to find later
environ['came_from'] = came_from
environ['repoze.who.application'] = HTTPUnauthorized()
return None
elif path_info == self.login_form_url or self._get_logins(environ):
## We are on the URL that displays the from OR any other page ##
## where the login counter is included in the query string. ##
# So let's load the counter into the environ and then hide it from
# the query string (it will cause problems in frameworks like TG2,
# where this unexpected variable would be passed to the controller)
environ['repoze.who.logins'] = self._get_logins(environ, True)
# Hiding the GET variable in the environ:
if self.login_counter_name in query:
del query[self.login_counter_name]
environ['QUERY_STRING'] = urlencode(query, doseq=True)
# IChallenger
def challenge(self, environ, status, app_headers, forget_headers):
"""
Override the parent's challenge to avoid challenging the user on
logout, introduce a post-logout page and/or pass the login counter
to the login form.
"""
url_parts = list(urlparse(self.login_form_url))
query = url_parts[4]
query_elements = parse_qs(query)
came_from = environ.get('came_from', construct_url(environ))
query_elements['came_from'] = came_from
url_parts[4] = urlencode(query_elements, doseq=True)
login_form_url = urlunparse(url_parts)
login_form_url = self._get_full_path(login_form_url, environ)
destination = login_form_url
# Configuring the headers to be set:
cookies = [(h,v) for (h,v) in app_headers if h.lower() == 'set-cookie']
headers = forget_headers + cookies
if environ['PATH_INFO'] == self.logout_handler_path:
# Let's log the user out without challenging.
came_from = environ.get('came_from')
if self.post_logout_url:
# Redirect to a predefined "post logout" URL.
destination = self._get_full_path(self.post_logout_url,
environ)
if came_from:
destination = self._insert_qs_variable(
destination, 'came_from', came_from)
else:
# Redirect to the referrer URL.
script_name = environ.get('SCRIPT_NAME', '')
destination = came_from or script_name or '/'
elif 'repoze.who.logins' in environ:
# Login failed! Let's redirect to the login form and include
# the login counter in the query string
environ['repoze.who.logins'] += 1
# Re-building the URL:
destination = self._set_logins_in_url(destination,
environ['repoze.who.logins'])
return HTTPFound(destination, headers=headers)
# IIdentifier
def remember(self, environ, identity):
rememberer = self._get_rememberer(environ)
return rememberer.remember(environ, identity)
# IIdentifier
def forget(self, environ, identity):
rememberer = self._get_rememberer(environ)
return rememberer.forget(environ, identity)
def _get_rememberer(self, environ):
rememberer = environ['repoze.who.plugins'][self.rememberer_name]
return rememberer
def _get_full_path(self, path, environ):
"""
Return the full path to ``path`` by prepending the SCRIPT_NAME.
If ``path`` is a URL, do nothing.
"""
if path.startswith('/'):
path = environ.get('SCRIPT_NAME', '') + path
return path
def _get_logins(self, environ, force_typecast=False):
"""
Return the login counter from the query string in the ``environ``.
If it's not possible to convert it into an integer and
``force_typecast`` is ``True``, it will be set to zero (int(0)).
Otherwise, it will be ``None`` or an string.
"""
variables = parse_dict_querystring(environ)
failed_logins = variables.get(self.login_counter_name)
if force_typecast:
try:
failed_logins = int(failed_logins)
except (ValueError, TypeError):
failed_logins = 0
return failed_logins
def _set_logins_in_url(self, url, logins):
"""
Insert the login counter variable with the ``logins`` value into
``url`` and return the new URL.
"""
return self._insert_qs_variable(url, self.login_counter_name, logins)
def _insert_qs_variable(self, url, var_name, var_value):
"""
Insert the variable ``var_name`` with value ``var_value`` in the query
string of ``url`` and return the new URL.
"""
url_parts = list(urlparse(url))
query_parts = parse_qs(url_parts[4])
query_parts[var_name] = var_value
url_parts[4] = urlencode(query_parts, doseq=True)
return urlunparse(url_parts)
def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, id(self))
_______________________________________________
Repoze-dev mailing list
Repoze-dev@lists.repoze.org
http://lists.repoze.org/listinfo/repoze-dev