Skip to content

Commit 3f14640

Browse files
grishagrisha
authored andcommitted
Some cookie clean-up
1 parent b890921 commit 3f14640

1 file changed

Lines changed: 130 additions & 22 deletions

File tree

lib/python/mod_python/Cookie.py

Lines changed: 130 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,82 @@
1+
# ====================================================================
2+
# The Apache Software License, Version 1.1
3+
#
4+
# Copyright (c) 2000-2003 The Apache Software Foundation. All rights
5+
# reserved.
6+
#
7+
# Redistribution and use in source and binary forms, with or without
8+
# modification, are permitted provided that the following conditions
9+
# are met:
10+
#
11+
# 1. Redistributions of source code must retain the above copyright
12+
# notice, this list of conditions and the following disclaimer.
13+
#
14+
# 2. Redistributions in binary form must reproduce the above copyright
15+
# notice, this list of conditions and the following disclaimer in
16+
# the documentation and/or other materials provided with the
17+
# distribution.
18+
#
19+
# 3. The end-user documentation included with the redistribution,
20+
# if any, must include the following acknowledgment:
21+
# "This product includes software developed by the
22+
# Apache Software Foundation (http://www.apache.org/)."
23+
# Alternately, this acknowledgment may appear in the software itself,
24+
# if and wherever such third-party acknowledgments normally appear.
25+
#
26+
# 4. The names "Apache" and "Apache Software Foundation" must
27+
# not be used to endorse or promote products derived from this
28+
# software without prior written permission. For written
29+
# permission, please contact apache@apache.org.
30+
#
31+
# 5. Products derived from this software may not be called "Apache",
32+
# "mod_python", or "modpython", nor may these terms appear in their
33+
# name, without prior written permission of the Apache Software
34+
# Foundation.
35+
#
36+
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38+
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39+
# DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40+
# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43+
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44+
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46+
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47+
# SUCH DAMAGE.
48+
# ====================================================================
49+
#
50+
# This software consists of voluntary contributions made by many
51+
# individuals on behalf of the Apache Software Foundation. For more
52+
# information on the Apache Software Foundation, please see
53+
# <http://www.apache.org/>.
54+
#
55+
# Originally developed by Gregory Trubetskoy.
56+
#
57+
# $Id: Cookie.py,v 1.2 2003/06/17 02:35:15 grisha Exp $
158

