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