Compare commits

...

15 Commits

Author SHA1 Message Date
James Bennett f4afec9893 Bump legacy support branch to 0.95.4 for impending security release
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@8879 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-09-02 21:18:44 +00:00
Jacob Kaplan-Moss aee48854a1 Security fix. Announcement forthcoming.
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@8877 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-09-02 21:10:00 +00:00
James Bennett 156e3653bd Version bump 0.95.2 -> 0.95.3 for forthcoming security bugfix release
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@7531 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-05-14 04:16:04 +00:00
James Bennett 50ce7fb57d Backport [7521] to 0.95-bugfixes per security policy; announcement and security bugfix release will be forthcoming.
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@7528 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-05-14 04:07:15 +00:00
Jacob Kaplan-Moss 412ed22502 i18n security fix. Details will be posted shortly to the Django mailing lists and the official weblog.
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@6606 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-10-26 19:52:16 +00:00
James Bennett 3fc271871f Backport generic-relations data loss fix from [4428]. Thanks to Malcolm for pointing out my stupid error the first time I tried this.
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4476 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-02-10 05:48:20 +00:00
James Bennett b0ea21299b Missed a 0.95 -> 0.95.1 change in there
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4386 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-01-21 09:37:40 +00:00
James Bennett 4fa6c90110 0.95-bugfixes: Update date in release notes
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4384 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-01-21 09:15:35 +00:00
James Bennett 3080f4a36f 0.95-bugfixes: Updated release notes to indicate new version and list changes since 0.95
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4383 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-01-21 08:23:16 +00:00
James Bennett 027afbcb00 0.95-bugfixes: Apply flup traceback suppression from [4170]
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4363 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-01-20 03:56:19 +00:00
James Bennett 154b64dcf8 0.95-bugfixes: Update VERSION for impending 0.95.1 release
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4362 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-01-20 02:09:13 +00:00
James Bennett e89f0a6558 0.95-bugfixes: Apply fix to LazyUser from [3754]
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4361 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-01-20 02:05:02 +00:00
James Bennett a132d411c6 0.95-bugfixes: Apply security fix from [3592] and Windows compatibility for same from [3672]
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4360 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-01-20 02:02:07 +00:00
James Bennett 69fb4babc3 Created 0.95-bugfixes branch to apply security fixes for the 0.95 release
git-svn-id: http://code.djangoproject.com/svn/django/branches/0.95-bugfixes@4359 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-01-20 01:45:08 +00:00
Adrian Holovaty 0b353abe67 Tagged version 0.95 (changeset [3489])
git-svn-id: http://code.djangoproject.com/svn/django/tags/releases/0.95@3491 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2006-07-30 00:36:48 +00:00
11 changed files with 170 additions and 109 deletions

View File

@ -1 +1 @@
VERSION = (0, 95, None)
VERSION = (0, 95.4, None)

View File

@ -19,7 +19,17 @@ def compile_messages():
if f.endswith('.po'):
sys.stderr.write('processing file %s in %s\n' % (f, dirpath))
pf = os.path.splitext(os.path.join(dirpath, f))[0]
cmd = 'msgfmt -o "%s.mo" "%s.po"' % (pf, pf)
# Store the names of the .mo and .po files in an environment
# variable, rather than doing a string replacement into the
# command, so that we can take advantage of shell quoting, to
# quote any malicious characters/escaping.
# See http://cyberelk.net/tim/articles/cmdline/ar01s02.html
os.environ['djangocompilemo'] = pf + '.mo'
os.environ['djangocompilepo'] = pf + '.po'
if sys.platform == 'win32': # Different shell-variable syntax
cmd = 'msgfmt -o "%djangocompilemo%" "%djangocompilepo%"'
else:
cmd = 'msgfmt -o "$djangocompilemo" "$djangocompilepo"'
os.system(cmd)
if __name__ == "__main__":

View File

