0001"""
0002Secure Form Tag Helpers -- For prevention of Cross-site request forgery (CSRF)
0003attacks.
0004
0005Generates form tags that include client-specific authorization tokens to be
0006verified by the destined web app.
0007
0008Authorization tokens are stored in the client's session. The web app can then
0009verify the request's submitted authorization token with the value in the
0010client's session.
0011
0012This ensures the request came from the originating page. See
0013http://en.wikipedia.org/wiki/Cross-site_request_forgery for more information.
0014
0015Pylons provides an ``authenticate_form`` decorator that does this verfication
0016on the behalf of controllers.
0017"""
0018import random
0019
0020from routes import request_config
0021
0022from form_tag import form, hidden_field
0023from prototype import form_remote_tag
0024from tags import content_tag
0025
0026token_key = '_authentication_token'
0027
0028def get_session():
0029    """Return the current session from the environ provided by routes. A Pylons
0030    supported session is assumed. A KeyError is raised if one doesn't exist."""
0031    environ = request_config().environ
0032    session_key = environ['pylons.environ_config']['session']
0033    session = environ[session_key]
0034    return session
0035
0036def authentication_token():
0037    """Return the current authentication token, creating one if one doesn't
0038    already exist."""
0039    session = get_session()
0040    if not token_key in session:
0041        try:
0042            token = str(random.getrandbits(128))
0043        except AttributeError: # Python < 2.4
0044            token = str(random.randrange(2**128))
0045        session[token_key] = token
0046        if hasattr(session, 'save'):
0047            session.save()
0048    return session[token_key]
0049
0050def secure_form(url, **args):
0051    """Create a form tag (like webhelpers.rails.form_tag.form) including a
0052    hidden authentication token field.
0053    """
0054    id = authentication_token()
0055    form_html = form(url, **args)
0056    return '%s\n%s' % (form_html,
0057                       content_tag('div', hidden_field(token_key, id),
0058                                   style='display: none;'))
0059
0060def secure_form_remote_tag(**args):
0061    """Create a form tag (like webhelpers.rails.prototype.form_remote_tag)
0062    including a hidden authentication token field.
0063    """
0064    id = authentication_token()
0065    form_html = form_remote_tag(**args)
0066    return '%s\n%s' % (form_html,
0067                       content_tag('div', hidden_field(token_key, id),
0068                                   style='display: none;'))
0069
0070__all__ = ['secure_form', 'secure_form_remote_tag']