import sha import time import random import os import smtplib from email.MIMEText import MIMEText from Component import Component, ServletComponent class ForgottenPasswordServletComponent(ServletComponent): _expiration = 24 # hours # @@: need better default _secretPath = 'password_secret.txt' _secret = None _fromAddress = None # @@: should get this from config: _smtpHost = '127.0.0.1' _servletMethods = ['remember', 'emailForgotten', 'forgottenPasswordForm', 'resetPassword'] def __init__(self, expiration=None, secretPath=None, secret=None, fromAddress=None): ServletComponent.__init__(self) if expiration is not None: self._expiration = expiration if secretPath is not None: self._secretPath = secretPath if secret and not self._secret: self.__class__.secret = secret self._fromAddress = fromAddress def actions(self): return self._servletMethods def writeForgottenPasswordForm(self): error = self._formError req = self.servlet().request() if error: error = '
%s
' % self._badMessage) def resetPasswordForm(self, username, error=''): self.servlet().setView(self.writeResetPasswordForm) self.servlet()._title = 'Reset your password' self._username = username self._formError = error self.servlet().writeHTML() def writeResetPasswordForm(self): write = self.servlet().response().write hasher = sha.new() hasher.update(self._username) hasher.update(self._getSecret()) # @@: There really should be an expiration for this signature, # to avoid replay attacks signature = hasher.hexdigest() error = self._formError if error: error = 'You may reset your password:
%s ''' % (error, self.htmlEncode(self._username), self.htmlEncode(signature))) def resetPassword(self): req = self.servlet().request() username = req.field('username', '') signature = req.field('signature', '') hasher = sha.new() hasher.update(username) hasher.update(self._getSecret()) if signature != hasher.hexdigest(): # @@: should link back to start of form self.badRequest('The request was bad; please start over and try again') return password = req.field('password', '') confirm = req.field('confirm', '') manager = self.servlet().userManager() msg = manager.passwordInvalid(password) if msg: self.resetPasswordForm(username, error=msg) return if password != confirm: self.resetPasswordForm(username, error='The password and confirmation do not match') return user = manager.userForUsername(username) user.setPassword(password) self.servlet()._title = 'Password reset' self.servlet().setView(self.writeResetPassword) self.servlet().writeHTML() def writeResetPassword(self): self.servlet().write('Your password has been reset!') def emailForgotten(self): req = self.servlet().request() email = req.field('email', '').strip() if not email: self.forgottenPasswordForm( error='You must enter a username or email address') return manager = self.servlet().userManager() for user in manager.allUsers(): if user.username().lower() == email.lower() \ or user.email().lower() == email.lower(): break else: # No email found self.forgottenPasswordForm( error='The username or email address you entered was ' 'not found in our system') return print repr(user) expiration = time.time() + (self._expiration * 360) # Round up, use hours: expiration = int(expiration / 360 + 0.9) secret = self._getSecret() hasher = sha.new() hasher.update(user.username()) hasher.update(str(expiration)) hasher.update(secret) signature = hasher.hexdigest() url = self.servlet().linkToSelf( args={'_action_': 'remember', 'u': user.username(), 'e': expiration, 's': str(signature)}, absolute=1) body = """You have requested that your email address be reset. To reset your password, go to this URL: %s """ % url self.sendEmail(toAddress=user.email(), fromAddress=self.fromAddress(), subject='Forgotten password', body=body) self.servlet().setView(self.writeEmailForgotten) self._sentUser = user self.servlet()._title = 'Email Sent!' self.servlet().writeHTML() def writeEmailForgotten(self): self.servlet().write('Your email has been sent to %s' % self.htmlEncode(self._sentUser.email())) def fromAddress(self): if self._fromAddress is None: env = self.servlet().request().environ() host = env.get('HTTP_HOST', env['SERVER_NAME']) return 'donotreply@%s' % host return self._fromAddress def sendEmail(self, toAddress, fromAddress, subject, body): msg = MIMEText(body) msg['Subject'] = subject msg['From'] = fromAddress msg['To'] = toAddress print msg server = smtplib.SMTP(self._smtpHost) #server.set_debuglevel(10) server.sendmail(fromAddress, [toAddress], msg.as_string()) server.close() def _getSecret(self): # Should join to working directory filename = self._secretPath if self._secret is None: if not os.path.exists(filename): secret = randomString() f = open(filename, 'wb') f.write(secret) f.close() else: f = open(filename, 'r') secret = f.read().strip() f.close() self.__class__._secret = secret return self._secret def forgottenPasswordForm(self, error=''): self._formError = error self.servlet()._title = 'Forgotten Password Form' self.servlet().setView(self.writeForgottenPasswordForm) self.servlet().writeHTML() class ForgottenPasswordComponent(Component): _componentClass = ForgottenPasswordServletComponent def randomString(): return (hex(random.randrange(0xffff))[2:] + hex(random.randrange(0xffff))[2:] + hex(random.randrange(0xffff))[2:] + hex(random.randrange(0xffff))[2:])