Merge auth branch.
- Legacy-Id: 1149
This commit is contained in:
parent
027232d642
commit
473b2bdc60
181
ietf/LICENSE
Normal file
181
ietf/LICENSE
Normal file
|
@ -0,0 +1,181 @@
|
|||
|
||||
|
||||
Unless otherwise noted, this software is Copyright The IETF Trust, and is
|
||||
distributed under the Non-Profit OSL 3.0 license, quoted below and also
|
||||
available at http://trustee.ietf.org/docs/Non-Profit_OSL_3.0.pdf .
|
||||
|
||||
========================================================================
|
||||
|
||||
Copyright The IETF Trust (2007)
|
||||
Licensed under the Non-Profit Open Software License version 3.0
|
||||
|
||||
1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-
|
||||
exclusive, sublicensable license, for the duration of the copyright, to do the following:
|
||||
|
||||
a) to reproduce the Original Work in copies, either alone or as part of a collective work;
|
||||
|
||||
b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby
|
||||
creating derivative works ("Derivative Works") based upon the Original Work;
|
||||
|
||||
c) to distribute or communicate copies of the Original Work and Derivative Works to the
|
||||
public, with the proviso that copies of Original Work or Derivative Works that You
|
||||
distribute or communicate shall be licensed under this Non-Profit Open Software License
|
||||
or as provided in section 17(d);
|
||||
|
||||
d) to perform the Original Work publicly; and
|
||||
|
||||
e) to display the Original Work publicly.
|
||||
|
||||
2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-
|
||||
exclusive, sublicensable license, under patent claims owned or controlled by the Licensor
|
||||
that are embodied in the Original Work as furnished by the Licensor, for the duration of
|
||||
the patents, to make, use, sell, offer for sale, have made, and import the Original Work
|
||||
and Derivative Works.
|
||||
|
||||
3) Grant of Source Code License. The term "Source Code" means the preferred form of
|
||||
the Original Work for making modifications to it and all available documentation
|
||||
describing how to modify the Original Work. Licensor agrees to provide a machine-
|
||||
readable copy of the Source Code of the Original Work along with each copy of the
|
||||
Original Work that Licensor distributes. Licensor reserves the right to satisfy this
|
||||
obligation by placing a machine-readable copy of the Source Code in an information
|
||||
repository reasonably calculated to permit inexpensive and convenient access by You for
|
||||
as long as Licensor continues to distribute the Original Work.
|
||||
|
||||
4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any
|
||||
contributors to the Original Work, nor any of their trademarks or service marks, may be
|
||||
used to endorse or promote products derived from this Original Work without express
|
||||
prior permission of the Licensor. Except as expressly stated herein, nothing in this
|
||||
License grants any license to Licensor’s trademarks, copyrights, patents, trade secrets or
|
||||
any other intellectual property. No patent license is granted to make, use, sell, offer for
|
||||
sale, have made, or import embodiments of any patent claims other than the licensed
|
||||
claims defined in Section 2. No license is granted to the trademarks of Licensor even if
|
||||
such marks are included in the Original Work. Nothing in this License shall be
|
||||
interpreted to prohibit Licensor from licensing under terms different from this License
|
||||
any Original Work that Licensor otherwise would have a right to license.
|
||||
|
||||
5) External Deployment. The term "External Deployment" means the use, distribution, or
|
||||
communication of the Original Work or Derivative Works in any way such that the
|
||||
Original Work or Derivative Works may be used by anyone other than You, whether
|
||||
those works are distributed or communicated to those persons or made available as an
|
||||
application intended for use over a network. As an express condition for the grants of
|
||||
license hereunder, You must treat any External Deployment by You of the Original Work
|
||||
or a Derivative Work as a distribution under section 1(c).
|
||||
|
||||
6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that
|
||||
You create, all copyright, patent, or trademark notices from the Source Code of the
|
||||
Original Work, as well as any notices of licensing and any descriptive text identified
|
||||
therein as an "Attribution Notice." You must cause the Source Code for any Derivative
|
||||
Works that You create to carry a prominent Attribution Notice reasonably calculated to
|
||||
inform recipients that You have modified the Original Work.
|
||||
|
||||
7) Warranty of Provenance and Disclaimer of Warranty. The Original Work is provided
|
||||
under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express
|
||||
or implied, including, without limitation, the warranties of non-infringement,
|
||||
merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE
|
||||
QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF
|
||||
WARRANTY constitutes an essential part of this License. No license to the Original
|
||||
Work is granted by this License except under this disclaimer.
|
||||
|
||||
8) Limitation of Liability. Under no circumstances and under no legal theory, whether in
|
||||
tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone
|
||||
for any direct, indirect, special, incidental, or consequential damages of any character
|
||||
arising as a result of this License or the use of the Original Work including, without
|
||||
limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction,
|
||||
or any and all other commercial damages or losses. This limitation of liability shall not
|
||||
apply to the extent applicable law prohibits such limitation.
|
||||
|
||||
9) Acceptance and Termination. If, at any time, You expressly assented to this License,
|
||||
that assent indicates your clear and irrevocable acceptance of this License and all of its
|
||||
terms and conditions. If You distribute or communicate copies of the Original Work or a
|
||||
Derivative Work, You must make a reasonable effort under the circumstances to obtain
|
||||
the express assent of recipients to the terms of this License. This License conditions your
|
||||
rights to undertake the activities listed in Section 1, including your right to create
|
||||
Derivative Works based upon the Original Work, and doing so without honoring these
|
||||
terms and conditions is prohibited by copyright law and international treaty. Nothing in
|
||||
this License is intended to affect copyright exceptions and limitations (including "fair
|
||||
use" or "fair dealing"). This License shall terminate immediately and You may no longer
|
||||
exercise any of the rights granted to You by this License upon your failure to honor the
|
||||
conditions in Section 1(c).
|
||||
|
||||
10) Termination for Patent Action. This License shall terminate automatically and You
|
||||
may no longer exercise any of the rights granted to You by this License as of the date
|
||||
You commence an action, including a cross-claim or counterclaim, against Licensor or
|
||||
any licensee alleging that the Original Work infringes a patent. This termination
|
||||
provision shall not apply for an action alleging patent infringement by combinations of
|
||||
the Original Work with other software or hardware.
|
||||
|
||||
11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License
|
||||
may be brought only in the courts of a jurisdiction wherein the Licensor resides or in
|
||||
which Licensor conducts its primary business, and under the laws of that jurisdiction
|
||||
excluding its conflict-of-law provisions. The application of the United Nations
|
||||
Convention on Contracts for the International Sale of Goods is expressly excluded. Any
|
||||
use of the Original Work outside the scope of this License or after its termination shall be
|
||||
subject to the requirements and penalties of copyright or patent law in the appropriate
|
||||
jurisdiction. This section shall survive the termination of this License.
|
||||
|
||||
12) Attorneys’ Fees. In any action to enforce the terms of this License or seeking
|
||||
damages relating thereto, the prevailing party shall be entitled to recover its costs and
|
||||
expenses, including, without limitation, reasonable attorneys' fees and costs incurred in
|
||||
connection with such action, including any appeal of such action. This section shall
|
||||
survive the termination of this License.
|
||||
|
||||
13) Miscellaneous. If any provision of this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent necessary to make it enforceable.
|
||||
|
||||
14) Definition of "You" in This License. "You" throughout this License, whether in upper
|
||||
or lower case, means an individual or a legal entity exercising rights under, and
|
||||
complying with all of the terms of, this License. For legal entities, "You" includes any
|
||||
entity that controls, is controlled by, or is under common control with you. For purposes
|
||||
of this definition, "control" means (i) the power, direct or indirect, to cause the direction
|
||||
or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty
|
||||
percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such
|
||||
entity.
|
||||
|
||||
15) Right to Use. You may use the Original Work in all ways not otherwise restricted or
|
||||
conditioned by this License or by law, and Licensor promises not to interfere with or be
|
||||
responsible for such uses by You.
|
||||
|
||||
16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen.
|
||||
Permission is granted to copy, distribute, or communicate this License without
|
||||
modification. Nothing in this License permits You to modify this License as applied to
|
||||
the Original Work or to Derivative Works. However, You may modify the text of this
|
||||
License and copy, distribute or communicate your modified version (the "Modified
|
||||
License") and apply it to other original works of authorship subject to the following
|
||||
conditions: (i) You may not indicate in any way that your Modified License is the "Open
|
||||
Software License" or "OSL" and you may not use those names in the name of your
|
||||
Modified License; (ii) You must replace the notice specified in the first paragraph above
|
||||
with the notice "Licensed under <insert your license name here>" or with a notice of your
|
||||
own that is not confusingly similar to the notice in this License; and (iii) You may not
|
||||
claim that your original works are open source software unless your Modified License
|
||||
has been approved by Open Source Initiative (OSI) and You comply with its license
|
||||
review and certification process.
|
||||
|
||||
17) Non-Profit Amendment. The name of this amended version of the Open Software
|
||||
License ("OSL 3.0") is "Non-Profit Open Software License 3.0". The original OSL 3.0
|
||||
license has been amended as follows:
|
||||
|
||||
(a) Licensor represents and declares that it is a not-for-profit organization that derives no
|
||||
revenue whatsoever from the distribution of the Original Work or Derivative Works
|
||||
thereof, or from support or services relating thereto.
|
||||
|
||||
(b) The first sentence of Section 7 ["Warranty of Provenance"] of OSL 3.0 has been
|
||||
stricken. For Original Works licensed under this Non-Profit OSL 3.0, LICENSOR
|
||||
OFFERS NO WARRANTIES WHATSOEVER.
|
||||
|
||||
(c) In the first sentence of Section 8 ["Limitation of Liability"] of this Non-Profit OSL
|
||||
3.0, the list of damages for which LIABILITY IS LIMITED now includes "direct"
|
||||
damages.
|
||||
|
||||
(d) The proviso in Section 1(c) of this License now refers to this "Non-Profit Open
|
||||
Software License" rather than the "Open Software License". You may distribute or
|
||||
communicate the Original Work or Derivative Works thereof under this Non-Profit OSL
|
||||
3.0 license only if You make the representation and declaration in paragraph (a) of this
|
||||
Section 17. Otherwise, You shall distribute or communicate the Original Work or
|
||||
Derivative Works thereof only under the OSL 3.0 license and You shall publish clear
|
||||
licensing notices so stating. Also by way of clarification, this License does not authorize
|
||||
You to distribute or communicate works under this Non-Profit OSL 3.0 if You received
|
||||
them under the original OSL 3.0 license.
|
||||
|
||||
(e) Original Works licensed under this license shall reference "Non-Profit OSL 3.0" in
|
||||
licensing notices to distinguish them from works licensed under the original OSL 3.0
|
||||
license.
|
20
ietf/bin/export-htdigest
Executable file
20
ietf/bin/export-htdigest
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
for u in User.objects.all():
|
||||
( algo, salt, hsh ) = u.password.split( '$' )
|
||||
if algo != 'htdigest':
|
||||
continue
|
||||
# Note: not everyone needs the username one.
|
||||
# Old IESG members and some liaison users do.
|
||||
# Too much work for now to skip the ones that
|
||||
# don't need it.
|
||||
print "%s:IETF:%s" % ( u.username, hsh )
|
||||
try:
|
||||
htdigest = u.get_profile().email_htdigest
|
||||
if htdigest != '':
|
||||
print "%s:IETF:%s" % ( u.email, htdigest )
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
|
@ -3,11 +3,15 @@
|
|||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.core.validators import email_re
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from ietf.ietfauth.models import UserMap
|
||||
import md5
|
||||
|
||||
def crypt_check_password(user, raw_password):
|
||||
def compat_check_password(user, raw_password):
|
||||
"""
|
||||
Returns a boolean of whether the raw_password was correct. Handles
|
||||
crypt format only, and updates the password to the hashed version
|
||||
crypt and htdigest formats, and updates the password to htdigest
|
||||
on first use. This is like User.check_password().
|
||||
"""
|
||||
enc_password = user.password
|
||||
|
@ -16,13 +20,31 @@ def crypt_check_password(user, raw_password):
|
|||
import crypt
|
||||
is_correct = ( salt + hsh == crypt.crypt(raw_password, salt) )
|
||||
if is_correct:
|
||||
user.set_password(raw_password)
|
||||
user.save()
|
||||
# upgrade to htdigest
|
||||
set_password(user, raw_password)
|
||||
return is_correct
|
||||
return user.check_password(raw_password)
|
||||
if algo == 'htdigest':
|
||||
# Check username hash.
|
||||
is_correct = ( hsh == htdigest( user.username, raw_password ) )
|
||||
if not is_correct:
|
||||
# Try to check email hash, which we stored in the profile.
|
||||
# If the profile doesn't exist, that's odd but we shouldn't
|
||||
# completely fail, so try/except it.
|
||||
try:
|
||||
is_correct = ( user.get_profile().email_htdigest == htdigest( user.email, raw_password ) )
|
||||
except ObjectDoesNotExist:
|
||||
# no user profile to store the htdigest, so can't check it.
|
||||
pass
|
||||
return is_correct
|
||||
# permit django passwords, but upgrade to htdigest
|
||||
is_correct = user.check_password(raw_password)
|
||||
if is_correct:
|
||||
# upgrade to htdigest
|
||||
set_password(user, raw_password)
|
||||
return is_correct
|
||||
|
||||
# Based on http://www.djangosnippets.org/snippets/74/
|
||||
# but modified to use crypt_check_password for all users.
|
||||
# but modified to use compat_check_password for all users.
|
||||
class EmailBackend(ModelBackend):
|
||||
def authenticate(self, username=None, password=None):
|
||||
try:
|
||||
|
@ -32,7 +54,7 @@ class EmailBackend(ModelBackend):
|
|||
user = User.objects.get(username__iexact=username)
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
if crypt_check_password(user, password):
|
||||
if compat_check_password(user, password):
|
||||
return user
|
||||
return None
|
||||
|
||||
|
@ -42,3 +64,23 @@ class EmailBackend(ModelBackend):
|
|||
except User.DoesNotExist:
|
||||
return None
|
||||
|
||||
def htdigest( username, password, realm=None ):
|
||||
"""Returns a hashed password in the Apache htdigest format, which
|
||||
is used in an AuthDigestFile ."""
|
||||
if realm is None:
|
||||
try:
|
||||
realm = settings.DIGEST_REALM
|
||||
except AttributeError:
|
||||
realm = 'IETF'
|
||||
return md5.md5( ':'.join( [ username, realm, password ] ) ).hexdigest()
|
||||
|
||||
def set_password( user, password, realm=None ):
|
||||
# The username-hashed digest goes in the user database;
|
||||
# the email-address-hashed digest goes in the userprof.
|
||||
user.password = '$'.join( [ 'htdigest', '',
|
||||
htdigest( user.username, password, realm ) ] )
|
||||
user.save()
|
||||
( userprof, created ) = UserMap.objects.get_or_create( user=user )
|
||||
userprof.email_htdigest = htdigest( user.email, password, realm )
|
||||
userprof.rfced_htdigest = htdigest( user.email, password, 'RFC Editor' )
|
||||
userprof.save()
|
||||
|
|
40
ietf/ietfauth/forms.py
Normal file
40
ietf/ietfauth/forms.py
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
from django import newforms as forms
|
||||
from django.conf import settings
|
||||
import hmac, sha
|
||||
import time
|
||||
|
||||
class EmailForm(forms.Form):
|
||||
email = forms.EmailField()
|
||||
|
||||
def email_hash(email, timestamp):
|
||||
return hmac.new(settings.SECRET_KEY, "%d%s" % (timestamp, email), sha).hexdigest()
|
||||
|
||||
class ChallengeForm(forms.Form):
|
||||
email = forms.EmailField()
|
||||
timestamp = forms.IntegerField()
|
||||
hash = forms.CharField()
|
||||
def clean_timestamp(self):
|
||||
now = int(time.time())
|
||||
timestamp = self.clean_data['timestamp']
|
||||
if timestamp > now:
|
||||
raise forms.ValidationError, 'Timestamp in the future'
|
||||
if timestamp < (now - 86400*settings.PASSWORD_DAYS):
|
||||
raise forms.ValidationError, 'Timestamp is too old'
|
||||
return timestamp
|
||||
def clean_hash(self):
|
||||
if self.clean_data['hash'] != email_hash(self.clean_data['email'], self.clean_data['timestamp']):
|
||||
raise forms.ValidationError, 'Hash is incorrect'
|
||||
return self.clean_data['hash']
|
||||
|
||||
class PWForm(forms.Form):
|
||||
password = forms.CharField(label='Enter your desired password', widget=forms.PasswordInput())
|
||||
repeat = forms.CharField(label='Re-enter the same password', widget=forms.PasswordInput())
|
||||
def clean_repeat(self):
|
||||
if self.clean_data['password'] != self.clean_data['repeat']:
|
||||
raise forms.ValidationError, 'Passwords do not match'
|
||||
|
||||
# Field lengths from PersonOrOrgInfo
|
||||
class FirstLastForm(forms.Form):
|
||||
first = forms.CharField(label='First Name', max_length=20, widget = forms.TextInput(attrs = {'size': 20}))
|
||||
last = forms.CharField(label='Last Name', max_length=50, widget = forms.TextInput(attrs = {'size': 50}))
|
|
@ -10,11 +10,18 @@ class UserMap(models.Model):
|
|||
This can't represent the users in the existing tool that
|
||||
have multiple accounts with multiple privilege levels: they
|
||||
need extra IETF users.
|
||||
|
||||
It also contains a text field for the user's hashed htdigest
|
||||
password. In order to allow logging in with either username
|
||||
or email address, we need to store two hashes. One is in the
|
||||
user model's password field, the other is here.
|
||||
"""
|
||||
user = models.ForeignKey(User, raw_id_admin=True, core=True)
|
||||
# user should have unique=True, but that confuses the
|
||||
# admin edit_inline interface.
|
||||
person = models.ForeignKey(PersonOrOrgInfo, edit_inline=models.STACKED, num_in_admin=1, max_num_in_admin=1, unique=True)
|
||||
person = models.ForeignKey(PersonOrOrgInfo, edit_inline=models.STACKED, num_in_admin=1, max_num_in_admin=1, unique=True, null=True)
|
||||
email_htdigest = models.CharField(maxlength=32, blank=True, null=True)
|
||||
rfced_htdigest = models.CharField(maxlength=32, blank=True, null=True)
|
||||
def __str__(self):
|
||||
return "Mapping django user %s to IETF person %s" % ( self.user, self.person )
|
||||
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
from django.conf.urls.defaults import patterns
|
||||
from ietf.ietfauth import views
|
||||
from ietf.my.views import my
|
||||
|
||||
urlpatterns = patterns('django.contrib.auth.views',
|
||||
(r'^login/$', 'login'),
|
||||
# need to provide templates for logout, password_change,
|
||||
# password_change_done
|
||||
# right now they use the admin templates, which are not
|
||||
# really appropriate.
|
||||
(r'^logout/$', 'logout'),
|
||||
(r'^password_change/$', 'password_change'),
|
||||
(r'^password_change/done/$', 'password_change_done'),
|
||||
# Built-in password reset changes before validation
|
||||
# so we want to implement a scheme similar to henrik's
|
||||
# loginmgr.
|
||||
#(r'^password_reset/$', 'password_reset'),
|
||||
#(r'^password_reset/done/$', 'password_reset_done'),
|
||||
)
|
||||
urlpatterns += patterns('',
|
||||
(r'^request/$', views.password_request),
|
||||
(r'^return/$', views.password_return),
|
||||
(r'^return/(?P<action>\w+)/$', 'django.views.generic.simple.direct_to_template', {'template': 'registration/action_done.html'}),
|
||||
(r'^profile/$', my)
|
||||
)
|
||||
|
|
|
@ -1,3 +1,101 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
from django.conf import settings
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template.context import RequestContext
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.contrib.sites.models import Site
|
||||
from django.contrib.auth.models import User
|
||||
from ietf.idtracker.models import PersonOrOrgInfo
|
||||
from ietf.ietfauth.models import UserMap
|
||||
from ietf.ietfauth.forms import EmailForm, ChallengeForm, PWForm, FirstLastForm, email_hash
|
||||
from ietf.ietfauth.auth import set_password
|
||||
from ietf.utils.mail import send_mail
|
||||
from ietf.utils.users import create_user
|
||||
from ietf.utils.log import log
|
||||
import time
|
||||
|
||||
# Create your views here.
|
||||
def password_request(request):
|
||||
if request.method == 'POST':
|
||||
form = EmailForm(request.POST)
|
||||
if form.is_valid():
|
||||
timestamp = int(time.time())
|
||||
email = form.clean_data['email']
|
||||
hash = email_hash(email, timestamp)
|
||||
site = Site.objects.get_current()
|
||||
context = {'timestamp': timestamp, 'email': email, 'hash': hash, 'days': settings.PASSWORD_DAYS, 'site': site}
|
||||
send_mail(request, email, None, 'IETF Datatracker Password',
|
||||
'registration/password_email.txt', context, toUser=True)
|
||||
return render_to_response('registration/challenge_sent.html', context,
|
||||
context_instance=RequestContext(request))
|
||||
else:
|
||||
form = EmailForm()
|
||||
return render_to_response('registration/password_request.html', {'form': form},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def password_return(request):
|
||||
form = ChallengeForm(request.REQUEST)
|
||||
if form.is_valid():
|
||||
email = form.clean_data['email']
|
||||
method = request.method
|
||||
try:
|
||||
# Is there a django user?
|
||||
user = User.objects.get(email__iexact=email)
|
||||
try:
|
||||
usermap = UserMap.objects.get(user=user)
|
||||
person = usermap.person
|
||||
except UserMap.DoesNotExist:
|
||||
person = None
|
||||
except User.DoesNotExist:
|
||||
# Is there an IETF person, and a usermap to a django user,
|
||||
# e.g., the django user table has the wrong email address?
|
||||
user = None
|
||||
try:
|
||||
person = PersonOrOrgInfo.objects.distinct().get(emailaddress__address__iexact=email)
|
||||
try:
|
||||
usermap = UserMap.objects.get(person=person)
|
||||
user = usermap.user
|
||||
except UserMap.DoesNotExist:
|
||||
pass
|
||||
except PersonOrOrgInfo.DoesNotExist:
|
||||
person = None
|
||||
if person is None:
|
||||
# If there's no IETF person, try creating one.
|
||||
if method == 'POST':
|
||||
flform = FirstLastForm(request.POST)
|
||||
if flform.is_valid():
|
||||
person = PersonOrOrgInfo( first_name=flform.clean_data['first'], last_name=flform.clean_data['last'], created_by='SelfSvc' )
|
||||
person.save()
|
||||
person.emailaddress_set.create( type='INET', priority=1, address=email, comment='Created with SelfService' )
|
||||
# fall through to "if user or person"
|
||||
# hack:
|
||||
# pretend to the fall-through form that we used GET.
|
||||
method = 'GET'
|
||||
else:
|
||||
flform = FirstLastForm()
|
||||
return render_to_response('registration/new_person_form.html', {'form': form, 'flform': flform},
|
||||
context_instance=RequestContext(request))
|
||||
if user or person:
|
||||
# form to get a password, either for reset or new user
|
||||
if method == 'POST':
|
||||
pwform = PWForm(request.POST)
|
||||
if pwform.is_valid():
|
||||
pw = pwform.clean_data['password']
|
||||
if user:
|
||||
set_password(user, pw)
|
||||
user.save()
|
||||
return HttpResponseRedirect('changed/')
|
||||
else:
|
||||
create_user(None, email, person, pw=pw)
|
||||
return HttpResponseRedirect('created/')
|
||||
else:
|
||||
pwform = PWForm()
|
||||
return render_to_response('registration/password_form.html', {'u': user, 'person': person, 'form': form, 'pwform': pwform},
|
||||
context_instance=RequestContext(request))
|
||||
else:
|
||||
# We shouldn't get here.
|
||||
return render_to_response('registration/generic_failure.html', {},
|
||||
context_instance=RequestContext(request))
|
||||
else:
|
||||
log("bad challenge for %s: %s" % (form.data.get('email', '<None>'), form.errors.as_text().replace('\n', ' ').replace(' *', ':')))
|
||||
return render_to_response('registration/bad_challenge.html', {'form': form, 'days': settings.PASSWORD_DAYS},
|
||||
context_instance=RequestContext(request))
|
||||
|
|
|
@ -165,6 +165,9 @@ IPR_DOCUMENT_PATH = '/home/local/ftp/data/ietf/IPR'
|
|||
|
||||
IPR_EMAIL_TO = ['ietf-ipr@ietf.org', ]
|
||||
|
||||
# The number of days for which a password-request URL is valid
|
||||
PASSWORD_DAYS = 3
|
||||
|
||||
# Put SECRET_KEY in here, or any other sensitive or site-specific
|
||||
# changes. DO NOT commit settings_local.py to svn.
|
||||
from settings_local import *
|
||||
|
|
23
ietf/templates/registration/action_done.html
Normal file
23
ietf/templates/registration/action_done.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Success{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Success</h1>
|
||||
|
||||
<p>Your
|
||||
{% ifequal params.action "changed" %}
|
||||
password
|
||||
{% else %}
|
||||
account
|
||||
{% endifequal %}
|
||||
has been {{ params.action }}. You can bask in the glow of your
|
||||
{% ifequal params.action "changed" %}
|
||||
changed password,
|
||||
{% else %}
|
||||
new account,
|
||||
{% endifequal %}
|
||||
or you can <a href="{% url django.contrib.auth.views.login %}">visit the
|
||||
login page</a> and exercise it.
|
||||
</p>
|
||||
{% endblock %}
|
22
ietf/templates/registration/bad_challenge.html
Normal file
22
ietf/templates/registration/bad_challenge.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Bad Challenge{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Bad Challenge</h1>
|
||||
|
||||
<p>Sorry, the URL you used was incorrect. Make sure that you copied it
|
||||
properly from the email you received.</p>
|
||||
|
||||
{% comment %}
|
||||
if we feel like it, report the detailed error
|
||||
<table>
|
||||
<tr><th colspan="2">DEBUG</th></tr>
|
||||
{{ form }}
|
||||
</table>
|
||||
{% endcomment %}
|
||||
|
||||
<p>Note that these URLs expire after {{ days }} days, so you may need
|
||||
to <a href="{% url ietf.ietfauth.views.password_request %}">request
|
||||
another one</a>.</p>
|
||||
{% endblock %}
|
13
ietf/templates/registration/challenge_sent.html
Normal file
13
ietf/templates/registration/challenge_sent.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Challenge Sent{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Challenge Sent</h1>
|
||||
|
||||
<p>
|
||||
A URL has been sent to the email address that you provided.
|
||||
Wait for that message to arrive, then come back to the URL
|
||||
provided in the email to continue your requested operation.
|
||||
</p>
|
||||
{% endblock %}
|
16
ietf/templates/registration/generic_failure.html
Normal file
16
ietf/templates/registration/generic_failure.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Manual Handling Required{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Manual Handling Required</h1>
|
||||
|
||||
<p>We're sorry, but while the algorithms used to create accounts
|
||||
automatically cover many IETF participants, they failed in your
|
||||
case. If you use a different email address for IETF participation,
|
||||
please use that one to create your account.</p>
|
||||
|
||||
<p>Please contact <a href="mailto:ietf-action@ietf.org">ietf-action@ietf.org</a>
|
||||
for assistance with your account. Please share the steps that got you
|
||||
to this page.</p>
|
||||
{% endblock %}
|
|
@ -1,7 +1,10 @@
|
|||
{# Copyright The IETF Trust 2007, All Rights Reserved #}
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Log In{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Log In</h1>
|
||||
|
||||
{% if form.has_errors %}
|
||||
<p>Your username and password didn't match our records. Please try again.</p>
|
||||
|
@ -17,4 +20,7 @@
|
|||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
</form>
|
||||
|
||||
<a href="{% url ietf.ietfauth.views.password_request %}">Reset Password</a> |
|
||||
<a href="{% url ietf.ietfauth.views.password_request %}">Register</a>
|
||||
|
||||
{% endblock %}
|
||||
|
|
18
ietf/templates/registration/new_person_form.html
Normal file
18
ietf/templates/registration/new_person_form.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}New User Creation{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
Creating New IETF User
|
||||
<form action="" method="POST">
|
||||
{# keep the challenge token with the transaction #}
|
||||
{{ form.timestamp.as_hidden }}
|
||||
{{ form.email.as_hidden }}
|
||||
{{ form.hash.as_hidden }}
|
||||
<table>
|
||||
{{ flform }}
|
||||
</table>
|
||||
<input type="submit">
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
11
ietf/templates/registration/password_email.txt
Normal file
11
ietf/templates/registration/password_email.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
Hi,
|
||||
|
||||
Someone gave this email address as theirs when requesting a
|
||||
password change or new account at {{ site.name }}. If that
|
||||
was you, please visit this URL to continue the process:
|
||||
|
||||
http://{{ site.domain }}{% url ietf.ietfauth.views.password_return %}?timestamp={{ timestamp }}&email={{ email|urlencode }}&hash={{ hash }}
|
||||
|
||||
This link is valid for {{ days }} days.
|
||||
|
||||
If that someone wasn't you, then you can ignore this email.
|
22
ietf/templates/registration/password_form.html
Normal file
22
ietf/templates/registration/password_form.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Password Entry{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if u %}
|
||||
Resetting password for {{ u.username }} ({{ u.get_full_name|escape }}).
|
||||
{% else %}
|
||||
Creating new account for {{ person }}
|
||||
{% endif %}
|
||||
<form action="" method="POST">
|
||||
{# keep the challenge token with the transaction #}
|
||||
{{ form.timestamp.as_hidden }}
|
||||
{{ form.email.as_hidden }}
|
||||
{{ form.hash.as_hidden }}
|
||||
<table>
|
||||
{{ pwform }}
|
||||
</table>
|
||||
<input type="submit">
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
17
ietf/templates/registration/password_request.html
Normal file
17
ietf/templates/registration/password_request.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Password Request{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Password Request</h1>
|
||||
|
||||
<p>This form allows you to confirm that you own an email address,
|
||||
in order to change your password or register a new account.</p>
|
||||
|
||||
<form action="" method="POST">
|
||||
<table>
|
||||
{{ form }}
|
||||
</table>
|
||||
<input type="submit">
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -133,8 +133,8 @@ def send_mail_text(request, to, frm, subject, txt, cc=None, extra=None, toUser=N
|
|||
elif settings.SERVER_MODE == 'test':
|
||||
if toUser:
|
||||
copy_email(msg, to, toUser=True)
|
||||
elif request and request.COOKIE.has_key( 'testmailcc' ):
|
||||
copy_email(msg, request.COOKIE[ 'testmailcc' ])
|
||||
elif request and request.COOKIES.has_key( 'testmailcc' ):
|
||||
copy_email(msg, request.COOKIES[ 'testmailcc' ])
|
||||
try:
|
||||
copy_to = settings.EMAIL_COPY_TO
|
||||
except AttributeError:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#
|
||||
|
||||
from ietf.ietfauth.models import UserMap
|
||||
from ietf.ietfauth.auth import set_password
|
||||
from django.contrib.auth.models import User
|
||||
from django.template import defaultfilters
|
||||
|
||||
|
@ -9,6 +10,12 @@ class UserAlreadyExists(Exception):
|
|||
pass
|
||||
|
||||
def create_user(user, email, person, pw=None, cryptpw=None):
|
||||
try:
|
||||
umap = UserMap.objects.get(person = person)
|
||||
u = umap.user
|
||||
raise UserAlreadyExists("Already in system as %s when adding %s (%s)" % ( u.username, user, email ), u)
|
||||
except UserMap.DoesNotExist:
|
||||
pass
|
||||
if user is None or '@' in user:
|
||||
# slugify to remove non-ASCII; slugify uses hyphens but
|
||||
# user schema says underscore.
|
||||
|
@ -51,7 +58,9 @@ def create_user(user, email, person, pw=None, cryptpw=None):
|
|||
u.last_name = person.last_name
|
||||
u.save()
|
||||
# make sure that the UserMap gets created
|
||||
umap, created = UserMap.objects.get_or_create(user = u, person = person)
|
||||
umap, created = UserMap.objects.get_or_create(user = u)
|
||||
umap.person = person
|
||||
umap.save()
|
||||
raise UserAlreadyExists("Already in system as %s when adding %s (%s)" % ( u.username, user, email ), u)
|
||||
else:
|
||||
if cryptpw:
|
||||
|
@ -60,10 +69,12 @@ def create_user(user, email, person, pw=None, cryptpw=None):
|
|||
password = '!' # no hash
|
||||
u = User(username = user, email = email, password = password, first_name = person.first_name, last_name = person.last_name )
|
||||
if pw:
|
||||
u.set_password(pw)
|
||||
set_password(u, pw)
|
||||
#print "Saving user: username='%s', email='%s'" % ( u.username, u.email )
|
||||
u.save()
|
||||
umap, created = UserMap.objects.get_or_create(user = u, person = person)
|
||||
umap, created = UserMap.objects.get_or_create(user = u)
|
||||
umap.person = person
|
||||
umap.save()
|
||||
# get_or_create saves umap for us.
|
||||
|
||||
return u
|
||||
|
|
Loading…
Reference in a new issue