Commits

Anonymous committed e9e24b4

Transfert from svn to mercurial

  • Participants

Comments (0)

Files changed (191)

File .DS_Store

Binary file added.

File __init__.py

Empty file added.

File django_authopenid/.DS_Store

Binary file added.

File django_authopenid/.svn/all-wcprops

+K 25
+svn:wc:ra_dav:version-url
+V 39
+/svn/!svn/ver/2/trunk/django_authopenid
+END
+util.py
+K 25
+svn:wc:ra_dav:version-url
+V 47
+/svn/!svn/ver/2/trunk/django_authopenid/util.py
+END
+middleware.py
+K 25
+svn:wc:ra_dav:version-url
+V 53
+/svn/!svn/ver/2/trunk/django_authopenid/middleware.py
+END
+views.py
+K 25
+svn:wc:ra_dav:version-url
+V 48
+/svn/!svn/ver/2/trunk/django_authopenid/views.py
+END
+__init__.py
+K 25
+svn:wc:ra_dav:version-url
+V 51
+/svn/!svn/ver/2/trunk/django_authopenid/__init__.py
+END
+mimeparse.py
+K 25
+svn:wc:ra_dav:version-url
+V 52
+/svn/!svn/ver/2/trunk/django_authopenid/mimeparse.py
+END
+models.py
+K 25
+svn:wc:ra_dav:version-url
+V 49
+/svn/!svn/ver/2/trunk/django_authopenid/models.py
+END
+urls.py
+K 25
+svn:wc:ra_dav:version-url
+V 47
+/svn/!svn/ver/2/trunk/django_authopenid/urls.py
+END
+forms.py
+K 25
+svn:wc:ra_dav:version-url
+V 48
+/svn/!svn/ver/2/trunk/django_authopenid/forms.py
+END

File django_authopenid/.svn/entries

+8
+
+dir
+2
+https://lifedo-web.googlecode.com/svn/trunk/django_authopenid
+https://lifedo-web.googlecode.com/svn
+
+
+
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+
+svn:special svn:externals svn:needs-lock
+
+
+
+
+
+
+
+
+
+
+
+b466afa6-975e-11dd-bf2f-7da7babd390c
+
+util.py
+file
+
+
+
+
+2008-10-11T06:37:58.000000Z
+18a63be4f8d6f8d2efa5b924eddf1910
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+middleware.py
+file
+
+
+
+
+2008-10-11T06:37:58.000000Z
+a9dada44caf6d224abca532033cf4c88
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+views.py
+file
+
+
+
+
+2008-10-11T06:37:58.000000Z
+6b7d9e0cdd589316e14cf97f961efe89
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+__init__.py
+file
+
+
+
+
+2008-10-11T06:37:58.000000Z
+3bbb5a8aef5f52387b8e2b2a62f74b47
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+mimeparse.py
+file
+
+
+
+
+2008-10-11T06:37:58.000000Z
+1818a1ca83497e5d862bf244794520c2
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+models.py
+file
+
+
+
+
+2008-10-11T06:37:58.000000Z
+b1aa61ba0963b1008e9522c20effaaae
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+urls.py
+file
+
+
+
+
+2008-10-11T06:37:58.000000Z
+d89e82caa1337e97a4d471a192aa2789
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+forms.py
+file
+
+
+
+
+2008-10-11T06:37:58.000000Z
+916b73ee5479118abcdfda7cb9282e12
+2008-10-11T06:36:34.244751Z
+2
+kizlum
+
+templates
+dir
+

File django_authopenid/.svn/format

+8

File django_authopenid/.svn/text-base/__init__.py.svn-base

