6 from PLC.Debug import log
7 from PLC.Faults import *
8 from PLC.Method import Method
9 from PLC.Parameter import Parameter, Mixed
10 from PLC.Persons import Person, Persons
11 from PLC.Messages import Message, Messages
12 from PLC.Auth import Auth
13 from PLC.sendmail import sendmail
15 class ResetPassword(Method):
17 If verification_key is not specified, then a new verification_key
18 will be generated and stored with the user's account. The key will
19 be e-mailed to the user in the form of a link to a web page.
21 The web page should verify the key by calling this function again
22 and specifying verification_key. If the key matches what has been
23 stored in the user's account, a new random password will be
26 Returns 1 if verification_key was not specified, or was specified
27 and is valid, faults otherwise.
34 Mixed(Person.fields['person_id'],
35 Person.fields['email']),
36 Person.fields['verification_key'],
37 Person.fields['verification_expires']
40 returns = Parameter(int, '1 if verification_key is valid')
42 def call(self, auth, person_id_or_email, verification_key = None, verification_expires = None):
43 # Get account information
44 persons = Persons(self.api, [person_id_or_email])
46 raise PLCInvalidArgument, "No such account"
49 if person['peer_id'] is not None:
50 raise PLCInvalidArgument, "Not a local account"
52 # Be paranoid and deny password resets for admins
53 if 'admin' in person['roles']:
54 raise PLCInvalidArgument, "Cannot reset admin passwords"
56 # Generate 32 random bytes
57 bytes = random.sample(xrange(0, 256), 32)
58 # Base64 encode their string representation
59 random_key = base64.b64encode("".join(map(chr, bytes)))
61 if verification_key is not None:
62 if person['verification_key'] is None or \
63 person['verification_expires'] is None or \
64 person['verification_expires'] < time.time():
65 raise PLCPermissionDenied, "Verification key has expired"
66 elif person['verification_key'] != verification_key:
67 raise PLCPermissionDenied, "Verification key incorrect"
69 # Reset password to random string
70 person['password'] = random_key
71 person['verification_key'] = None
72 person['verification_expires'] = None
75 message_id = 'Password reset'
77 # Only allow one reset at a time
78 if person['verification_expires'] is not None and \
79 person['verification_expires'] > time.time():
80 raise PLCPermissionDenied, "Password reset request already pending"
82 if verification_expires is None:
83 verification_expires = int(time.time() + (24 * 60 * 60))
85 person['verification_key'] = random_key
86 person['verification_expires'] = verification_expires
89 message_id = 'Password reset requested'
91 messages = Messages(self.api, [message_id])
93 # Send password to user
96 params = {'PLC_NAME': self.api.config.PLC_NAME,
97 'PLC_MAIL_SUPPORT_ADDRESS': self.api.config.PLC_MAIL_SUPPORT_ADDRESS,
98 'PLC_WWW_HOST': self.api.config.PLC_WWW_HOST,
99 'PLC_WWW_SSL_PORT': self.api.config.PLC_WWW_PORT,
100 'person_id': person['person_id'],
101 # Will be used in a URL, so must quote appropriately
102 'verification_key': urllib.quote_plus(random_key),
103 'password': random_key,
104 'email': person['email']}
107 To = "%s %s <%s>" % (person['first_name'], person['last_name'], person['email']),
108 Subject = message['subject'],
109 Body = message['template'] % params)
111 print >> log, "Warning: No message template '%s'" % message-id
114 self.object_ids = [person['person_id']]
115 self.message = message_id