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