+# -*- coding: utf-8 -*-
+# Copyright (c) 2007, 2008, Benoît Chesneau
+# 
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 
+#      * Redistributions of source code must retain the above copyright
+#      * notice, this list of conditions and the following disclaimer.
+#      * Redistributions in binary form must reproduce the above copyright
+#      * notice, this list of conditions and the following disclaimer in the
+#      * documentation and/or other materials provided with the
+#      * distribution.  Neither the name of the <ORGANIZATION> nor the names
+#      * of its contributors may be used to endorse or promote products
+#      * derived from this software without specific prior written
+#      * permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+Django authentification application to *with openid using django auth contrib/.
+
+This application allow a user to connect to you website with :
+ * legacy account : username/password
+ * openid url
+"""
+
+__version__ = "0.9.4"

File django_authopenid/.svn/text-base/forms.py.svn-base

+# -*- coding: utf-8 -*-
+# Copyright (c) 2007, 2008, Benoît Chesneau
+# 
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 
+#      * Redistributions of source code must retain the above copyright
+#      * notice, this list of conditions and the following disclaimer.
+#      * Redistributions in binary form must reproduce the above copyright
+#      * notice, this list of conditions and the following disclaimer in the
+#      * documentation and/or other materials provided with the
+#      * distribution.  Neither the name of the <ORGANIZATION> nor the names
+#      * of its contributors may be used to endorse or promote products
+#      * derived from this software without specific prior written
+#      * permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+from django import forms
+from django.contrib.auth.models import User
+from django.contrib.auth import authenticate
+from django.utils.translation import ugettext as _
+from django.conf import settings
+
+import re
+
+
+# needed for some linux distributions like debian
+try:
+    from openid.yadis import xri
+except ImportError:
+    from yadis import xri
+
+__all__ = ['OpenidSigninForm', 'OpenidAuthForm', 'OpenidVerifyForm',
+        'OpenidRegisterForm', 'RegistrationForm', 'ChangepwForm',
+        'ChangeemailForm', 'EmailPasswordForm', 'DeleteForm',
+        'ChangeOpenidForm', 'ChangeEmailForm', 'ChangepwForm']
+
+class OpenidSigninForm(forms.Form):
+    """ signin form """
+    openid_url = forms.CharField(max_length=255, 
+            widget=forms.widgets.TextInput(attrs={'class': 'required openid'}))
+    next = forms.CharField(max_length=255, widget=forms.HiddenInput(), 
+            required=False)
+
+    def clean_openid_url(self):
+        """ test if openid is accepted """
+        if 'openid_url' in self.cleaned_data:
+            openid_url = self.cleaned_data['openid_url']
+            if xri.identifierScheme(openid_url) == 'XRI' and getattr(
+                settings, 'OPENID_DISALLOW_INAMES', False
+                ):
+                raise forms.ValidationError(_('i-names are not supported'))
+            return self.cleaned_data['openid_url']
+
+    def clean_next(self):
+        """ validate next """
+        if 'next' in self.cleaned_data and self.cleaned_data['next'] != "":
+            next_url_re = re.compile('^/[-\w/]*$')
+            if not next_url_re.match(self.cleaned_data['next']):
+                raise forms.ValidationError(_('next url "%s" is invalid' % 
+                    self.cleaned_data['next']))
+            return self.cleaned_data['next']
+
+
+attrs_dict = { 'class': 'required login' }
+username_re = re.compile(r'^\w+$')
+
+class OpenidAuthForm(forms.Form):
+    """ legacy account signin form """
+    next = forms.CharField(max_length=255, widget=forms.HiddenInput(), 
+            required=False)
+    username = forms.CharField(max_length=30,  
+            widget=forms.widgets.TextInput(attrs=attrs_dict))
+    password = forms.CharField(max_length=128, 
+            widget=forms.widgets.PasswordInput(attrs=attrs_dict))
+       
+    def __init__(self, data=None, files=None, auto_id='id_%s',
+            prefix=None, initial=None): 
+        super(OpenidAuthForm, self).__init__(data, files, auto_id,
+                prefix, initial)
+        self.user_cache = None
+            
+    def clean_username(self):
+        """ validate username and test if it exists."""
+        if 'username' in self.cleaned_data and \
+                'openid_url' not in self.cleaned_data:
+            if not username_re.search(self.cleaned_data['username']):
+                raise forms.ValidationError(_("Usernames can only contain \
+                    letters, numbers and underscores"))
+            try:
+                user = User.objects.get(
+                        username__exact = self.cleaned_data['username']
+                )
+            except User.DoesNotExist:
+                raise forms.ValidationError(_("This username does not exist \
+                    in our database. Please choose another."))
+            return self.cleaned_data['username']
+
+    def clean_password(self):
+        """" test if password is valid for this username """
+        if 'username' in self.cleaned_data and \
+                'password' in self.cleaned_data:
+            self.user_cache =  authenticate(
+                    username=self.cleaned_data['username'], 
+                    password=self.cleaned_data['password']
+            )
+            if self.user_cache is None:
+                raise forms.ValidationError(_("Please enter a valid \
+                    username and password. Note that both fields are \
+                    case-sensitive."))
+            elif self.user_cache.is_active == False:
+                raise forms.ValidationError(_("This account is inactive."))
+            return self.cleaned_data['password']
+
+    def clean_next(self):
+        """ validate next url """
+        if 'next' in self.cleaned_data and \
+                self.cleaned_data['next'] != "":
+            next_url_re = re.compile('^/[-\w/]*$')
+            if not next_url_re.match(self.cleaned_data['next']):
+                raise forms.ValidationError(
+                        _('next url "%s" is invalid' % 
+                            self.cleaned_data['next'])
+                )
+            return self.cleaned_data['next']
+            
+    def get_user(self):
+        """ get authenticated user """
+        return self.user_cache
+            
+
+class OpenidRegisterForm(forms.Form):
+    """ openid signin form """
+    next = forms.CharField(max_length=255, widget=forms.HiddenInput(), 
+            required=False)
+    username = forms.CharField(max_length=30, 
+            widget=forms.widgets.TextInput(attrs=attrs_dict))
+    email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, 
+        maxlength=200)), label=u'Email address')
+    
+    def clean_username(self):
+        """ test if username is valid and exist in database """
+        if 'username' in self.cleaned_data:
+            if not username_re.search(self.cleaned_data['username']):
+                raise forms.ValidationError(_("Usernames can only contain \
+                    letters, numbers and underscores"))
+            try:
+                user = User.objects.get(
+                        username__exact = self.cleaned_data['username']
+                        )
+            except User.DoesNotExist:
+                return self.cleaned_data['username']
+            raise forms.ValidationError(_("This username is already \
+                taken. Please choose another."))
+            
+    def clean_email(self):
+        """For security reason one unique email in database"""
+        if 'email' in self.cleaned_data:
+            try:
+                user = User.objects.get(email = self.cleaned_data['email'])
+            except User.DoesNotExist:
+                return self.cleaned_data['email']
+            raise forms.ValidationError(_("This email is already \
+                registered in our database. Please choose another."))
+ 
+    
+class OpenidVerifyForm(forms.Form):
+    """ openid verify form (associate an openid with an account) """
+    next = forms.CharField(max_length=255, widget = forms.HiddenInput(), 
+            required=False)
+    username = forms.CharField(max_length=30, 
+            widget=forms.widgets.TextInput(attrs=attrs_dict))
+    password = forms.CharField(max_length=128, 
+            widget=forms.widgets.PasswordInput(attrs=attrs_dict))
+    
+    def __init__(self, data=None, files=None, auto_id='id_%s',
+            prefix=None, initial=None): 
+        super(OpenidVerifyForm, self).__init__(data, files, auto_id,
+                prefix, initial)
+        self.user_cache = None
+
+    def clean_username(self):
+        """ validate username """
+        if 'username' in self.cleaned_data:
+            if not username_re.search(self.cleaned_data['username']):
+                raise forms.ValidationError(_("Usernames can only contain \
+                    letters, numbers and underscores"))
+            try:
+                user = User.objects.get(
+                        username__exact = self.cleaned_data['username']
+                )
+            except User.DoesNotExist:
+                raise forms.ValidationError(_("This username don't exist. \
+                        Please choose another."))
+            return self.cleaned_data['username']
+            
+    def clean_password(self):
+        """ test if password is valid for this user """
+        if 'username' in self.cleaned_data and \
+                'password' in self.cleaned_data:
+            self.user_cache =  authenticate(
+                    username = self.cleaned_data['username'], 
+                    password = self.cleaned_data['password']
+            )
+            if self.user_cache is None:
+                raise forms.ValidationError(_("Please enter a valid \
+                    username and password. Note that both fields are \
+                    case-sensitive."))
+            elif self.user_cache.is_active == False:
+                raise forms.ValidationError(_("This account is inactive."))
+            return self.cleaned_data['password']
+            
+    def get_user(self):
+        """ get authenticated user """
+        return self.user_cache
+
+
+attrs_dict = { 'class': 'required' }
+username_re = re.compile(r'^\w+$')
+
+class RegistrationForm(forms.Form):
+    """ legacy registration form """
+
+    next = forms.CharField(max_length=255, widget=forms.HiddenInput(), 
+            required=False)
+    username = forms.CharField(max_length=30,
+            widget=forms.TextInput(attrs=attrs_dict),
+            label=u'Username')
+    email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
+            maxlength=200)), label=u'Email address')
+    password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict),
+            label=u'Password')
+    password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict),
+            label=u'Password (again, to catch typos)')
+
+    def clean_username(self):
+        """
+        Validates that the username is alphanumeric and is not already
+        in use.
+        
+        """
+        if 'username' in self.cleaned_data:
+            if not username_re.search(self.cleaned_data['username']):
+                raise forms.ValidationError(u'Usernames can only contain \
+                        letters, numbers and underscores')
+            try:
+                user = User.objects.get(
+                        username__exact = self.cleaned_data['username']
+                )
+
+            except User.DoesNotExist:
+                return self.cleaned_data['username']
+            raise forms.ValidationError(u'This username is already taken. \
+                    Please choose another.')
+
+    def clean_email(self):
+        """ validate if email exist in database
+        :return: raise error if it exist """
+        if 'email' in self.cleaned_data:
+            try:
+                user = User.objects.get(email = self.cleaned_data['email'])
+            except User.DoesNotExist:
+                return self.cleaned_data['email']
+            raise forms.ValidationError(u'This email is already registered \
+                    in our database. Please choose another.')
+        return self.cleaned_data['email']
+    
+    def clean_password2(self):
+        """
+        Validates that the two password inputs match.
+        
+        """
+        if 'password1' in self.cleaned_data and \
+                'password2' in self.cleaned_data and \
+                self.cleaned_data['password1'] == \
+                self.cleaned_data['password2']:
+            return self.cleaned_data['password2']
+        raise forms.ValidationError(u'You must type the same password each \
+                time')
+
+
+class ChangepwForm(forms.Form):
+    """ change password form """
+    oldpw = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict))
+    password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict))
+    password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict))
+
+    def __init__(self, data=None, user=None, *args, **kwargs):
+        if user is None:
+            raise TypeError("Keyword argument 'user' must be supplied")
+        super(ChangepwForm, self).__init__(data, *args, **kwargs)
+        self.user = user
+
+    def clean_oldpw(self):
+        """ test old password """
+        if not self.user.check_password(self.cleaned_data['oldpw']):
+            raise forms.ValidationError(_("Old password is incorrect. \
+                    Please enter the correct password."))
+        return self.cleaned_data['oldpw']
+    
+    def clean_password2(self):
+        """
+        Validates that the two password inputs match.
+        """
+        if 'password1' in self.cleaned_data and \
+                'password2' in self.cleaned_data and \
+           self.cleaned_data['password1'] == self.cleaned_data['password2']:
+            return self.cleaned_data['password2']
+        raise forms.ValidationError(_("new passwords do not match"))
+        
+        
+class ChangeemailForm(forms.Form):
+    """ change email form """
+    email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict, 
+        maxlength=200)), label=u'Email address')
+    password = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict))
+
+    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, \
+            initial=None, user=None):
+        if user is None:
+            raise TypeError("Keyword argument 'user' must be supplied")
+        super(ChangeemailForm, self).__init__(data, files, auto_id, 
+                prefix, initial)
+        self.test_openid = False
+        self.user = user
+
+    def clean_password(self):
+        """ check if we have to test a legacy account or not """
+        if 'password' in self.cleaned_data:
+            if not self.user.check_password(self.cleaned_data['password']):
+                self.test_openid = True
+        return self.cleaned_data['password']
+                
+class ChangeopenidForm(forms.Form):
+    """ change openid form """
+    openid_url = forms.CharField(max_length=255,
+            widget=forms.TextInput(attrs={'class': "required" }))
+
+    def __init__(self, data=None, user=None, *args, **kwargs):
+        if user is None:
+            raise TypeError("Keyword argument 'user' must be supplied")
+        super(ChangeopenidForm, self).__init__(data, *args, **kwargs)
+        self.user = user
+
+class DeleteForm(forms.Form):
+    """ confirm form to delete an account """
+    confirm = forms.CharField(widget=forms.CheckboxInput(attrs=attrs_dict))
+    password = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict))
+
+    def __init__(self, data=None, files=None, auto_id='id_%s',
+            prefix=None, initial=None, user=None):
+        super(DeleteForm, self).__init__(data, files, auto_id, prefix, initial)
+        self.test_openid = False
+        self.user = user
+
+    def clean_password(self):
+        """ check if we have to test a legacy account or not """
+        if 'password' in self.cleaned_data:
+            if not self.user.check_password(self.cleaned_data['password']):
+                self.test_openid = True
+        return self.cleaned_data['password']
+
+
+class EmailPasswordForm(forms.Form):
+    """ send new password form """
+    username = forms.CharField(max_length=30,
+            widget=forms.TextInput(attrs={'class': "required" }))
+
+    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, 
+            initial=None):
+        super(EmailPasswordForm, self).__init__(data, files, auto_id, 
+                prefix, initial)
+        self.user_cache = None
+
+
+    def clean_username(self):
+        """ get user for this username """
+        if 'username' in self.cleaned_data:
+            try:
+                self.user_cache = User.objects.get(
+                        username = self.cleaned_data['username'])
+            except:
+                raise forms.ValidationError(_("Incorrect username."))
+        return self.cleaned_data['username']