259
"""
360
4-
(C) 2003 Apache Software Foundation
61+
This module contains classes to support HTTP State Management
62+
Mechanism, also known as Cookies. The classes provide simple
63+
ways for creating, parsing and digitally signing cookies, as
64+
well as the ability to store simple Python objects in Cookies
65+
(using marshalling).
66+
67+
The behaviour of the classes is designed to be most useful
68+
within mod_python applications.
69+
70+
The current state of HTTP State Management standardization is
71+
rather unclear. It appears that the de-facto standard is the
72+
original Netscape specification, even though already two RFC's
73+
have been put out (RFC2109 (1997) and RFC2965 (2000)). The
74+
RFC's add a couple of useful features (e.g. using Max-Age instead
75+
of Expires, but my limited tests show that Max-Age is ignored
76+
by the two browsers tested (IE and Safari). As a result of this,
77+
perhaps trying to be RFC-compliant (by automatically providing
78+
Max-Age and Version) could be a waste of cookie space...
579
6-
$Id: Cookie.py,v 1.1 2003/06/14 03:03:37 grisha Exp $
780
881
Sample usage:
982
@@ -26,7 +99,7 @@
2699
File "<stdin>", line 1, in ?
27100
AttributeError: 'Cookie' object has no attribute 'eggs'
28101
29-
parsing (note the result is a dict of cookies)
102+
* parsing (note the result is a dict of cookies)
30103
31104
>>> Cookie.parse(str(c))
32105
{'spam': <Cookie: spam=eggs; version=1; expires=Sat, 14-Jun-2003 02:42:36 GMT; max_age=3>}
@@ -73,20 +146,16 @@
73146
import marshal
74147
import base64
75148

149+
from mod_python import apache
150+
76151
class CookieError(Exception):
77152
pass
78153

79154
class Cookie(object):
80155
"""
81-
This class reflects the sorry state that standardizaion of
82-
HTTP State Management is currently in. Even though RFC2109
83-
has been out since 1997, and was even upgraded by RFC2165 in
84-
2000, most browsers out there still support the buggy Netscape
85-
specification...
86-
Here we try to be RFC2109 compliant while making all necessary
87-
adjustments for those clients that only understand the NS spec.
88-
89-
156+
This class implements the basic Cookie functionality. Note that
157+
unlike the Python Standard Library Cookie class, this class represents
158+
a single cookie (not a list of Morsels).
90159
"""
91160

92161
_valid_attr = (
@@ -102,27 +171,48 @@ class Cookie(object):
102171
"_expires", "_max_age")
103172

104173
def parse(Class, str):
174+
"""
175+
Parse a Cookie or Set-Cookie header value, and return
176+
a dict of Cookies. Note: the string should NOT include the
177+
header name, only the value.
178+
"""
105179

106180
dict = _parseCookie(str, Class)
107-
108181
return dict
109182

110183
parse = classmethod(parse)
111184

185+
112186
def __init__(self, name, value, **kw):
113187

188+
"""
189+
This constructor takes at least a name and value as the
190+
arguments, as well as optionally any of allowed cookie attributes
191+
as defined in the existing cookie standards.
192+
"""
193+
114194
self.name, self.value = name, value
115195

116196
for k in kw:
117-
setattr(self, k, kw[k])
197+
setattr(self, k.lower(), kw[k])
118198

119199
if not hasattr(self, "version"):
120200
self.version = "1"
121201

202+
122203
def __str__(self):
123204

124-
# NOTE: quoting the value is up to the user!
205+
"""
206+
Provides the string representation of the Cookie suitable for
207+
sending to the browser. Note that the actual header name will
208+
not be part of the string.
125209
210+
This method makes no attempt to automatically double-quote
211+
strings that contain special characters, even though the RFC's
212+
dictate this. This is because doing so seems to confuse most
213+
browsers out there.
214+
"""
215+
126216
result = ["%s=%s" % (self.name, self.value)]
127217
for name in self._valid_attr:
128218
if hasattr(self, name):
@@ -182,6 +272,15 @@ def get_max_age(self):
182272
max_age = property(fget=get_max_age, fset=set_max_age)
183273

184274
class SignedCookie(Cookie):
275+
"""
276+
This is a variation of Cookie that provides automatic
277+
cryptographic signing of cookies and verification. It uses
278+
the HMAC support in the Python standard library. This ensures
279+
that the cookie has not been tamprered with on the client side.
280+
281+
Note that this class does not encrypt cookie data, thus it
282+
is still plainly visible as part of the cookie.
283+
"""
185284

186285
def parse(Class, secret, str):
187286

@@ -240,13 +339,20 @@ def unsign(self, secret):
240339

241340
class MarshalCookie(SignedCookie):
242341

243-
# It is wrong to assume that unmarshalling data is safe, though
244-
# here is a link to a sugesstion that it is at least safer than unpickling
245-
# http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=7xn0hcugmy.fsf%40ruckus.brouhaha.com
246-
#
247-
# This class unmarshals only signed cookies, so we're pretty safe
248-
#
249-
342+
"""
343+
This is a variation of SignedCookie that can store more than
344+
just strings. It will automatically marshal the cookie value,
345+
therefore any marshallable object can be used as value.
346+
347+
The standard library Cookie module provides the ability to pickle
348+
data, which is a major security problem. It is believed that unmarshalling
349+
(as opposed to unpickling) is safe, yet we still err on the side of caution
350+
which is why this class is a subclass of SignedCooke making sure what
351+
we are about to unmarshal passes the digital signature test.
352+
353+
Here is a link to a sugesstion that marshalling is safer than unpickling
354+
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=7xn0hcugmy.fsf%40ruckus.brouhaha.com
355+
"""
250356
__slots__ = SignedCookie.__slots__
251357

252358
expires = property(fget=Cookie.get_expires, fset=Cookie.set_expires)
@@ -303,6 +409,8 @@ def unmarshal(self, secret):
303409

304410
def _parseCookie(str, Class):
305411

412+
# XXX problem is we should allow duplicate
413+
# strings
306414
result = {}
307415

308416
# max-age is a problem because of the '-'

0 commit comments

Comments
 (0)