summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: d7fe6d4)
raw | patch | inline | side by side (parent: d7fe6d4)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Thu, 27 Feb 2003 05:43:02 +0000 (05:43 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Thu, 27 Feb 2003 05:43:02 +0000 (05:43 +0000) |
mechanism to PyPI.
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1554 57a73879-2fb5-44c3-a270-3262357dd7e2
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1554 57a73879-2fb5-44c3-a270-3262357dd7e2
roundup/cgi/client.py | patch | blob | history | |
roundup/mailgw.py | patch | blob | history | |
roundup/templates/classic/html/page | patch | blob | history | |
roundup/templates/classic/html/user.forgotten | [new file with mode: 0644] | patch | blob |
roundup/templates/classic/html/user.rego_progress | [new file with mode: 0644] | patch | blob |
diff --git a/roundup/cgi/client.py b/roundup/cgi/client.py
index e109bafacc7d7a46f296b1aa3a40c5f96f360e7f..8f4bf01e42bd9ad2b86a3e79ef9bc37ccbb087b9 100644 (file)
--- a/roundup/cgi/client.py
+++ b/roundup/cgi/client.py
-# $Id: client.py,v 1.100 2003-02-26 04:57:49 richard Exp $
+# $Id: client.py,v 1.101 2003-02-27 05:43:01 richard Exp $
__doc__ = """
WWW request handler (also used in the stand-alone server).
import os, os.path, cgi, StringIO, urlparse, re, traceback, mimetypes, urllib
import binascii, Cookie, time, random, MimeWriter, smtplib, socket, quopri
-import stat, rfc822
+import stat, rfc822, string
from roundup import roundupdb, date, hyperdb, password
from roundup.i18n import _
from roundup.cgi import cgitb
from roundup.cgi.PageTemplates import PageTemplate
from roundup.rfc2822 import encode_header
+from roundup.mailgw import uidFromAddress
class HTTPException(Exception):
pass
self.write(cgitb.html())
def clean_sessions(self):
- '''age sessions, remove when they haven't been used for a week.
- Do it only once an hour'''
+ ''' Age sessions, remove when they haven't been used for a week.
+
+ Do it only once an hour.
+
+ Note: also cleans One Time Keys, and other "session" based
+ stuff.
+ '''
sessions = self.db.sessions
last_clean = sessions.get('last_clean', 'last_use') or 0
hour = 60*60
now = time.time()
if now - last_clean > hour:
- # remove age sessions
+ # remove aged sessions
for sessid in sessions.list():
interval = now - sessions.get(sessid, 'last_use')
if interval > week:
sessions.destroy(sessid)
+ # remove aged otks
+ otks = self.db.otks
+ for sessid in otks.list():
+ interval = now - okts.get(sessid, '__time')
+ if interval > week:
+ otk.destroy(sessid)
sessions.set('last_clean', last_use=time.time())
def determine_user(self):
('new', 'newItemAction'),
('register', 'registerAction'),
('confrego', 'confRegoAction'),
+ ('passrst', 'passResetAction'),
('login', 'loginAction'),
('logout', 'logout_action'),
('search', 'searchAction'),
''' Determine whether there should be an Action called.
The action is defined by the form variable :action which
- identifies the method on this object to call. The four basic
- actions are defined in the "actions" sequence on this class:
- "edit" -> self.editItemAction
- "editcsv" -> self.editCSVAction
- "new" -> self.newItemAction
- "register" -> self.registerAction
- "confrego" -> self.confRegoAction
- "login" -> self.loginAction
- "logout" -> self.logout_action
- "search" -> self.searchAction
- "retire" -> self.retireAction
+ identifies the method on this object to call. The actions
+ are defined in the "actions" sequence on this class.
'''
if self.form.has_key(':action'):
action = self.form[':action'].value.lower()
# Let the user know what's going on
self.ok_message.append(_('You are logged out'))
- chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
+ chars = string.letters+string.digits
def registerAction(self):
'''Attempt to create a new user based on the contents of the form
and then set the cookie.
props[propname] = str(value)
elif isinstance(proptype, hyperdb.Password):
props[propname] = str(value)
+ props['__time'] = time.time()
self.db.otks.set(otk, **props)
+ # send the email
+ tracker_name = self.db.config.TRACKER_NAME
+ subject = 'Complete your registration to %s'%tracker_name
+ body = '''
+To complete your registration of the user "%(name)s" with %(tracker)s,
+please visit the following URL:
+
+ %(url)s?@action=confrego&otk=%(otk)s
+'''%{'name': props['username'], 'tracker': tracker_name, 'url': self.base,
+ 'otk': otk}
+ if not self.sendEmail(props['address'], subject, body):
+ return
+
+ # commit changes to the database
+ self.db.commit()
+
+ # redirect to the "you're almost there" page
+ raise Redirect, '%suser?@template=rego_progress'%self.base
+
+ def sendEmail(self, to, subject, content):
# send email to the user's email address
message = StringIO.StringIO()
writer = MimeWriter.MimeWriter(message)
tracker_name = self.db.config.TRACKER_NAME
- s = 'Complete your registration to %s'%tracker_name
- writer.addheader('Subject', encode_header(s))
- writer.addheader('To', props['address'])
+ writer.addheader('Subject', encode_header(subject))
+ writer.addheader('To', to)
writer.addheader('From', roundupdb.straddr((tracker_name,
self.db.config.ADMIN_EMAIL)))
writer.addheader('Date', time.strftime("%a, %d %b %Y %H:%M:%S +0000",
body = writer.startbody('text/plain; charset=utf-8')
# message body, encoded quoted-printable
- content = StringIO.StringIO('''
-To complete your registration of the user "%(name)s" with %(tracker)s,
-please visit the following URL:
-
- http://localhost:8001/test/?@action=confrego&otk=%(otk)s
-'''%{'name': props['username'], 'tracker': tracker_name, 'url': self.base,
- 'otk': otk})
+ content = StringIO.StringIO(content)
quopri.encode(content, body, 0)
# now try to send the message
# send the message as admin so bounces are sent there
# instead of to roundup
smtp = smtplib.SMTP(self.db.config.MAILHOST)
- smtp.sendmail(self.db.config.ADMIN_EMAIL, [props['address']],
- message.getvalue())
+ smtp.sendmail(self.db.config.ADMIN_EMAIL, [to], message.getvalue())
except socket.error, value:
- self.error_message.append("Error: couldn't send "
- "confirmation email: mailhost %s"%value)
- return
+ self.error_message.append("Error: couldn't send email: "
+ "mailhost %s"%value)
+ return 0
except smtplib.SMTPException, value:
- self.error_message.append("Error: couldn't send "
- "confirmation email: %s"%value)
- return
-
- # commit changes to the database
- self.db.commit()
-
- # redirect to the "you're almost there" page
- raise Redirect, '%s?:template=rego_step1_done'%self.base
+ self.error_message.append("Error: couldn't send email: %s"%value)
+ return 0
+ return 1
def registerPermission(self, props):
''' Determine whether the user has permission to register
# try:
if 1:
props['roles'] = self.instance.config.NEW_WEB_USER_ROLES
+ del props['__time']
self.userid = cl.create(**props)
# clear the props from the otk database
self.db.otks.destroy(otk)
raise Redirect, '%suser%s?@ok_message=%s'%(
self.base, self.userid, urllib.quote(message))
+ def passResetAction(self):
+ ''' Handle password reset requests.
+
+ Presence of either "name" or "address" generate email.
+ Presense of "otk" performs the reset.
+ '''
+ if self.form.has_key('otk'):
+ # pull the rego information out of the otk database
+ otk = self.form['otk'].value
+ uid = self.db.otks.get(otk, 'uid')
+
+ # re-open the database as "admin"
+ if self.user != 'admin':
+ self.opendb('admin')
+
+ # change the password
+ newpw = ''.join([random.choice(self.chars) for x in range(8)])
+
+ cl = self.db.user
+ # XXX we need to make the "default" page be able to display errors!
+ # try:
+ if 1:
+ # set the password
+ cl.set(uid, password=password.Password(newpw))
+ # clear the props from the otk database
+ self.db.otks.destroy(otk)
+ self.db.commit()
+ # except (ValueError, KeyError), message:
+ # self.error_message.append(str(message))
+ # return
+
+ # user info
+ address = self.db.user.get(uid, 'address')
+ name = self.db.user.get(uid, 'username')
+
+ # send the email
+ tracker_name = self.db.config.TRACKER_NAME
+ subject = 'Password reset for %s'%tracker_name
+ body = '''
+The password has been reset for username "%(name)s".
+
+Your password is now: %(password)s
+'''%{'name': name, 'password': newpw}
+ if not self.sendEmail(address, subject, body):
+ return
+
+ self.ok_message.append('Password reset and email sent to %s'%address)
+ return
+
+ # no OTK, so now figure the user
+ if self.form.has_key('username'):
+ name = self.form['username'].value
+ try:
+ uid = self.db.user.lookup(name)
+ except KeyError:
+ self.error_message.append('Unknown username')
+ return
+ address = self.db.user.get(uid, 'address')
+ elif self.form.has_key('address'):
+ address = self.form['address'].value
+ uid = uidFromAddress(self.db, ('', address), create=0)
+ if not uid:
+ self.error_message.append('Unknown email address')
+ return
+ name = self.db.user.get(uid, 'username')
+ else:
+ self.error_message.append('You need to specify a username '
+ 'or address')
+ return
+
+ # generate the one-time-key and store the props for later
+ otk = ''.join([random.choice(self.chars) for x in range(32)])
+ self.db.otks.set(otk, uid=uid, __time=time.time())
+
+ # send the email
+ tracker_name = self.db.config.TRACKER_NAME
+ subject = 'Confirm reset of password for %s'%tracker_name
+ body = '''
+Someone, perhaps you, has requested that the password be changed for your
+username, "%(name)s". If you wish to proceed with the change, please follow
+the link below:
+
+ %(url)suser?@template=forgotten&@action=passrst&otk=%(otk)s
+
+You should then receive another email with the new password.
+'''%{'name': name, 'tracker': tracker_name, 'url': self.base, 'otk': otk}
+ if not self.sendEmail(address, subject, body):
+ return
+
+ self.ok_message.append('Email sent to %s'%address)
+
def editItemAction(self):
''' Perform an edit of an item in the database.
diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index 14b5d03ec4d66cb391980907235971ac5eda12e0..53dc0417fbd433ffececfa1765b58f530c28ab07 100644 (file)
--- a/roundup/mailgw.py
+++ b/roundup/mailgw.py
an exception, the original message is bounced back to the sender with the
explanatory message given in the exception.
-$Id: mailgw.py,v 1.110 2003-02-22 06:47:04 richard Exp $
+$Id: mailgw.py,v 1.111 2003-02-27 05:43:01 richard Exp $
'''
import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri
# try a straight match of the address
user = extractUserFromList(db.user, db.user.stringFind(address=address))
- if user is not None: return user
+ if user is not None:
+ return user
# try the user alternate addresses if possible
props = db.user.getprops()
if props.has_key('alternate_addresses'):
users = db.user.filter(None, {'alternate_addresses': address})
user = extractUserFromList(db.user, users)
- if user is not None: return user
+ if user is not None:
+ return user
# try to match the username to the address (for local
# submissions where the address is empty)
index 112fbcecfb78e3b15e548a0567643fec6713d604..cd2e98f182d9abb758019afe1bc2ac099288f724 100644 (file)
<input size="10" type="password" name="__login_password"><br>
<input type="submit" name=":action" value="login">
<span tal:replace="structure request/indexargs_form" />
- <a href="user?:template=register">Register</a>
+ <a href="user?:template=register">Register</a><br>
+ <a href="user?:template=forgotten">Forgotten your password?</a><br>
</p>
</form>
diff --git a/roundup/templates/classic/html/user.forgotten b/roundup/templates/classic/html/user.forgotten
--- /dev/null
@@ -0,0 +1,33 @@
+<!-- dollarId: user.item,v 1.7 2002/08/16 04:29:04 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title">Password reset request</title>
+<td class="page-header-top" metal:fill-slot="body_title">
+ <h2>Password reset request</h2>
+</td>
+<td class="content" metal:fill-slot="content">
+
+<p>You have two options if you have forgotten your password. If you
+know the email address you registered with, enter it below.</p>
+
+<form method="POST" onSubmit="return submit_once()">
+<input type="hidden" name="@action" value="passrst">
+<input type="hidden" name="@template" value="forgotten">
+<table class="form">
+ <tr><th>Email Address:</th> <td><input name="address"></td> </tr>
+ <tr><td></td><td><input type="submit" value="Request password reset"></td></tr>
+</table>
+
+<p>Or, if you know your username, then enter it below.</p>
+
+<table class="form">
+ <tr><th>Username:</th> <td><input name="username"></td> </tr>
+ <tr><td></td><td><input type="submit" value="Request password reset"></td></tr>
+</table>
+</form>
+
+<p>A confirmation email will be sent to you - please follow the
+instructions
+within it to complete the reset process.</p>
+</td>
+
+</tal:block>
diff --git a/roundup/templates/classic/html/user.rego_progress b/roundup/templates/classic/html/user.rego_progress
--- /dev/null
@@ -0,0 +1,16 @@
+<!-- dollarId: issue.index,v 1.2 2001/07/29 04:07:37 richard Exp dollar-->
+<tal:block metal:use-macro="templates/page/macros/icing">
+<title metal:fill-slot="head_title">List of issues</title>
+<td class="page-header-top" metal:fill-slot="body_title">
+ <h1>Registration in progress...</h1>
+</td>
+<td class="content" metal:fill-slot="content">
+
+<p>You will shortly receive an email to confirm your registration. To
+complete the registration process, visit the link indicated in the
+email.
+</p>
+
+</td>
+</tal:block>
+