File django_authopenid/.svn/text-base/middleware.py.svn-base

+# -*- coding: utf-8 -*-
+from django_authopenid import mimeparse
+from django.http import HttpResponseRedirect
+from django.core.urlresolvers import reverse
+
+__all__ = ["OpenIDMiddleware"]
+
+class OpenIDMiddleware(object):
+    """
+    Populate request.openid. This comes either from cookie or from
+    session, depending on the presence of OPENID_USE_SESSIONS.
+    """
+    def process_request(self, request):
+        request.openid = request.session.get('openid', None)
+    
+    def process_response(self, request, response):
+        if response.status_code != 200 or len(response.content) < 200:
+            return response
+        path = request.get_full_path()
+        if path == "/" and request.META.has_key('HTTP_ACCEPT') and \
+                mimeparse.best_match(['text/html', 'application/xrds+xml'], 
+                    request.META['HTTP_ACCEPT']) == 'application/xrds+xml':
+            return HttpResponseRedirect(reverse('yadis_xrdf'))
+        return response

File django_authopenid/.svn/text-base/mimeparse.py.svn-base

+"""MIME-Type Parser
+
+This module provides basic functions for handling mime-types. It can handle
+matching mime-types against a list of media-ranges. See section 14.1 of 
+the HTTP specification [RFC 2616] for a complete explaination.
+
+   http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
+
+Contents:
+    - parse_mime_type():   Parses a mime-type into it's component parts.
+    - parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q' quality parameter.
+    - quality():           Determines the quality ('q') of a mime-type when compared against a list of media-ranges.
+    - quality_parsed():    Just like quality() except the second parameter must be pre-parsed.
+    - best_match():        Choose the mime-type with the highest quality ('q') from a list of candidates. 
+"""
+
+__version__ = "0.1.1"
+__author__ = 'Joe Gregorio'
+__email__ = "joe@bitworking.org"
+__credits__ = ""
+
+def parse_mime_type(mime_type):
+    """Carves up a mime_type and returns a tuple of the
+       (type, subtype, params) where 'params' is a dictionary
+       of all the parameters for the media range.
+       For example, the media range 'application/xhtml;q=0.5' would
+       get parsed into:
+
+       ('application', 'xhtml', {'q', '0.5'})
+       """
+    parts = mime_type.split(";")
+    params = dict([tuple([s.strip() for s in param.split("=")])\
+            for param in parts[1:] ])
+    (type, subtype) = parts[0].split("/")
+    return (type.strip(), subtype.strip(), params)
+
+def parse_media_range(range):
+    """Carves up a media range and returns a tuple of the
+       (type, subtype, params) where 'params' is a dictionary
+       of all the parameters for the media range.
+       For example, the media range 'application/*;q=0.5' would
+       get parsed into:
+
+       ('application', '*', {'q', '0.5'})
+
+       In addition this function also guarantees that there 
+       is a value for 'q' in the params dictionary, filling it
+       in with a proper default if necessary.
+       """
+    (type, subtype, params) = parse_mime_type(range)
+    if not params.has_key('q') or not params['q'] or \
+            not float(params['q']) or float(params['q']) > 1\
+            or float(params['q']) < 0:
+        params['q'] = '1'
+    return (type, subtype, params)
+
+def quality_parsed(mime_type, parsed_ranges):
+    """Find the best match for a given mime_type against 
+       a list of media_ranges that have already been 
+       parsed by parse_media_range(). Returns the 
+       'q' quality parameter of the best match, 0 if no
+       match was found. This function bahaves the same as quality()
+       except that 'parsed_ranges' must be a list of
+       parsed media ranges. """
+    best_fitness = -1 
+    best_match = ""
+    best_fit_q = 0
+    (target_type, target_subtype, target_params) =\
+            parse_media_range(mime_type)
+    for (type, subtype, params) in parsed_ranges:
+        param_matches = reduce(lambda x, y: x+y, [1 for (key, value) in \
+                target_params.iteritems() if key != 'q' and \
+                params.has_key(key) and value == params[key]], 0)
+        if (type == target_type or type == '*' or target_type == '*') and \
+                (subtype == target_subtype or subtype == '*' or target_subtype == '*'):
+            fitness = (type == target_type) and 100 or 0
+            fitness += (subtype == target_subtype) and 10 or 0
+            fitness += param_matches
+            if fitness > best_fitness:
+                best_fitness = fitness
+                best_fit_q = params['q']
+            
+    return float(best_fit_q)
+    
+def quality(mime_type, ranges):
+    """Returns the quality 'q' of a mime_type when compared
+    against the media-ranges in ranges. For example:
+
+    >>> quality('text/html','text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
+    0.7
+    
+    """ 
+    parsed_ranges = [parse_media_range(r) for r in ranges.split(",")]
+    return quality_parsed(mime_type, parsed_ranges)
+
+def best_match(supported, header):
+    """Takes a list of supported mime-types and finds the best
+    match for all the media-ranges listed in header. The value of
+    header must be a string that conforms to the format of the 
+    HTTP Accept: header. The value of 'supported' is a list of
+    mime-types.
+    
+    >>> best_match(['application/xbel+xml', 'text/xml'], 'text/*;q=0.5,*/*; q=0.1')
+    'text/xml'
+    """
+    parsed_header = [parse_media_range(r) for r in header.split(",")]
+    weighted_matches = [(quality_parsed(mime_type, parsed_header), mime_type)\
+            for mime_type in supported]
+    weighted_matches.sort()
+    return weighted_matches[-1][0] and weighted_matches[-1][1] or ''
+
+if __name__ == "__main__":
+    import unittest
+
+    class TestMimeParsing(unittest.TestCase):
+
+        def test_parse_media_range(self):
+            self.assert_(('application', 'xml', {'q': '1'}) == parse_media_range('application/xml;q=1'))
+            self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml'))
+            self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml;q='))
+            self.assertEqual(('application', 'xml', {'q': '1'}), parse_media_range('application/xml ; q='))
+            self.assertEqual(('application', 'xml', {'q': '1', 'b': 'other'}), parse_media_range('application/xml ; q=1;b=other'))
+            self.assertEqual(('application', 'xml', {'q': '1', 'b': 'other'}), parse_media_range('application/xml ; q=2;b=other'))
+
+        def test_rfc_2616_example(self):
+            accept = "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5"
+            self.assertEqual(1, quality("text/html;level=1", accept))
+            self.assertEqual(0.7, quality("text/html", accept))
+            self.assertEqual(0.3, quality("text/plain", accept))
+            self.assertEqual(0.5, quality("image/jpeg", accept))
+            self.assertEqual(0.4, quality("text/html;level=2", accept))
+            self.assertEqual(0.7, quality("text/html;level=3", accept))
+
+        def test_best_match(self):
+            mime_types_supported = ['application/xbel+xml', 'application/xml']
+            # direct match
+            self.assertEqual(best_match(mime_types_supported, 'application/xbel+xml'), 'application/xbel+xml')
+            # direct match with a q parameter
+            self.assertEqual(best_match(mime_types_supported, 'application/xbel+xml; q=1'), 'application/xbel+xml')
+            # direct match of our second choice with a q parameter
+            self.assertEqual(best_match(mime_types_supported, 'application/xml; q=1'), 'application/xml')
+            # match using a subtype wildcard
+            self.assertEqual(best_match(mime_types_supported, 'application/*; q=1'), 'application/xml')
+            # match using a type wildcard
+            self.assertEqual(best_match(mime_types_supported, '*/*'), 'application/xml')
+
+            mime_types_supported = ['application/xbel+xml', 'text/xml']
+            # match using a type versus a lower weighted subtype
+            self.assertEqual(best_match(mime_types_supported, 'text/*;q=0.5,*/*; q=0.1'), 'text/xml')
+            # fail to match anything
+            self.assertEqual(best_match(mime_types_supported, 'text/html,application/atom+xml; q=0.9'), '')
+
+        def test_support_wildcards(self):
+            mime_types_supported = ['image/*', 'application/xml']
+            # match using a type wildcard
+            self.assertEqual(best_match(mime_types_supported, 'image/png'), 'image/*')
+            # match using a wildcard for both requested and supported 
+            self.assertEqual(best_match(mime_types_supported, 'image/*'), 'image/*')
+
+    unittest.main() 