@ -19,7 +19,6 @@
<div class="form-row">
<label for="id_password">{% trans 'Password:' %}</label> <input type="password" name="password" id="id_password" />
<input type="hidden" name="this_is_the_login_form" value="1" />
<input type="hidden" name="post_data" value="{{ post_data }}" /> {% comment %}<span class="help">{% trans 'Have you <a href="/password_reset/">forgotten your password</a>?' %}</span>{% endcomment %}
</div>
<div class="submit-row">
<label>&nbsp;</label><input type="submit" value="{% trans 'Log in' %}" />

View File

@ -3,43 +3,21 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login
from django.shortcuts import render_to_response
from django.utils.html import escape
from django.utils.translation import gettext_lazy
import base64, datetime, md5
import cPickle as pickle
import base64, datetime
ERROR_MESSAGE = gettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
LOGIN_FORM_KEY = 'this_is_the_login_form'
def _display_login_form(request, error_message=''):
request.session.set_test_cookie()
if request.POST and request.POST.has_key('post_data'):
# User has failed login BUT has previously saved post data.
post_data = request.POST['post_data']
elif request.POST:
# User's session must have expired; save their post data.
post_data = _encode_post_data(request.POST)
else:
post_data = _encode_post_data({})
return render_to_response('admin/login.html', {
'title': _('Log in'),
'app_path': request.path,
'post_data': post_data,
'app_path': escape(request.path),
'error_message': error_message
}, context_instance=template.RequestContext(request))
def _encode_post_data(post_data):
pickled = pickle.dumps(post_data)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5)
def _decode_post_data(encoded_data):
encoded_data = base64.decodestring(encoded_data)
pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
from django.core.exceptions import SuspiciousOperation
raise SuspiciousOperation, "User may have tampered with session cookie."
return pickle.loads(pickled)
def staff_member_required(view_func):
"""
Decorator for views that checks that the user is logged in and is a staff
@ -48,10 +26,6 @@ def staff_member_required(view_func):
def _checklogin(request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_staff:
# The user is valid. Continue to the admin page.
if request.POST.has_key('post_data'):
# User must have re-authenticated through a different window
# or tab.
request.POST = _decode_post_data(request.POST['post_data'])
return view_func(request, *args, **kwargs)
assert hasattr(request, 'session'), "The Django admin requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
@ -59,7 +33,7 @@ def staff_member_required(view_func):
# If this isn't already the login page, display it.
if not request.POST.has_key(LOGIN_FORM_KEY):
if request.POST:
message = _("Please log in again, because your session has expired. Don't worry: Your submission has been saved.")
message = _("Please log in again, because your session has expired.")
else:
message = ""
return _display_login_form(request, message)
@ -92,16 +66,7 @@ def staff_member_required(view_func):
# TODO: set last_login with an event.
user.last_login = datetime.datetime.now()
user.save()
if request.POST.has_key('post_data'):
post_data = _decode_post_data(request.POST['post_data'])
if post_data and not post_data.has_key(LOGIN_FORM_KEY):
# overwrite request.POST with the saved post_data, and continue
request.POST = post_data
request.user = user
return view_func(request, *args, **kwargs)
else:
request.session.delete_test_cookie()
return http.HttpResponseRedirect(request.path)
return http.HttpResponseRedirect(request.path)
else:
return _display_login_form(request, ERROR_MESSAGE)

View File

@ -1,12 +1,9 @@
class LazyUser(object):
def __init__(self):
self._user = None
def __get__(self, request, obj_type=None):
if self._user is None:
if not hasattr(request, '_cached_user'):
from django.contrib.auth import get_user
self._user = get_user(request)
return self._user
request._cached_user = get_user(request)
return request._cached_user
class AuthenticationMiddleware(object):
def process_request(self, request):

View File

@ -108,7 +108,9 @@ def runfastcgi(argset):
wsgi_opts = {}
else:
return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")
wsgi_opts['debug'] = False # Turn off flup tracebacks
# Prep up and go
from django.core.handlers.wsgi import WSGIHandler

View File

@ -1,5 +1,6 @@
from django.db import backend, connection, transaction
from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models.fields.generic import GenericRelation
from django.db.models import signals
from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict
@ -925,18 +926,26 @@ def delete_objects(seen_objs):
pk_list = [pk for pk,instance in seen_objs[cls]]
for related in cls._meta.get_all_related_many_to_many_objects():
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(related.field.m2m_db_table()),
qn(related.field.m2m_reverse_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
if not isinstance(related.field, GenericRelation):
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(related.field.m2m_db_table()),
qn(related.field.m2m_reverse_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
for f in cls._meta.many_to_many:
if isinstance(f, GenericRelation):
from django.contrib.contenttypes.models import ContentType
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
args_extra = [ContentType.objects.get_for_model(cls).id]
else:
query_extra = ''
args_extra = []
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])) + query_extra,
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_objs:
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):

View File

@ -1,6 +1,9 @@
"Translation helper functions"
import os, re, sys
import locale
import os
import re
import sys
import gettext as gettext_module
from cStringIO import StringIO
from django.utils.functional import lazy
@ -25,15 +28,25 @@ _active = {}
# The default translation is based on the settings file.
_default = None
# This is a cache for accept-header to translation object mappings to prevent
# the accept parser to run multiple times for one user.
# This is a cache for normalised accept-header languages to prevent multiple
# file lookups when checking the same locale on repeated requests.
_accepted = {}
def to_locale(language):
# Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9.
accept_language_re = re.compile(r'''
([A-Za-z]{1,8}(?:-[A-Za-z]{1,8})*|\*) # "en", "en-au", "x-y-z", "*"
(?:;q=(0(?:\.\d{,3})?|1(?:.0{,3})?))? # Optional "q=1.00", "q=0.8"
(?:\s*,\s*|$) # Multiple accepts per header.
''', re.VERBOSE)
def to_locale(language, to_lower=False):
"Turns a language name (en-us) into a locale name (en_US)."
p = language.find('-')
if p >= 0:
return language[:p].lower()+'_'+language[p+1:].upper()
if to_lower:
return language[:p].lower()+'_'+language[p+1:].lower()
else:
return language[:p].lower()+'_'+language[p+1:].upper()
else:
return language.lower()
@ -309,46 +322,40 @@ def get_language_from_request(request):
if lang_code in supported and lang_code is not None and check_for_language(lang_code):
return lang_code
lang_code = request.COOKIES.get('django_language', None)
if lang_code in supported and lang_code is not None and check_for_language(lang_code):
lang_code = request.COOKIES.get('django_language')
if lang_code and lang_code in supported and check_for_language(lang_code):
return lang_code
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', None)
if accept is not None:
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
for lang, unused in parse_accept_lang_header(accept):
if lang == '*':
break
t = _accepted.get(accept, None)
if t is not None:
return t
# We have a very restricted form for our language files (no encoding
# specifier, since they all must be UTF-8 and only one possible
# language each time. So we avoid the overhead of gettext.find() and
# look up the MO file manually.
def _parsed(el):
p = el.find(';q=')
if p >= 0:
lang = el[:p].strip()
order = int(float(el[p+3:].strip())*100)
else:
lang = el
order = 100
p = lang.find('-')
if p >= 0:
mainlang = lang[:p]
else:
mainlang = lang
return (lang, mainlang, order)
normalized = locale.locale_alias.get(to_locale(lang, True))
if not normalized:
continue
langs = [_parsed(el) for el in accept.split(',')]
langs.sort(lambda a,b: -1*cmp(a[2], b[2]))
# Remove the default encoding from locale_alias
normalized = normalized.split('.')[0]
for lang, mainlang, order in langs:
if lang in supported or mainlang in supported:
langfile = gettext_module.find('django', globalpath, [to_locale(lang)])
if langfile:
# reconstruct the actual language from the language
# filename, because otherwise we might incorrectly
# report de_DE if we only have de available, but
# did find de_DE because of language normalization
lang = langfile[len(globalpath):].split(os.path.sep)[1]
_accepted[accept] = lang
return lang
if normalized in _accepted:
# We've seen this locale before and have an MO file for it, so no
# need to check again.
return _accepted[normalized]
for lang in (normalized, normalized.split('_')[0]):
if lang not in supported:
continue
langfile = os.path.join(globalpath, lang, 'LC_MESSAGES',
'django.mo')
if os.path.exists(langfile):
_accepted[normalized] = lang
return lang
return settings.LANGUAGE_CODE
@ -494,3 +501,24 @@ def string_concat(*strings):
return ''.join([str(el) for el in strings])
string_concat = lazy(string_concat, str)
def parse_accept_lang_header(lang_string):
"""
Parses the lang_string, which is the body of an HTTP Accept-Language
header, and returns a list of (lang, q-value), ordered by 'q' values.
Any format errors in lang_string results in an empty list being returned.
"""
result = []
pieces = accept_language_re.split(lang_string)
if pieces[-1]:
return []
for i in range(0, len(pieces) - 1, 3):
first, lang, priority = pieces[i : i + 3]
if first:
return []
priority = priority and float(priority) or 1.0
result.append((lang, priority))
result.sort(lambda x, y: -cmp(x[1], y[1]))
return result

View File

@ -1,9 +1,8 @@
=================================
Django version 0.95 release notes
=================================
===================================
Django version 0.95.2 release notes
===================================
Welcome to the Django 0.95 release.
Welcome to the Django 0.95.2 release.
This represents a significant advance in Django development since the 0.91
release in January 2006. The details of every change in this release would be
@ -91,6 +90,31 @@ easy checklist_ for reference when undertaking the porting operation.
.. _Removing The Magic: http://code.djangoproject.com/wiki/RemovingTheMagic
.. _checklist: http://code.djangoproject.com/wiki/MagicRemovalCheatSheet1
Changes since the 0.95 release
==============================
This release contains fixes for several bugs discovered after the
initial release of Django 0.95; these include:
* A patch for a small security vulnerability in the script
Django's internationalization system uses to compile translation
files.
* A fix for a bug in Django's authentication middleware which
could cause apparent "caching" of a logged-in user.
* A patch which disables debugging mode in the flup FastCGI
package Django uses to launch its FastCGI server, which prevents
tracebacks from bubbling up during production use.
* A security fix to the i18n framework which could allow an
attacker to send extremely large strings in the Accept-Language
header and cause a denial of service by filling available memory.
Because these problems weren't discovered and fixed until after the
0.95 release, it's recommended that you use this release rather than
the original 0.95.
Problem reports and getting help
================================
@ -118,5 +142,5 @@ available at any hour of the day -- to help, or just to chat.
Thanks for using Django!
The Django Team
July 2006
January 2007

View File

@ -5,7 +5,7 @@ from setuptools import setup, find_packages
setup(
name = "Django",
version = "0.95",
version = "0.95.4",
url = 'http://www.djangoproject.com/',
author = 'Lawrence Journal-World',
author_email = 'holovaty@gmail.com',

View File

@ -65,14 +65,14 @@ API_TESTS = """
# Objects with declared GenericRelations can be tagged directly -- the API
# mimics the many-to-many API.
>>> lion.tags.create(tag="yellow")
<TaggedItem: yellow>
>>> lion.tags.create(tag="hairy")
<TaggedItem: hairy>
>>> bacon.tags.create(tag="fatty")
<TaggedItem: fatty>
>>> bacon.tags.create(tag="salty")
<TaggedItem: salty>
>>> lion.tags.create(tag="yellow")
<TaggedItem: yellow>
>>> lion.tags.create(tag="hairy")
<TaggedItem: hairy>
>>> lion.tags.all()
[<TaggedItem: hairy>, <TaggedItem: yellow>]
@ -105,4 +105,31 @@ API_TESTS = """
[<TaggedItem: shiny>]
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
[<TaggedItem: clearish>]
# If you delete an object with an explicit Generic relation, the related
# objects are deleted when the source object is deleted.
# Original list of tags:
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)]
>>> lion.delete()
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
# If Generic Relation is not explicitly defined, any related objects
# remain after deletion of the source object.
>>> quartz.delete()
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
# If you delete a tag, the objects using the tag are unaffected
# (other than losing a tag)
>>> tag = TaggedItem.objects.get(id=1)
>>> tag.delete()
>>> bacon.tags.all()
[<TaggedItem: salty>]
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
[('clearish', <ContentType: mineral>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
"""