- Verify a new (must be disabled) user's e-mail address and registration
[plcapi.git] / PLC / Methods / VerifyPerson.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.Sites import Site, Sites
12 from PLC.Messages import Message, Messages
13 from PLC.Auth import Auth
14 from PLC.sendmail import sendmail
15
16 class VerifyPerson(Method):
17     """
18     Verify a new (must be disabled) user's e-mail address and registration.
19
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.
23
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, then an e-mail will be sent to the
27     user's PI (and support if the user is requesting a PI role),
28     asking the PI (or support) to enable the account.
29
30     Returns 1 if the verification key if valid.
31     """
32
33     roles = ['admin']
34
35     accepts = [
36         Auth(),
37         Mixed(Person.fields['person_id'],
38               Person.fields['email']),
39         Person.fields['verification_key'],
40         Person.fields['verification_expires']
41         ]
42
43     returns = Parameter(int, '1 if verification_key is valid')
44
45     def call(self, auth, person_id_or_email, verification_key = None, verification_expires = None):
46         # Get account information
47         persons = Persons(self.api, [person_id_or_email])
48         if not persons:
49             raise PLCInvalidArgument, "No such account"
50         person = persons[0]
51
52         if person['peer_id'] is not None:
53             raise PLCInvalidArgument, "Not a local account"
54
55         if person['enabled']:
56             raise PLCInvalidArgument, "Account must be new (disabled)"
57
58         # Get the primary site name
59         person_sites = Sites(self.api, person['site_ids'])
60         if person_sites:
61             site_name = person_sites[0]['name']
62         else:
63             site_name = "No Site"
64
65         # Generate 32 random bytes
66         bytes = random.sample(xrange(0, 256), 32)
67         # Base64 encode their string representation
68         random_key = base64.b64encode("".join(map(chr, bytes)))
69
70         if verification_key is not None:
71             if person['verification_key'] is None or \
72                person['verification_expires'] is None or \
73                person['verification_expires'] < time.time():
74                 person.delete()
75                 raise PLCPermissionDenied, "Verification key has expired. Please re-register."
76             elif person['verification_key'] != verification_key:
77                 raise PLCPermissionDenied, "Verification key incorrect"
78             else:
79                 person['verification_key'] = None
80                 person['verification_expires'] = None
81                 person.sync()
82
83                 # Get the PI(s) of each site that the user is registering with
84                 person_ids = set()
85                 for site in person_sites:
86                     person_ids.update(site['person_ids'])
87                 persons = Persons(self.api, person_ids)
88                 pis = filter(lambda person: 'pi' in person['roles'], persons)
89
90                 # Send e-mail to PI(s) and copy the user
91                 To = [("%s %s" % (pi['first_name'], pi['last_name']), pi['email']) for pi in pis]
92                 Cc = ("%s %s" % (person['first_name'], person['last_name']), person['email'])
93
94                 if 'pi' in person['roles']:
95                     # And support if user is requesting a PI role
96                     To.append(("%s Support" % self.api.config.PLC_NAME,
97                                self.api.config.PLC_MAIL_SUPPORT_ADDRESS))
98                     message_id = 'New PI account'
99                 else:
100                     message_id = 'New account'
101         else:
102             # Only allow one verification at a time
103             if person['verification_expires'] is not None and \
104                person['verification_expires'] > time.time():
105                 raise PLCPermissionDenied, "Verification request already pending"
106
107             if verification_expires is None:
108                 verification_expires = int(time.time() + (24 * 60 * 60))
109
110             person['verification_key'] = random_key
111             person['verification_expires'] = verification_expires
112             person.sync()
113
114             # Send e-mail to user
115             To = ("%s %s" % (person['first_name'], person['last_name']), person['email'])
116             Cc = None
117
118             message_id = 'Verify account'
119
120         messages = Messages(self.api, [message_id])
121         if messages:
122             # Send message to user
123             message = messages[0]
124
125             params = {'PLC_NAME': self.api.config.PLC_NAME,
126                       'PLC_MAIL_SUPPORT_ADDRESS': self.api.config.PLC_MAIL_SUPPORT_ADDRESS,
127                       'PLC_WWW_HOST': self.api.config.PLC_WWW_HOST,
128                       'PLC_WWW_SSL_PORT': self.api.config.PLC_WWW_SSL_PORT,
129                       'person_id': person['person_id'],
130                       # Will be used in a URL, so must quote appropriately
131                       'verification_key': urllib.quote_plus(random_key),
132                       'site_name': site_name,
133                       'first_name': person['first_name'],
134                       'last_name': person['last_name'],
135                       'email': person['email'],
136                       'roles': ", ".join(person['roles'])}
137
138             sendmail(self.api,
139                      To = To,
140                      Cc = Cc,
141                      Subject = message['subject'] % params,
142                      Body = message['template'] % params)
143         else:
144             print >> log, "Warning: No message template '%s'" % message_id
145
146         # Logging variables
147         self.object_ids = [person['person_id']]
148         self.message = message_id
149
150         return 1