replace with single function ResetPassword()
authorMark Huang <mlhuang@cs.princeton.edu>
Tue, 16 Jan 2007 17:09:19 +0000 (17:09 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Tue, 16 Jan 2007 17:09:19 +0000 (17:09 +0000)
PLC/Methods/InitiateResetPassword.py [deleted file]
PLC/Methods/ResetPassword.py [new file with mode: 0644]
PLC/Methods/VerifyPerson.py [deleted file]

diff --git a/PLC/Methods/InitiateResetPassword.py b/PLC/Methods/InitiateResetPassword.py
deleted file mode 100644 (file)
index 62769bc..0000000
+++ /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 (file)
index 0000000..2428245
--- /dev/null
@@ -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 (file)
index b238d77..0000000
+++ /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