+++ /dev/null
-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
--- /dev/null
+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
+++ /dev/null
-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