Merge auth branch.

- Legacy-Id: 1426
This commit is contained in:
Bill Fenner 2009-03-22 01:37:20 +00:00
parent 26b7eb2f7b
commit 3904c34055
12 changed files with 135 additions and 40 deletions

View file

@ -1,20 +1,33 @@
#!/usr/bin/env python
#
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from ietf.ietfauth.models import LegacyLiaisonUser, LegacyWgPassword
from ietf.idtracker.models import IESGLogin
for u in User.objects.all():
output = {}
# we use pseudo-polymorphism to get through all of these objects.
for p in list( IESGLogin.objects.all() ) + \
list( LegacyLiaisonUser.objects.all() ) + \
list( LegacyWgPassword.objects.all() ):
# Bad rows in these tables use bad references.
if p.person_id == 0 or p.person_id == 999999 or p.person_id == 888888:
continue
# Skip if we've already output this person
if p.login_name in output:
continue
output[ p.login_name ] = 1
if p.person.usermap_set.count() != 1:
print "# Can't find user mapping for %s" % p.login_name
continue
usermap = p.person.usermap_set.all()[0]
u = usermap.user
( 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
if p.login_name == u.username:
print "%s:IETF:%s" % ( u.username, hsh )
elif p.login_name == u.email:
print "%s:IETF:%s" % ( u.email, usermap.email_htdigest )
else:
print "# Can't find user mapping for %s (%s/%s)" % ( p.login_name, u.username, u.email )

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# Copyright The IETF Trust 2007, All Rights Reserved
# Copyright The IETF Trust 2007, 2008, All Rights Reserved
#
# Import users from all different IETF authentication sources into
# django. Map user names to people via the ietf.ietfauth.models.UserMap
@ -21,9 +21,14 @@
# - Liaison tool ('Users')
# - Then look for email address
#
# The LegacyWgPassword table contains a plaintext password, which is
# both unconscionable and very useful, as it lets us upgrade their
# password to full htdigest style.
#
#
from ietf.idtracker.models import PersonOrOrgInfo, IESGLogin, EmailAddress
from ietf.ietfauth.models import LegacyWgPassword, LegacyLiaisonUser
from ietf.ietfauth.auth import set_password
from ietf.utils import users
from django.core.validators import email_re
from django.contrib.auth.models import Group
@ -35,12 +40,17 @@ level2group = {
1: 'IESG',
2: 'ex-IESG',
}
if len(sys.argv) != 2:
print "usage: import-users htpasswd"
sys.exit(1)
f = open(sys.argv[1], 'r')
line = f.readline()
while line != '':
(user, pw) = line.rstrip("\n").split(":")
person = None
wg = None
# Some login names are a different E-Mail
# address than the one stored in email_addresses.
# If the login name looks like an email address
@ -73,7 +83,7 @@ while line != '':
pass
if person is None and email:
try:
person = PersonOrOrgInfo.objects.distinct().get(emailaddress__address=user)
person = PersonOrOrgInfo.objects.distinct().get(emailaddress__address__iexact=user)
except PersonOrOrgInfo.DoesNotExist:
pass
except AssertionError:
@ -83,6 +93,13 @@ while line != '':
u = users.create_user(user, email, person, cryptpw=pw)
except users.UserAlreadyExists, (msg, u):
print "Already in system as %s when adding %s (%s)" % ( u.username, user, email )
if not email_re.search( user ):
# If the existing username looks like it's one we made up
# (first_last), then change it to the one in the digest file.
if "_" in u.username:
print "Changing username from %s to %s" % ( u.username, user )
u.username = user
u.save()
if iesg:
try:
group, created = Group.objects.get_or_create(name = level2group[iesg.user_level])
@ -91,6 +108,9 @@ while line != '':
if group:
print "Adding %s to %s (user_level %d)" % (u.username, group.name, iesg.user_level)
u.groups.add(group)
if wg and wg.password:
# Use the plaintext password
set_password( u, wg.password )
else:
print "Could not map %s to person" % ( user )
line = f.readline()

View file

@ -774,6 +774,11 @@ class EmailAddress(models.Model):
#unique_together = (('email_priority', 'person_or_org'), )
# with this, I get 'ChangeManipulator' object has no attribute 'isUniqueemail_priority_person_or_org'
verbose_name_plural = 'Email addresses'
class Admin:
# Even though this is edit_inline, we want to be able
# to search for email addresses.
search_fields = [ 'address' ]
list_display = ( 'person_or_org', 'address', 'type', 'priority' )
class PhoneNumber(models.Model):
person_or_org = models.ForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag', edit_inline=models.TABULAR, num_in_admin=1)

View file

@ -53,7 +53,16 @@ class EmailBackend(ModelBackend):
else:
user = User.objects.get(username__iexact=username)
except User.DoesNotExist:
return None
#
# See if there's an IETF person with this address:
try:
usermap = UserMap.objects.distinct().get(person__emailaddress__address__iexact=username)
except UserMap.DoesNotExist:
return None
except AssertionError:
# multiple UserMaps, should never happen!
return None
user = usermap.user
if compat_check_password(user, password):
return user
return None

View file

@ -14,7 +14,8 @@ class UserMap(models.Model):
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 model's password field, the other is here. We also store
a hashed version of just the email address for the RFC Editor.
"""
user = models.ForeignKey(User, raw_id_admin=True, core=True)
# user should have unique=True, but that confuses the

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2007, All Rights Reserved
# Copyright The IETF Trust 2007, 2009, All Rights Reserved
from django.conf.urls.defaults import patterns
from ietf.ietfauth import views
@ -11,6 +11,7 @@ urlpatterns = patterns('django.contrib.auth.views',
(r'^password_change/done/$', 'password_change_done'),
)
urlpatterns += patterns('',
(r'^$', 'django.views.generic.simple.direct_to_template', {'template': 'registration/account_info.html'}),
(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'}),

View file

@ -34,7 +34,9 @@
<div id="content">
{% if user.is_authenticated %}
<span style="float: right; font-size: 80%;">Logged in as {{ user }} |
<a href="/accounts/logout/">Log Out</a></span>
<a href="{% url django.contrib.auth.views.logout %}">Log Out</a></span>
{% else %}
<span style="float: right; font-size: 80%;"><a href="{% url django.contrib.auth.views.login %}">Log In</a></span>
{% endif %}
{% block content %}{% endblock %}
</div>

View file

@ -3,12 +3,27 @@
{% block content %}
<p>
{% if me %}
Hello, {{ me }}!
<h1>Stuff I know about you:</h1>
<h2>Internet Drafts that you Author</h2>
<ul>
{% for doc in me.idauthor_set.all %}
<li>{{ doc.document.filename }}: {{ doc.document.status }} / {{ doc.document.idstate }}
{% endfor %}
</ul>
{% endif %}
<h2>Username and Email Address for legacy tools:</h2>
<dl>
{% if me.iesglogin_set.count %}
<dt>IESG I-D Tracker</dt>
<dd>{{ me.iesglogin_set.all.0.login_name }}</dd>
{% endif %}
{% if me.legacywgpassword_set.count %}
<dt>Working group chair tools (session request, proceedings, etc.)</dt>
<dd>{{ me.legacywgpassword_set.all.0.login_name }}</dd>
{% endif %}
{% if me.legacyliaisonuser_set.count %}
<dt>Liaison Manager</dt>
<dd>{{ me.legacyliaisonuser_set.all.0.login_name }}</dd>
{% endif %}
<dt>Username (for other tools, including django)</dt>
<dd>{{ user.username }}</dd>
<dt>Email Address (for other tools, including django)</dt>
<dd>{{ user.email }}</dd>
</dl>
<p>(A tool to allow you to change your username and/or email address is planned)</p>
{% endblock %}

View file

@ -0,0 +1,24 @@
{# Copyright The IETF Trust 2009, All Rights Reserved #}
{% extends "base.html" %}
{% block title %}IETF Account Information{% endblock %}
{% block content %}
<h1>IETF Account Information</h1>
<p>
Starting at IETF 74, we have switched to a self-service username/password
handling system. We attempted to retain your password, so try
<a href="{% url django.contrib.auth.views.login %}">logging in</a>.
If you can't find your password, you can
<a href="{% url ietf.ietfauth.views.password_request %}">get a new one here</a>.
If you don't have an account, you can
<a href="{% url ietf.ietfauth.views.password_request %}">create one</a>.
</p>
<p>
If you find that even after you register an account, your tool access
does not work (especially the historical cgi-based tools), please
contact <a href="mailto:webtools@ietf.org">webtools@ietf.org</a>.
</p>
{% endblock %}

View file

@ -1,11 +1,9 @@
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:
{% filter wordwrap:72 %}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:{% endfilter %}
http://{{ site.domain }}{% url ietf.ietfauth.views.password_return %}?timestamp={{ timestamp }}&email={{ email|urlencode }}&hash={{ hash }}
This link is valid for {{ days }} days.
This link is valid for {{ days }} days.
If that someone wasn't you, then you can ignore this email.
If that someone wasn't you, then you can ignore this email.

View file

@ -1,4 +1,4 @@
# Copyright The IETF Trust 2007, All Rights Reserved
# Copyright The IETF Trust 2007, 2009, All Rights Reserved
from django.conf.urls.defaults import patterns, include, handler404, handler500
@ -52,7 +52,7 @@ urlpatterns = patterns('',
(r'^(?P<path>public|cgi-bin)/', include('ietf.redirects.urls')),
(r'^ipr/', include('ietf.ipr.urls')),
(r'^meeting/', include('ietf.meeting.urls')),
(r'^accounts/', include('ietf.ietfauth.urls')),
(r'^account/', include('ietf.ietfauth.urls')),
(r'^doc/', include('ietf.idrfc.urls')),
(r'^wg/', include('ietf.wginfo.urls')),
@ -75,6 +75,10 @@ urlpatterns = patterns('',
# Uncomment this for pre-approval tool for initial Internet-Drafts
#(r'^wg/', include('ietf.wg.urls')),
# Django 0.96 hardcodes /accounts/profile/; we want to use
# /account/profile.
(r'accounts/profile/', 'django.views.generic.simple.redirect_to', { 'url': '/account/profile/' }),
)
if settings.SERVER_MODE in ('development', 'test'):

View file

@ -58,9 +58,11 @@ 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)
umap.person = person
umap.save()
umap, created = UserMap.objects.get_or_create(user = u,
defaults={'person': person})
if not created:
umap.person = person
umap.save()
raise UserAlreadyExists("Already in system as %s when adding %s (%s)" % ( u.username, user, email ), u)
else:
if cryptpw:
@ -72,9 +74,10 @@ def create_user(user, email, person, pw=None, cryptpw=None):
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)
umap.person = person
umap.save()
# get_or_create saves umap for us.
umap, created = UserMap.objects.get_or_create(user = u,
defaults={'person': person})
if not created:
umap.person = person
umap.save()
return u