Beginning of email verification. Just need to clean up, add field validation, and allow users to set {primary, bugzilla, ...} emails to stuff from the person_email table.
This commit is contained in:
parent
ea0b7949b8
commit
3d687e8e54
7 changed files with 191 additions and 29 deletions
12
fas/TODO
12
fas/TODO
|
@ -1,10 +1,4 @@
|
|||
Things to Fix in FAS2 before declaring it done
|
||||
Things to Fix in FAS2 before declaring it done:
|
||||
|
||||
|
||||
safasprovider.py
|
||||
----------------
|
||||
validate_password():
|
||||
We'll want to change this to something that allows for longer passwords
|
||||
(like md5). The one thing about that is we have to figure out how system
|
||||
passwords use salt, etc. That way we'll be able to use this with
|
||||
make_shell_accounts.
|
||||
Nice-to-have things:
|
||||
* Easy searching within groups (and sponsor/admin interface)
|
||||
|
|
|
@ -54,7 +54,7 @@ tg.strict_parameters = True
|
|||
|
||||
server.webpath='/accounts'
|
||||
base_url_filter.on=True
|
||||
base_url_filter.base_url = "https://publictest10.fedoraproject.org/accounts"
|
||||
base_url_filter.base_url = "https://publictest10.fedoraproject.org"
|
||||
|
||||
# Make the session cookie only return to the host over an SSL link
|
||||
# Disabled for testing.
|
||||
|
|
|
@ -423,15 +423,15 @@ mapper(People, PeopleTable, properties = {
|
|||
'email_purposes': relation(EmailPurposes, backref = 'person',
|
||||
collection_class = column_mapped_collection(
|
||||
EmailPurposesTable.c.purpose)),
|
||||
'person_emails': relation(PersonEmails, backref = 'person',
|
||||
collection_class = column_mapped_collection(
|
||||
PersonEmailsTable.c.email)),
|
||||
'approved_roles': relation(ApprovedRoles, backref='member',
|
||||
primaryjoin = PeopleTable.c.id==ApprovedRoles.c.person_id),
|
||||
'unapproved_roles': relation(UnApprovedRoles, backref='member',
|
||||
primaryjoin = PeopleTable.c.id==UnApprovedRoles.c.person_id)
|
||||
})
|
||||
mapper(PersonEmails, PersonEmailsTable, properties = {
|
||||
'person': relation(People, backref = 'person_emails', uselist = False,
|
||||
primaryjoin = PeopleTable.c.id==PersonEmailsTable.c.person_id)
|
||||
})
|
||||
mapper(PersonEmails, PersonEmailsTable)
|
||||
mapper(EmailPurposes, EmailPurposesTable, properties = {
|
||||
'person_email': relation(PersonEmails, uselist = False,
|
||||
primaryjoin = PersonEmailsTable.c.id==EmailPurposesTable.c.email_id)
|
||||
|
|
30
fas/fas/templates/user/email/add.html
Normal file
30
fas/fas/templates/user/email/add.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:py="http://genshi.edgewall.org/"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
<xi:include href="../../master.html" />
|
||||
<head>
|
||||
<title>${_('Add Email')}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>${_('Add Email')}</h2>
|
||||
<form action="${tg.url('/user/email/save/%s' % target.username)}" method="post" enctype="multipart/form-data">
|
||||
<div class="field">
|
||||
<label for="email">${_('Email')}:</label>
|
||||
<input type="text" id="email" name="email" />
|
||||
<!-- TODO: More generic documentation for adding an email -->
|
||||
<script type="text/javascript">var hb1 = new HelpBalloon({dataURL: '${tg.url('/help/get_help/user_primary_email')}'});</script>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="description">${_('Description')}:</label>
|
||||
<input type="text" id="description" name="description" />
|
||||
<!-- TODO: Correct documentation for this -->
|
||||
<script type="text/javascript">var hb1 = new HelpBalloon({dataURL: '${tg.url('/help/get_help/user_primary_email')}'});</script>
|
||||
</div>
|
||||
<div class="field">
|
||||
<input type="submit" value="${_('Save!')}" />
|
||||
<a href="${tg.url('/user/email/manage')}">${_('Cancel')}</a>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -7,7 +7,7 @@
|
|||
<title>${_('Manage Emails')}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>${_('Managing Emails for %s') % person.username}</h2>
|
||||
<h2>${_('Managing Emails for %s') % target.username}</h2>
|
||||
<h3>Available Emails</h3>
|
||||
<table>
|
||||
<thead>
|
||||
|
@ -18,7 +18,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr py:for="email in person.person_emails">
|
||||
<tr py:for="email in target.person_emails.values()">
|
||||
<td><a href="mailto:${email.email}">${email.email}</a></td>
|
||||
<td>${email.description}</td>
|
||||
<td py:if="email.verified"><span class="approved">${_('Verified')}</span></td>
|
||||
|
@ -36,7 +36,7 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr py:for="purpose in person.email_purposes.values()">
|
||||
<tr py:for="purpose in target.email_purposes.values()">
|
||||
<td><a href="mailto:${purpose.email}">${purpose.email}</a></td>
|
||||
<td>${purpose.person_email.description}</td>
|
||||
<td>${purpose.purpose}</td>
|
||||
|
|
|
@ -3,6 +3,8 @@ from turbogears import controllers, expose, paginate, identity, redirect, widget
|
|||
from turbogears.database import session
|
||||
import cherrypy
|
||||
|
||||
import turbomail
|
||||
|
||||
import os
|
||||
import re
|
||||
import gpgme
|
||||
|
@ -221,7 +223,8 @@ class User(controllers.Controller):
|
|||
target = person
|
||||
if not canEditUser(person, target):
|
||||
turbogears.flash(_('You cannot edit %s') % target.username )
|
||||
username = turbogears.identity.current.username
|
||||
turbogears.redirect('/user/view/%s', target.username)
|
||||
return dict()
|
||||
return dict(target=target)
|
||||
|
||||
@identity.require(turbogears.identity.not_anonymous())
|
||||
|
@ -236,7 +239,7 @@ class User(controllers.Controller):
|
|||
|
||||
if not canEditUser(person, target):
|
||||
turbogears.flash(_("You do not have permission to edit '%s'") % target.username)
|
||||
turbogears.redirect('/user/edit/%s', target.username)
|
||||
turbogears.redirect('/user/view/%s', target.username)
|
||||
return dict()
|
||||
try:
|
||||
target.human_name = human_name
|
||||
|
@ -286,7 +289,6 @@ class User(controllers.Controller):
|
|||
# Also, perhaps implement a timeout- delete account
|
||||
# if the e-mail is not verified (i.e. the person changes
|
||||
# their password) withing X days.
|
||||
import turbomail
|
||||
try:
|
||||
person = People()
|
||||
person.username = username
|
||||
|
@ -296,6 +298,7 @@ class User(controllers.Controller):
|
|||
person.status = 'active'
|
||||
session.flush()
|
||||
|
||||
# TODO: Handle properly if email has already been used. This might be painful, since the person already exists, at this point.
|
||||
person_email = PersonEmails()
|
||||
person_email.email = email
|
||||
person_email.person = person
|
||||
|
|
|
@ -3,11 +3,16 @@ from turbogears import controllers, expose, paginate, identity, redirect, widget
|
|||
from turbogears.database import session
|
||||
import cherrypy
|
||||
|
||||
import turbomail
|
||||
import random
|
||||
|
||||
from fas.model import People
|
||||
from fas.model import PersonEmails
|
||||
from fas.model import EmailPurposes
|
||||
from fas.model import Log
|
||||
|
||||
from fas.auth import *
|
||||
|
||||
class NonFedoraEmail(validators.FancyValidator):
|
||||
'''Make sure that an email address is not @fedoraproject.org'''
|
||||
def _to_python(self, value, state):
|
||||
|
@ -16,13 +21,20 @@ class NonFedoraEmail(validators.FancyValidator):
|
|||
if value.endswith('@fedoraproject.org'):
|
||||
raise validators.Invalid(_("To prevent email loops, your email address cannot be @fedoraproject.org."), value, state)
|
||||
|
||||
class EmailCreate(validators.Schema):
|
||||
class EmailSave(validators.Schema):
|
||||
email = validators.All(
|
||||
validators.Email(not_empty=True, strip=True),
|
||||
NonFedoraEmail(not_empty=True, strip=True),
|
||||
)
|
||||
#fedoraPersonBugzillaMail = validators.Email(strip=True)
|
||||
postal_address = validators.String(max=512)
|
||||
description = validators.String(not_empty=True, max=512)
|
||||
|
||||
def generate_validtoken(length=32):
|
||||
''' Generate Validation Token '''
|
||||
chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
token = ''
|
||||
for i in xrange(length):
|
||||
token += random.choice(chars)
|
||||
return token
|
||||
|
||||
class Email(controllers.Controller):
|
||||
|
||||
|
@ -34,7 +46,7 @@ class Email(controllers.Controller):
|
|||
def index(self):
|
||||
'''Redirect to manage
|
||||
'''
|
||||
turbogears.redirect('/user/email/manage/%s' % turbogears.identity.current.user_name)
|
||||
turbogears.redirect('/user/email/manage')
|
||||
|
||||
|
||||
@expose(template="fas.templates.error")
|
||||
|
@ -48,12 +60,135 @@ class Email(controllers.Controller):
|
|||
#@validate(validators=UserView())
|
||||
@error_handler(error)
|
||||
@expose(template="fas.templates.user.email.manage", allow_json=True)
|
||||
def manage(self, username=None):
|
||||
def manage(self, targetname=None):
|
||||
'''
|
||||
Manage a person's emails.
|
||||
Manage a person's emails
|
||||
'''
|
||||
if not username:
|
||||
username = turbogears.identity.current.user_name
|
||||
# TODO: Some sort of auth checking - other people should
|
||||
# probably be limited to looking at a person's email through
|
||||
# /user/view, although admins should probably be able to set
|
||||
# emails (with/without verification?)
|
||||
username = turbogears.identity.current.user_name
|
||||
person = People.by_username(username)
|
||||
return dict(person=person)
|
||||
|
||||
if targetname:
|
||||
target = People.by_username(targetname)
|
||||
else:
|
||||
target = person
|
||||
|
||||
return dict(target=target)
|
||||
|
||||
@identity.require(turbogears.identity.not_anonymous())
|
||||
#@validate(validators=UserView())
|
||||
@error_handler(error)
|
||||
@expose(template="fas.templates.user.email.add", allow_json=True)
|
||||
def add(self, targetname=None):
|
||||
'''
|
||||
Display the form to add an email
|
||||
'''
|
||||
username = turbogears.identity.current.user_name
|
||||
person = People.by_username(username)
|
||||
|
||||
if targetname:
|
||||
target = People.by_username(targetname)
|
||||
else:
|
||||
target = person
|
||||
|
||||
if not canEditUser(person, target):
|
||||
turbogears.flash(_('You cannot edit %s') % target.username )
|
||||
turbogears.redirect('/user/email/manage')
|
||||
return dict()
|
||||
|
||||
return dict(target=target)
|
||||
|
||||
@identity.require(turbogears.identity.not_anonymous())
|
||||
@validate(validators=EmailSave())
|
||||
@error_handler(error)
|
||||
@expose(template="fas.templates.user.email.add", allow_json=True)
|
||||
def save(self, targetname, email, description):
|
||||
'''
|
||||
Display the form to add an email
|
||||
'''
|
||||
username = turbogears.identity.current.user_name
|
||||
person = People.by_username(username)
|
||||
|
||||
if targetname:
|
||||
target = People.by_username(targetname)
|
||||
else:
|
||||
target = person
|
||||
|
||||
if not canEditUser(person, target):
|
||||
turbogears.flash(_('You cannot edit %s') % target.username )
|
||||
turbogears.redirect('/user/email/manage')
|
||||
return dict()
|
||||
|
||||
validtoken = generate_validtoken()
|
||||
|
||||
try:
|
||||
person_email = PersonEmails()
|
||||
person_email.email = email
|
||||
person_email.person = target
|
||||
person_email.description = description
|
||||
person_email.validtoken = validtoken
|
||||
session.flush()
|
||||
# Hmm, should this be checked in the validator or here?
|
||||
except IntegrityError:
|
||||
turbogears.flash(_('The email \'%s\' is already in used.') % email)
|
||||
return dict(target=target)
|
||||
else:
|
||||
# TODO: Make this email more friendly. Maybe escape the @ in email too?
|
||||
validurl = config.get('base_url_filter.base_url') + turbogears.url('/user/email/verify/%s/%s/%s') % (target.username, email, validtoken)
|
||||
message = turbomail.Message(config.get('accounts_mail'), email, _('Confirm this email address'))
|
||||
message.plain = _('''
|
||||
Go to this URL to verify that you own this email address: %s
|
||||
''') % validurl
|
||||
turbomail.enqueue(message)
|
||||
turbogears.flash(_('Your email has been added. Before you can use this email, you must verify it. The email you added should receive a message with instructions shortly.'))
|
||||
|
||||
return dict(target=target)
|
||||
|
||||
return dict(target=target)
|
||||
|
||||
@identity.require(turbogears.identity.not_anonymous())
|
||||
# TODO: Validation!
|
||||
#@validate(validators=UserView())
|
||||
@error_handler(error)
|
||||
@expose(allow_json=True)
|
||||
def verify(self, targetname, email, validtoken):
|
||||
'''
|
||||
Verify an email
|
||||
'''
|
||||
username = turbogears.identity.current.user_name
|
||||
person = People.by_username(username)
|
||||
|
||||
if targetname:
|
||||
target = People.by_username(targetname)
|
||||
else:
|
||||
target = person
|
||||
|
||||
if not canEditUser(person, target):
|
||||
turbogears.flash(_('You cannot edit %s') % target.username )
|
||||
turbogears.redirect('/user/email/manage')
|
||||
return dict()
|
||||
|
||||
if target.person_emails[email].verified:
|
||||
turbogears.flash(_('The email provided has already been verified.'))
|
||||
turbogears.redirect('/user/email/manage')
|
||||
return dict()
|
||||
|
||||
try:
|
||||
if target.person_emails[email].validtoken == validtoken:
|
||||
target.person_emails[email].validtoken = ''
|
||||
target.person_emails[email].verified = True
|
||||
turbogears.flash(_('Your email has been successfully verified.'))
|
||||
turbogears.redirect('/user/email/manage')
|
||||
return dict()
|
||||
else:
|
||||
turbogears.flash(_('The verification string did not match.'))
|
||||
turbogears.redirect('/user/email/manage')
|
||||
return dict()
|
||||
except KeyError:
|
||||
turbogears.flash(_('No such email is associated with your user.'))
|
||||
turbogears.redirect('/user/email/manage')
|
||||
return dict()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue