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 object_type = 'Person'
44 def call(self, auth, person_id_or_email, verification_key = None, verification_expires = None):
45 # Get account information
46 persons = Persons(self.api, [person_id_or_email])
48 raise PLCInvalidArgument, "No such account"
51 if person['peer_id'] is not None:
52 raise PLCInvalidArgument, "Not a local account"
54 if not person['enabled']:
55 raise PLCInvalidArgument, "Account must be enabled"
57 # Be paranoid and deny password resets for admins
58 if 'admin' in person['roles']:
59 raise PLCInvalidArgument, "Cannot reset admin passwords"
61 # Generate 32 random bytes
62 bytes = random.sample(xrange(0, 256), 32)
63 # Base64 encode their string representation
64 random_key = base64.b64encode("".join(map(chr, bytes)))
66 if verification_key is not None:
67 if person['verification_key'] is None or \
68 person['verification_expires'] is None or \
69 person['verification_expires'] < time.time():
70 raise PLCPermissionDenied, "Verification key has expired"
71 elif person['verification_key'] != verification_key:
72 raise PLCPermissionDenied, "Verification key incorrect"
74 # Reset password to random string
75 person['password'] = random_key
76 person['verification_key'] = None
77 person['verification_expires'] = None
80 message_id = 'Password reset'
82 # Only allow one reset at a time
83 if person['verification_expires'] is not None and \
84 person['verification_expires'] > time.time():
85 raise PLCPermissionDenied, "Password reset request already pending"
87 if verification_expires is None:
88 verification_expires = int(time.time() + (24 * 60 * 60))
90 person['verification_key'] = random_key
91 person['verification_expires'] = verification_expires
94 message_id = 'Password reset requested'
96 messages = Messages(self.api, [message_id])
98 # Send password to user
101 params = {'PLC_NAME': self.api.config.PLC_NAME,
102 'PLC_MAIL_SUPPORT_ADDRESS': self.api.config.PLC_MAIL_SUPPORT_ADDRESS,
103 'PLC_WWW_HOST': self.api.config.PLC_WWW_HOST,
104 'PLC_WWW_SSL_PORT': self.api.config.PLC_WWW_SSL_PORT,
105 'person_id': person['person_id'],
106 # Will be used in a URL, so must quote appropriately
107 'verification_key': urllib.quote_plus(random_key),
108 'password': random_key,
109 'email': person['email']}
112 To = ("%s %s" % (person['first_name'], person['last_name']), person['email']),
113 Subject = message['subject'] % params,
114 Body = message['template'] % params)
116 print >> log, "Warning: No message template '%s'" % message_id
119 self.object_ids = [person['person_id']]
120 self.message = message_id