File django_authopenid/.svn/text-base/models.py.svn-base

+# -*- coding: utf-8 -*-
+from django.conf import settings
+from django.contrib import admin
+from django.contrib.auth.models import User
+from django.db import models
+
+import md5, random, sys, os, time
+
+__all__ = ['Nonce', 'Association', 'UserAssociation', 
+        'UserPasswordQueueManager', 'UserPasswordQueue']
+
+class Nonce(models.Model):
+    """ openid nonce """
+    server_url = models.CharField(max_length=255)
+    timestamp = models.IntegerField()
+    salt = models.CharField(max_length=40)
+    
+    def __unicode__(self):
+        return u"Nonce: %s" % self.id
+
+    
+class Association(models.Model):
+    """ association openid url and lifetime """
+    server_url = models.TextField(max_length=2047)
+    handle = models.CharField(max_length=255)
+    secret = models.TextField(max_length=255) # Stored base64 encoded
+    issued = models.IntegerField()
+    lifetime = models.IntegerField()
+    assoc_type = models.TextField(max_length=64)
+    
+    def __unicode__(self):
+        return u"Association: %s, %s" % (self.server_url, self.handle)
+
+class UserAssociation(models.Model):
+    """ 
+    model to manage association between openid and user 
+    """
+    openid_url = models.CharField(blank=False, max_length=255)
+    user = models.ForeignKey(User, unique=True)
+    
+    def __unicode__(self):
+        return "Openid %s with user %s" % (self.openid_url, self.user)
+        
+class UserAssociationAdmin(admin.ModelAdmin):
+    """User association admin class"""
+admin.site.register(UserAssociation, UserAssociationAdmin)
+
+class UserPasswordQueueManager(models.Manager):
+    """ manager for UserPasswordQueue object """
+    def get_new_confirm_key(self):
+        "Returns key that isn't being used."
+        # The random module is seeded when this Apache child is created.
+        # Use SECRET_KEY as added salt.
+        while 1:
+            confirm_key = md5.new("%s%s%s%s" % (
+                random.randint(0, sys.maxint - 1), os.getpid(),
+                time.time(), settings.SECRET_KEY)).hexdigest()
+            try:
+                self.get(confirm_key=confirm_key)
+            except self.model.DoesNotExist:
+                break
+        return confirm_key
+
+
+class UserPasswordQueue(models.Model):
+    """
+    model for new password queue.
+    """
+    user = models.ForeignKey(User, unique=True)
+    new_password = models.CharField(max_length=30)
+    confirm_key = models.CharField(max_length=40)
+
+    objects = UserPasswordQueueManager()
+
+    def __unicode__(self):
+        return self.user.username

