From 6af2c3e935948543c3c5aaa59a91baa2a05caa7c Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Tue, 16 Jan 2007 17:09:19 +0000 Subject: [PATCH] replace with single function ResetPassword() --- PLC/Methods/InitiateResetPassword.py | 70 ---------------- PLC/Methods/ResetPassword.py | 117 +++++++++++++++++++++++++++ PLC/Methods/VerifyPerson.py | 57 ------------- 3 files changed, 117 insertions(+), 127 deletions(-) delete mode 100644 PLC/Methods/InitiateResetPassword.py create mode 100644 PLC/Methods/ResetPassword.py delete mode 100644 PLC/Methods/VerifyPerson.py diff --git a/PLC/Methods/InitiateResetPassword.py b/PLC/Methods/InitiateResetPassword.py deleted file mode 100644 index 62769bc..0000000 --- a/PLC/Methods/InitiateResetPassword.py +++ /dev/null @@ -1,70 +0,0 @@ -from PLC.Faults import * -from PLC.Method import Method -from PLC.Parameter import Parameter, Mixed -from PLC.Persons import Person, Persons -from PLC.Messages import Message, Messages -from PLC.Auth import AnonymousAuth - -import os -import time -from random import Random -import string - -def create_random_string(): - """ - create and return a random string. - """ - random = Random() - pool = string.letters + string.digits - key = [random.choice(pool) for i in range(32)] - random.shuffle(key) - key = ''.join(key) - - return key - -class InitiateResetPassword(Method): - """ - start the reset password procedure. this sends the user an email - they can use to go to the web interface to finish the reset of their - password. - - the password is not modified yet. A random link to a password reset page - is created, and set to expire in 24 hours. - - Returns 1 if successful, faults otherwise. - """ - - roles = ['admin', 'pi', 'user', 'tech'] - - accepts = [ - AnonymousAuth(), - Mixed(Person.fields['person_id'], - Person.fields['email']) - ] - - returns = Parameter(int, '1 if successful') - - def call(self, auth, person_id_or_email): - - # Get account information - persons = Persons(self.api, [person_id_or_email]) - if not persons: - raise PLCInvalidArgument, "No such account" - - # update the verification key for this person in the db - person = persons[0] - verification_key = create_random_string() - person['verification_key'] = verification_key - person['verification_expires'] = \ - time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(time.time()+86400)) - person.sync() - - # send notification email - person.send_initiate_password_reset_email() - - # Logging variables - self.object_ids = [person['person_id']] - self.message = 'Initiated password reset for person %d.' % \ - (person['person_id']) - - return 1 diff --git a/PLC/Methods/ResetPassword.py b/PLC/Methods/ResetPassword.py new file mode 100644 index 0000000..2428245 --- /dev/null +++ b/PLC/Methods/ResetPassword.py @@ -0,0 +1,117 @@ +import random +import base64 +import time +import urllib + +from PLC.Debug import log +from PLC.Faults import * +from PLC.Method import Method +from PLC.Parameter import Parameter, Mixed +from PLC.Persons import Person, Persons +from PLC.Messages import Message, Messages +from PLC.Auth import Auth +from PLC.sendmail import sendmail + +class ResetPassword(Method): + """ + If verification_key is not specified, then a new verification_key + will be generated and stored with the user's account. The key will + be e-mailed to the user in the form of a link to a web page. + + The web page should verify the key by calling this function again + and specifying verification_key. If the key matches what has been + stored in the user's account, a new random password will be + e-mailed to the user. + + Returns 1 if verification_key was not specified, or was specified + and is valid, faults otherwise. + """ + + roles = ['admin'] + + accepts = [ + Auth(), + Mixed(Person.fields['person_id'], + Person.fields['email']), + Person.fields['verification_key'], + Person.fields['verification_expires'] + ] + + returns = Parameter(int, '1 if verification_key is valid') + + def call(self, auth, person_id_or_email, verification_key = None, verification_expires = None): + # Get account information + persons = Persons(self.api, [person_id_or_email]) + if not persons: + raise PLCInvalidArgument, "No such account" + person = persons[0] + + if person['peer_id'] is not None: + raise PLCInvalidArgument, "Not a local account" + + # Be paranoid and deny password resets for admins + if 'admin' in person['roles']: + raise PLCInvalidArgument, "Cannot reset admin passwords" + + # Generate 32 random bytes + bytes = random.sample(xrange(0, 256), 32) + # Base64 encode their string representation + random_key = base64.b64encode("".join(map(chr, bytes))) + + if verification_key is not None: + if person['verification_key'] is None or \ + person['verification_expires'] is None or \ + person['verification_expires'] < time.time(): + raise PLCPermissionDenied, "Verification key has expired" + elif person['verification_key'] != verification_key: + raise PLCPermissionDenied, "Verification key incorrect" + else: + # Reset password to random string + person['password'] = random_key + person['verification_key'] = None + person['verification_expires'] = None + person.sync() + + message_id = 'Password reset' + else: + # Only allow one reset at a time + if person['verification_expires'] is not None and \ + person['verification_expires'] > time.time(): + raise PLCPermissionDenied, "Password reset request already pending" + + if verification_expires is None: + verification_expires = int(time.time() + (24 * 60 * 60)) + + person['verification_key'] = random_key + person['verification_expires'] = verification_expires + person.sync() + + message_id = 'Password reset requested' + + messages = Messages(self.api, [message_id]) + if messages: + # Send password to user + message = messages[0] + + params = {'PLC_NAME': self.api.config.PLC_NAME, + 'PLC_MAIL_SUPPORT_ADDRESS': self.api.config.PLC_MAIL_SUPPORT_ADDRESS, + 'PLC_WWW_HOST': self.api.config.PLC_WWW_HOST, + 'PLC_WWW_SSL_PORT': self.api.config.PLC_WWW_PORT, + 'person_id': person['person_id'], + # Will be used in a URL, so must quote appropriately + 'verification_key': urllib.quote_plus(random_key), + 'password': random_key, + 'email': person['email']} + + sendmail(self.api, + To = "%s %s <%s>" % (person['first_name'], person['last_name'], person['email']), + Subject = message['subject'], + Body = message['template'] % params) + else: + print >> log, "Warning: No message template '%s'" % message-id + + # Logging variables + self.object_ids = [person['person_id']] + self.message = message_id + + return 1 diff --git a/PLC/Methods/VerifyPerson.py b/PLC/Methods/VerifyPerson.py deleted file mode 100644 index b238d77..0000000 --- a/PLC/Methods/VerifyPerson.py +++ /dev/null @@ -1,57 +0,0 @@ -from PLC.Faults import * -from PLC.Method import Method -from PLC.Parameter import Parameter, Mixed -from PLC.Persons import Person, Persons -from PLC.Messages import Message, Messages -from PLC.Auth import AnonymousAuth - -import time - -class VerifyPerson(Method): - """ - Check that the verification_key is valid for a specified person - and not expired. - - Returns 1 if the verification key if valid. - """ - - roles = ['admin', 'pi', 'user', 'tech'] - - accepts = [ - AnonymousAuth(), - Mixed(Person.fields['person_id'], - Person.fields['email']), - Person.fields['verification_key'] - ] - - returns = Parameter(int, '1 if verification_key is valid') - - def call(self, auth, person_id_or_email, verification_key): - - # Get account information - persons = Persons(self.api, [person_id_or_email]) - if not persons: - raise PLCInvalidArgument, "No such account" - - person = persons[0] - - # make sure verification key matches - if not person['verification_key']: - raise PLCInvalidArgument, "Invalid key" - if person['verification_key'] != verification_key: - raise PLCInvalidArgument, "Invalid key" - - # make sure key is not expired - if not person['verification_expires']: - raise PLCInvalidArgument, "Invalid key" - expires = str(person['verification_expires']) - if time.strptime(expires, "%Y-%m-%d %H:%M:%S") < \ - time.gmtime(time.time()): - raise PLCInvalidArgument, "Invalid key" - - # Logging variables - self.object_ids = [person['person_id']] - self.message = 'Verification key check on perons %d.' % \ - (person['person_id']) - - return 1 -- 2.43.0