- added logging variable 'object_type'
[plcapi.git] / PLC / Methods / ResetPassword.py
1 import random
2 import base64
3 import time
4 import urllib
5
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
14
15 class ResetPassword(Method):
16     """
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.
20
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
24     e-mailed to the user.
25
26     Returns 1 if verification_key was not specified, or was specified
27     and is valid, faults otherwise.
28     """
29
30     roles = ['admin']
31
32     accepts = [
33         Auth(),
34         Mixed(Person.fields['person_id'],
35               Person.fields['email']),
36         Person.fields['verification_key'],
37         Person.fields['verification_expires']
38         ]
39
40     returns = Parameter(int, '1 if verification_key is valid')
41
42     object_type = 'Person'
43
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])
47         if not persons:
48             raise PLCInvalidArgument, "No such account"
49         person = persons[0]
50
51         if person['peer_id'] is not None:
52             raise PLCInvalidArgument, "Not a local account"
53
54         if not person['enabled']:
55             raise PLCInvalidArgument, "Account must be enabled"
56
57         # Be paranoid and deny password resets for admins
58         if 'admin' in person['roles']:
59             raise PLCInvalidArgument, "Cannot reset admin passwords"
60
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)))
65
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"
73             else:
74                 # Reset password to random string
75                 person['password'] = random_key
76                 person['verification_key'] = None
77                 person['verification_expires'] = None
78                 person.sync()
79
80                 message_id = 'Password reset'
81         else:
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"
86
87             if verification_expires is None:
88                 verification_expires = int(time.time() + (24 * 60 * 60))
89
90             person['verification_key'] = random_key
91             person['verification_expires'] = verification_expires
92             person.sync()
93
94             message_id = 'Password reset requested'
95
96         messages = Messages(self.api, [message_id])
97         if messages:
98             # Send password to user
99             message = messages[0]
100
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']}
110
111             sendmail(self.api,
112                      To = ("%s %s" % (person['first_name'], person['last_name']), person['email']),
113                      Subject = message['subject'] % params,
114                      Body = message['template'] % params)
115         else:
116             print >> log, "Warning: No message template '%s'" % message_id
117
118         # Logging variables
119         self.object_ids = [person['person_id']]
120         self.message = message_id
121
122         return 1