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 if not person['enabled']:
53 raise PLCInvalidArgument, "Account must be enabled"
55 # Be paranoid and deny password resets for admins
56 if 'admin' in person['roles']:
57 raise PLCInvalidArgument, "Cannot reset admin passwords"
59 # Generate 32 random bytes
60 bytes = random.sample(xrange(0, 256), 32)
61 # Base64 encode their string representation
62 random_key = base64.b64encode("".join(map(chr, bytes)))
64 if verification_key is not None:
65 if person['verification_key'] is None or \
66 person['verification_expires'] is None or \
67 person['verification_expires'] < time.time():
68 raise PLCPermissionDenied, "Verification key has expired"
69 elif person['verification_key'] != verification_key:
70 raise PLCPermissionDenied, "Verification key incorrect"
72 # Reset password to random string
73 person['password'] = random_key
74 person['verification_key'] = None
75 person['verification_expires'] = None
78 message_id = 'Password reset'
80 # Only allow one reset at a time
81 if person['verification_expires'] is not None and \
82 person['verification_expires'] > time.time():
83 raise PLCPermissionDenied, "Password reset request already pending"
85 if verification_expires is None:
86 verification_expires = int(time.time() + (24 * 60 * 60))
88 person['verification_key'] = random_key
89 person['verification_expires'] = verification_expires
92 message_id = 'Password reset requested'
94 messages = Messages(self.api, [message_id])
96 # Send password to user
99 params = {'PLC_NAME': self.api.config.PLC_NAME,
100 'PLC_MAIL_SUPPORT_ADDRESS': self.api.config.PLC_MAIL_SUPPORT_ADDRESS,
101 'PLC_WWW_HOST': self.api.config.PLC_WWW_HOST,
102 'PLC_WWW_SSL_PORT': self.api.config.PLC_WWW_SSL_PORT,
103 'person_id': person['person_id'],
104 # Will be used in a URL, so must quote appropriately
105 'verification_key': urllib.quote_plus(random_key),
106 'password': random_key,
107 'email': person['email']}
110 To = ("%s %s" % (person['first_name'], person['last_name']), person['email']),
111 Subject = message['subject'] % params,
112 Body = message['template'] % params)
114 print >> log, "Warning: No message template '%s'" % message_id
117 self.object_ids = [person['person_id']]
118 self.message = message_id