File django_authopenid/.svn/text-base/urls.py.svn-base

+# -*- coding: utf-8 -*-
+from django.conf.urls.defaults import patterns, url
+from django.utils.translation import ugettext as _
+
+urlpatterns = patterns('django_authopenid.views',
+    # yadis rdf
+    url(r'^yadis\.xrdf$', 'xrdf', name='yadis_xrdf'),
+     # manage account registration
+    url(r'^%s$' % _('signin/'), 'signin', name='user_signin'),
+    url(r'^%s$' % _('signout/'), 'signout', name='user_signout'),
+    url(r'^%s%s$' % (_('signin/'), _('complete/')), 'complete_signin', 
+        name='user_complete_signin'),
+    url(r'^%s$' % _('register/'), 'register', name='user_register'),
+    url(r'^%s$' % _('signup/'), 'signup', name='user_signup'),
+    url(r'^%s$' % _('sendpw/'), 'sendpw', name='user_sendpw'),
+    url(r'^%s%s$' % (_('password/'), _('confirm/')), 'confirmchangepw', 
+        name='user_confirmchangepw'),
+
+    # manage account settings
+    url(r'^$', 'account_settings', name='user_account_settings'),
+    url(r'^%s$' % _('password/'), 'changepw', name='user_changepw'),
+    url(r'^%s$' % _('email/'), 'changeemail', name='user_changeemail'),
+    url(r'^%s$' % _('openid/'), 'changeopenid', name='user_changeopenid'),
+    url(r'^%s$' % _('delete/'), 'delete', name='user_delete'),
+)

File django_authopenid/.svn/text-base/util.py.svn-base

+# -*- coding: utf-8 -*-
+from openid.store.interface import OpenIDStore
+from openid.association import Association as OIDAssociation
+from openid.extensions import sreg
+import openid.store
+
+from django.db.models.query import Q
+from django.conf import settings
+
+
+# needed for some linux distributions like debian
+try:
+    from openid.yadis import xri
+except:
+    from yadis import xri
+
+import time, base64, md5, operator
+
+from models import Association, Nonce
+
+__all__ = ['OpenID', 'DjangoOpenIDStore', 'from_openid_response']
+
+class OpenID:
+    def __init__(self, openid_, issued, attrs=None, sreg_=None):
+        self.openid = openid_
+        self.issued = issued
+        self.attrs = attrs or {}
+        self.sreg = sreg_ or {}
+        self.is_iname = (xri.identifierScheme(openid_) == 'XRI')
+    
+    def __repr__(self):
+        return '<OpenID: %s>' % self.openid
+    
+    def __str__(self):
+        return self.openid
+
+class DjangoOpenIDStore(OpenIDStore):
+    def __init__(self):
+        self.max_nonce_age = 6 * 60 * 60 # Six hours
+    
+    def storeAssociation(self, server_url, association):
+        assoc = Association(
+            server_url = server_url,
+            handle = association.handle,
+            secret = base64.encodestring(association.secret),
+            issued = association.issued,
+            lifetime = association.issued,
+            assoc_type = association.assoc_type
+        )
+        assoc.save()
+    
+    def getAssociation(self, server_url, handle=None):
+        assocs = []
+        if handle is not None:
+            assocs = Association.objects.filter(
+                server_url = server_url, handle = handle
+            )
+        else:
+            assocs = Association.objects.filter(
+                server_url = server_url
+            )
+        if not assocs:
+            return None
+        associations = []
+        for assoc in assocs:
+            association = OIDAssociation(
+                assoc.handle, base64.decodestring(assoc.secret), assoc.issued,
+                assoc.lifetime, assoc.assoc_type
+            )
+            if association.getExpiresIn() == 0:
+                self.removeAssociation(server_url, assoc.handle)
+            else:
+                associations.append((association.issued, association))
+        if not associations:
+            return None
+        return associations[-1][1]
+    
+    def removeAssociation(self, server_url, handle):
+        assocs = list(Association.objects.filter(
+            server_url = server_url, handle = handle
+        ))
+        assocs_exist = len(assocs) > 0
+        for assoc in assocs:
+            assoc.delete()
+        return assocs_exist
+
+    def useNonce(self, server_url, timestamp, salt):
+        if abs(timestamp - time.time()) > openid.store.nonce.SKEW:
+            return False
+        
+        query = [
+                Q(server_url__exact=server_url),
+                Q(timestamp__exact=timestamp),
+                Q(salt__exact=salt),
+        ]
+        try:
+            ononce = Nonce.objects.get(reduce(operator.and_, query))
+        except Nonce.DoesNotExist:
+            ononce = Nonce(
+                    server_url=server_url,
+                    timestamp=timestamp,
+                    salt=salt
+            )
+            ononce.save()
+            return True
+        
+        ononce.delete()
+
+        return False
+   
+    def cleanupNonce(self):
+        Nonce.objects.filter(timestamp<int(time.time()) - nonce.SKEW).delete()
+
+    def cleanupAssociations(self):
+        Association.objects.extra(where=['issued + lifetimeint<(%s)' % time.time()]).delete()
+
+    def getAuthKey(self):
+        # Use first AUTH_KEY_LEN characters of md5 hash of SECRET_KEY
+        return md5.new(settings.SECRET_KEY).hexdigest()[:self.AUTH_KEY_LEN]
+    
+    def isDumb(self):
+        return False
+
+def from_openid_response(openid_response):
+    """ return openid object from response """
+    issued = int(time.time())
+    sreg_resp = sreg.SRegResponse.fromSuccessResponse(openid_response) \
+            or []
+    
+    return OpenID(
+        openid_response.identity_url, issued, openid_response.signed_fields, 
+         dict(sreg_resp)
+    )

