8 from types import StringTypes
10 from PLC.Debug import log
11 from PLC.Faults import *
12 from PLC.Method import Method
13 from PLC.Parameter import Parameter, Mixed
14 from PLC.Persons import Person, Persons
15 from PLC.Messages import Message, Messages
16 from PLC.Auth import Auth
17 from PLC.sendmail import sendmail
19 class ResetPassword(Method):
21 If verification_key is not specified, then a new verification_key
22 will be generated and stored with the user's account. The key will
23 be e-mailed to the user in the form of a link to a web page.
25 The web page should verify the key by calling this function again
26 and specifying verification_key. If the key matches what has been
27 stored in the user's account, a new random password will be
30 Returns 1 if verification_key was not specified, or was specified
31 and is valid, faults otherwise.
38 Mixed(Person.fields['person_id'],
39 Person.fields['email']),
40 Person.fields['verification_key'],
41 Person.fields['verification_expires']
44 returns = Parameter(int, '1 if verification_key is valid')
46 def call(self, auth, person_id_or_email, verification_key = None, verification_expires = None):
47 # Get account information
48 # we need to search in local objects only
49 if isinstance (person_id_or_email,StringTypes):
50 filter={'email':person_id_or_email}
52 filter={'person_id':person_id_or_email}
53 filter['peer_id']=None
54 persons = Persons(self.api, filter)
56 raise PLCInvalidArgument, "No such account"
59 if person['peer_id'] is not None:
60 raise PLCInvalidArgument, "Not a local account"
62 if not person['enabled']:
63 raise PLCInvalidArgument, "Account must be enabled"
65 # Be paranoid and deny password resets for admins
66 if 'admin' in person['roles']:
67 raise PLCInvalidArgument, "Cannot reset admin passwords"
69 # Generate 32 random bytes
70 bytes = random.sample(xrange(0, 256), 32)
71 # Base64 encode their string representation
72 random_key = base64.b64encode("".join(map(chr, bytes)))
74 if verification_key is not None:
75 if person['verification_key'] is None or \
76 person['verification_expires'] is None or \
77 person['verification_expires'] < time.time():
78 raise PLCPermissionDenied, "Verification key has expired"
79 elif person['verification_key'] != verification_key:
80 raise PLCPermissionDenied, "Verification key incorrect"
82 # Reset password to random string
83 person['password'] = random_key
84 person['verification_key'] = None
85 person['verification_expires'] = None
88 message_id = 'Password reset'
90 # Only allow one reset at a time
91 if person['verification_expires'] is not None and \
92 person['verification_expires'] > time.time():
93 raise PLCPermissionDenied, "Password reset request already pending"
95 if verification_expires is None:
96 verification_expires = int(time.time() + (24 * 60 * 60))
98 person['verification_key'] = random_key
99 person['verification_expires'] = verification_expires
102 message_id = 'Password reset requested'
104 messages = Messages(self.api, [message_id])
106 # Send password to user
107 message = messages[0]
109 params = {'PLC_NAME': self.api.config.PLC_NAME,
110 'PLC_MAIL_SUPPORT_ADDRESS': self.api.config.PLC_MAIL_SUPPORT_ADDRESS,
111 'PLC_WWW_HOST': self.api.config.PLC_WWW_HOST,
112 'PLC_WWW_SSL_PORT': self.api.config.PLC_WWW_SSL_PORT,
113 'person_id': person['person_id'],
114 # Will be used in a URL, so must quote appropriately
115 'verification_key': urllib.quote_plus(random_key),
116 'password': random_key,
117 'email': person['email']}
120 To = ("%s %s" % (person['first_name'], person['last_name']), person['email']),
121 Subject = message['subject'] % params,
122 Body = message['template'] % params)
124 print >> log, "Warning: No message template '%s'" % message_id
127 self.event_objects = {'Person': [person['person_id']]}
128 self.message = message_id