File django_authopenid/.svn/text-base/views.py.svn-base

+# -*- coding: utf-8 -*-
+# Copyright (c) 2007, 2008, Benoît Chesneau
+# Copyright (c) 2007 Simon Willison, original work on django-openid
+# 
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 
+#      * Redistributions of source code must retain the above copyright
+#      * notice, this list of conditions and the following disclaimer.
+#      * Redistributions in binary form must reproduce the above copyright
+#      * notice, this list of conditions and the following disclaimer in the
+#      * documentation and/or other materials provided with the
+#      * distribution.  Neither the name of the <ORGANIZATION> nor the names
+#      * of its contributors may be used to endorse or promote products
+#      * derived from this software without specific prior written
+#      * permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from django.http import HttpResponseRedirect, get_host
+from django.shortcuts import render_to_response as render
+from django.template import RequestContext, loader, Context
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.auth import login, logout
+from django.contrib.auth.decorators import login_required
+from django.core.urlresolvers import reverse
+from django.utils.encoding import smart_unicode
+from django.utils.html import escape
+from django.utils.translation import ugettext as _
+from django.contrib.sites.models import Site
+from django.utils.http import urlquote_plus
+from django.core.mail import send_mail
+
+from openid.consumer.consumer import Consumer, \
+    SUCCESS, CANCEL, FAILURE, SETUP_NEEDED
+from openid.consumer.discover import DiscoveryFailure
+from openid.extensions import sreg
+# needed for some linux distributions like debian
+try:
+    from openid.yadis import xri
+except ImportError:
+    from yadis import xri
+
+import re
+import urllib
+
+
+from django_authopenid.util import OpenID, DjangoOpenIDStore, from_openid_response
+from django_authopenid.models import UserAssociation, UserPasswordQueue
+from django_authopenid.forms import OpenidSigninForm, OpenidAuthForm, OpenidRegisterForm, \
+        OpenidVerifyForm, RegistrationForm, ChangepwForm, ChangeemailForm, \
+        ChangeopenidForm, DeleteForm, EmailPasswordForm
+
+def get_url_host(request):
+    if request.is_secure():
+        protocol = 'https'
+    else:
+        protocol = 'http'
+    host = escape(get_host(request))
+    return '%s://%s' % (protocol, host)
+
+def get_full_url(request):
+    if request.is_secure():
+        protocol = 'https'
+    else:
+        protocol = 'http'
+    host = escape(request.META['HTTP_HOST'])
+    return get_url_host(request) + request.get_full_path()
+
+next_url_re = re.compile('^/[-\w/]+$')
+
+def is_valid_next_url(next):
+    # When we allow this:
+    #   /openid/?next=/welcome/
+    # For security reasons we want to restrict the next= bit 
+    # to being a local path, not a complete URL.
+    return bool(next_url_re.match(next))
+
+def ask_openid(request, openid_url, redirect_to, on_failure=None,
+        sreg_request=None):
+    """ basic function to ask openid and return response """
+    on_failure = on_failure or signin_failure
+    
+    trust_root = getattr(
+        settings, 'OPENID_TRUST_ROOT', get_url_host(request) + '/'
+    )
+    if xri.identifierScheme(openid_url) == 'XRI' and getattr(
+            settings, 'OPENID_DISALLOW_INAMES', False
+    ):
+        msg = _("i-names are not supported")
+        return on_failure(request, msg)
+    consumer = Consumer(request.session, DjangoOpenIDStore())
+    try:
+        auth_request = consumer.begin(openid_url)
+    except DiscoveryFailure:
+        msg = _("The OpenID %s was invalid" % openid_url)
+        return on_failure(request, msg)
+
+    if sreg_request:
+        auth_request.addExtension(sreg_request)
+    redirect_url = auth_request.redirectURL(trust_root, redirect_to)
+    return HttpResponseRedirect(redirect_url)
+
+def complete(request, on_success=None, on_failure=None, return_to=None):
+    """ complete openid signin """
+    on_success = on_success or default_on_success
+    on_failure = on_failure or default_on_failure
+    
+    consumer = Consumer(request.session, DjangoOpenIDStore())
+    # make sure params are encoded in utf8
+    params = dict((k,smart_unicode(v)) for k, v in request.GET.items())
+    openid_response = consumer.complete(params, return_to)
+            
+    
+    if openid_response.status == SUCCESS:
+        return on_success(request, openid_response.identity_url,
+                openid_response)
+    elif openid_response.status == CANCEL:
+        return on_failure(request, 'The request was canceled')
+    elif openid_response.status == FAILURE:
+        return on_failure(request, openid_response.message)
+    elif openid_response.status == SETUP_NEEDED:
+        return on_failure(request, 'Setup needed')
+    else:
+        assert False, "Bad openid status: %s" % openid_response.status
+
+def default_on_success(request, identity_url, openid_response):
+    """ default action on openid signin success """
+    request.session['openid'] = from_openid_response(openid_response)
+    
+    next = request.GET.get('next', '').strip()
+    if not next or not is_valid_next_url(next):
+        next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+    
+    return HttpResponseRedirect(next)
+
+def default_on_failure(request, message):
+    """ default failure action on signin """
+    return render('openid_failure.html', {
+        'message': message
+    })
+
+
+def not_authenticated(func):
+    """ decorator that redirect user to next page if
+    he is already logged."""
+    def decorated(request, **kwargs):
+        if request.user.is_authenticated():
+            next = request.GET.get("next", "/")
+            return HttpResponseRedirect(next)
+        return func(request, **kwargs)
+    return decorated
+
+@not_authenticated
+def signin(request):
+    """
+    signin page. It manage the legacy authentification (user/password) 
+    and authentification with openid.
+
+    url: /signin/
+    
+    template : authopenid/signin.htm
+    """
+
+    on_failure = signin_failure
+    next = ''
+
+
+    if request.GET.get('next') and is_valid_next_url(request.GET['next']):
+        next = request.GET.get('next', '').strip()
+    if not next or not is_valid_next_url(next):
+        next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+
+    form_signin = OpenidSigninForm(initial={'next':next})
+    form_auth = OpenidAuthForm(initial={'next':next})
+
+    if request.POST:   
+        if 'bsignin' in request.POST.keys():
+            form_signin = OpenidSigninForm(request.POST)
+            if form_signin.is_valid():
+                next = form_signin.cleaned_data['next']
+                if not next:
+                    next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+
+                sreg_req = sreg.SRegRequest(optional=['nickname', 'email'])
+                redirect_to = "%s%s?%s" % (
+                        get_url_host(request),
+                        reverse('user_complete_signin'), 
+                        urllib.urlencode({'next':next})
+                )
+
+                return ask_openid(request, 
+                        form_signin.cleaned_data['openid_url'], 
+                        redirect_to, 
+                        on_failure=signin_failure, 
+                        sreg_request=sreg_req)
+
+        elif 'blogin' in request.POST.keys():
+            # perform normal django authentification
+            form_auth = OpenidAuthForm(request.POST)
+            if form_auth.is_valid():
+                user_ = form_auth.get_user()
+                login(request, user_)
+
+                next = form_auth.cleaned_data['next']
+                if not next:
+                    next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+                return HttpResponseRedirect(next)
+
+
+    return render('authopenid/signin.html', {
+        'form1': form_auth,
+        'form2': form_signin,
+        'msg':  request.GET.get('msg',''),
+        'sendpw_url': reverse('user_sendpw'),
+    }, context_instance=RequestContext(request))
+
+def complete_signin(request):
+    """ in case of complete signin with openid """
+    return complete(request, signin_success, signin_failure,
+            get_url_host(request) + reverse('user_complete_signin'))
+
+
+def signin_success(request, identity_url, openid_response):
+    """
+    openid signin success.
+
+    If the openid is already registered, the user is redirected to 
+    url set par next or in settings with OPENID_REDIRECT_NEXT variable.
+    If none of these urls are set user is redirectd to /.
+
+    if openid isn't registered user is redirected to register page.
+    """
+
+    openid_ = from_openid_response(openid_response)
+    request.session['openid'] = openid_
+
+    try:
+        rel = UserAssociation.objects.get(openid_url__exact = str(openid_))
+    except:
+        # try to register this new user
+        return register(request)
+    user_ = rel.user
+    if user_.is_active:
+        user_.backend = "django.contrib.auth.backends.ModelBackend"
+        login(request, user_)
+
+    next = request.GET.get('next', '').strip()
+    if not next or not is_valid_next_url(next):
+        next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+    
+    return HttpResponseRedirect(next)
+
+def is_association_exist(openid_url):
+    """ test if an openid is already in database """
+    is_exist = True
+    try:
+        uassoc = UserAssociation.objects.get(openid_url__exact = openid_url)
+    except:
+        is_exist = False
+    return is_exist
+
+@not_authenticated
+def register(request):
+    """
+    register an openid.
+
+    If user is already a member he can associate its openid with 
+    its account.
+
+    A new account could also be created and automaticaly associated
+    to the openid.
+
+    url : /complete/
+
+    template : authopenid/complete.html
+    """
+
+    is_redirect = False
+    next = request.GET.get('next', '').strip()
+    if not next or not is_valid_next_url(next):
+        next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+
+
+    openid_ = request.session.get('openid', None)
+    if not openid_:
+        return HttpResponseRedirect(reverse('user_signin') + next)
+
+    nickname = openid_.sreg.get('nickname', '')
+    email = openid_.sreg.get('email', '')
+    
+    form1 = OpenidRegisterForm(initial={
+        'next': next,
+        'username': nickname,
+        'email': email,
+    }) 
+    form2 = OpenidVerifyForm(initial={
+        'next': next,
+        'username': nickname,
+    })
+    
+    if request.POST:
+        just_completed = False
+        if 'bnewaccount' in request.POST.keys():
+            form1 = OpenidRegisterForm(request.POST)
+            if form1.is_valid():
+                next = form1.cleaned_data['next']
+                if not next:
+                    next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+                is_redirect = True
+                tmp_pwd = User.objects.make_random_password()
+                user_ = User.objects.create_user(form1.cleaned_data['username'],
+                         form1.cleaned_data['email'], tmp_pwd)
+                
+                # make association with openid
+                uassoc = UserAssociation(openid_url=str(openid_),
+                        user_id=user_.id)
+                uassoc.save()
+                    
+                # login 
+                user_.backend = "django.contrib.auth.backends.ModelBackend"
+                login(request, user_)
+        elif 'bverify' in request.POST.keys():
+            form2 = OpenidVerifyForm(request.POST)
+            if form2.is_valid():
+                is_redirect = True
+                next = form2.cleaned_data['next']
+                if not next:
+                    next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+                user_ = form2.get_user()
+
+                uassoc = UserAssociation(openid_url=str(openid_),
+                        user_id=user_.id)
+                uassoc.save()
+                login(request, user_)
+        
+        # redirect, can redirect only if forms are valid.
+        if is_redirect:
+            return HttpResponseRedirect(next) 
+    
+    return render('authopenid/complete.html', {
+        'form1': form1,
+        'form2': form2,
+        'nickname': nickname,
+        'email': email
+    }, context_instance=RequestContext(request))
+
+def signin_failure(request, message):
+    """
+    falure with openid signin. Go back to signin page.
+
+    template : "authopenid/signin.html"
+    """
+    next = request.REQUEST.get('next', '')
+
+    form_signin = OpenidSigninForm(initial={'next': next})
+    form_auth = OpenidAuthForm(initial={'next': next})
+
+    return render('authopenid/signin.html', {
+        'msg': message,
+        'form1': form_auth,
+        'form2': form_signin,
+    }, context_instance=RequestContext(request))
+
+@not_authenticated
+def signup(request):
+    """
+    signup page. Create a legacy account
+
+    url : /signup/"
+
+    templates: authopenid/signup.html, authopenid/confirm_email.txt
+    """
+    action_signin = reverse('user_signin')
+
+    next = request.GET.get('next', '')
+    if not next or not is_valid_next_url(next):
+        next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+
+    form = RegistrationForm(initial={'next':next})
+    form_signin = OpenidSigninForm(initial={'next':next})
+    
+    if request.POST:
+        form = RegistrationForm(request.POST)
+        if form.is_valid():
+            next = form.cleaned_data.get('next', '')
+            if not next or not is_valid_next_url(next):
+                next = getattr(settings, 'OPENID_REDIRECT_NEXT', '/')
+
+            user_ = User.objects.create_user( form.cleaned_data['username'],
+                    form.cleaned_data['email'], form.cleaned_data['password1'])
+           
+            user_.backend = "django.contrib.auth.backends.ModelBackend"
+            login(request, user_)
+            
+            # send email
+            current_domain = Site.objects.get_current().domain
+            subject = _("Welcome")
+            message_template = loader.get_template(
+                    'authopenid/confirm_email.txt'
+            )
+            message_context = Context({ 
+                'site_url': 'http://%s/' % current_domain,
+                'username': form.cleaned_data['username'],
+                'password': form.cleaned_data['password1'] 
+            })
+            message = message_template.render(message_context)
+            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, 
+                    [user_.email])
+            
+            return HttpResponseRedirect(next)
+    
+    return render('authopenid/signup.html', {
+        'form': form,
+        'form2': form_signin,
+        }, context_instance=RequestContext(request))
+
+@login_required
+def signout(request):
+    """
+    signout from the website. Remove openid from session and kill it.
+
+    url : /signout/"
+    """
+    try:
+        del request.session['openid']
+    except KeyError:
+        pass
+    next = request.GET.get('next', '/')
+    if not is_valid_next_url(next):
+        next = '/'
+
+    logout(request)
+    
+    return HttpResponseRedirect(next)
+    
+def xrdf(request):
+    url_host = get_url_host(request)
+    return_to = [
+        "%s%s" % (url_host, reverse('user_complete_signin'))
+    ]
+    return render('authopenid/yadis.xrdf', { 
+        'return_to': return_to 
+        }, context_instance=RequestContext(request))
+
+@login_required
+def account_settings(request):
+    """
+    index pages to changes some basic account settings :
+     - change password
+     - change email
+     - associate a new openid
+     - delete account
+
+    url : /
+
+    template : authopenid/settings.html
+    """
+    msg = request.GET.get('msg', '')
+    is_openid = True
+
+    try:
+        uassoc = UserAssociation.objects.get(
+                user__username__exact=request.user.username
+        )
+    except:
+        is_openid = False
+
+
+    return render('authopenid/settings.html', {
+        'msg': msg,
+        'is_openid': is_openid
+        }, context_instance=RequestContext(request))
+
+@login_required
+def changepw(request):
+    """
+    change password view.
+
+    url : /changepw/
+    template: authopenid/changepw.html
+    """
+    
+    user_ = request.user
+    
+    if request.POST:
+        form = ChangepwForm(request.POST, user=user_)
+        if form.is_valid():
+            user_.set_password(form.cleaned_data['password1'])
+            user_.save()
+            msg = _("Password changed.") 
+            redirect = "%s?msg=%s" % (
+                    reverse('user_account_settings'),
+                    urlquote_plus(msg))
+            return HttpResponseRedirect(redirect)
+    else:
+        form = ChangepwForm(user=user_)
+
+    return render('authopenid/changepw.html', {'form': form },
+                                context_instance=RequestContext(request))
+
+@login_required
+def changeemail(request):
+    """ 
+    changeemail view. It require password or openid to allow change.
+
+    url: /changeemail/
+
+    template : authopenid/changeemail.html
+    """
+    msg = request.GET.get('msg', '')
+    extension_args = {}
+    user_ = request.user
+    
+    redirect_to = get_url_host(request) + reverse('user_changeemail')
+
+    if request.POST:
+        form = ChangeemailForm(request.POST, user=user_)
+        if form.is_valid():
+            if not form.test_openid:
+                user_.email = form.cleaned_data['email']
+                user_.save()
+                msg = _("Email changed.") 
+                redirect = "%s?msg=%s" % (reverse('user_account_settings'),
+                        urlquote_plus(msg))
+                return HttpResponseRedirect(redirect)
+            else:
+                request.session['new_email'] = form.cleaned_data['email']
+                return ask_openid(request, form.cleaned_data['password'], 
+                        redirect_to, on_failure=emailopenid_failure)    
+    elif not request.POST and 'openid.mode' in request.GET:
+        return complete(request, emailopenid_success, 
+                emailopenid_failure, redirect_to) 
+    else:
+        form = ChangeemailForm(initial={'email': user_.email},
+                user=user_)
+    
+    return render('authopenid/changeemail.html', {
+        'form': form,
+        'msg': msg 
+        }, context_instance=RequestContext(request))
+
+
+def emailopenid_success(request, identity_url, openid_response):
+    openid_ = from_openid_response(openid_response)
+
+    user_ = request.user
+    try:
+        uassoc = UserAssociation.objects.get(
+                openid_url__exact=identity_url
+        )
+    except:
+        return emailopenid_failure(request, 
+                _("No OpenID %s found associated in our database" % identity_url))
+
+    if uassoc.user.username != request.user.username:
+        return emailopenid_failure(request, 
+                _("The OpenID %s isn't associated to current user logged in" % 
+                    identity_url))
+    
+    new_email = request.session.get('new_email', '')
+    if new_email:
+        user_.email = new_email
+        user_.save()
+        del request.session['new_email']
+    msg = _("Email Changed.")
+
+    redirect = "%s?msg=%s" % (reverse('user_account_settings'),
+            urlquote_plus(msg))
+    return HttpResponseRedirect(redirect)
+    
+
+def emailopenid_failure(request, message):
+    redirect_to = "%s?msg=%s" % (
+            reverse('user_changeemail'), urlquote_plus(message))
+    return HttpResponseRedirect(redirect_to)
+ 
+@login_required
+def changeopenid(request):
+    """
+    change openid view. Allow user to change openid 
+    associated to its username.
+
+    url : /changeopenid/
+
+    template: authopenid/changeopenid.html