a0fc304274a9a40f65d27da477730df732f58128
[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 %r"%person_id_or_email
50         person = persons[0]
51
52         if person['peer_id'] is not None:
53             raise PLCInvalidArgument, "Not a local account %r"%person_id_or_email
54
55         if person['enabled']:
56             raise PLCInvalidArgument, "Account %r must be new (disabled)"%person_id_or_email
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 None or \
71         (verification_key is not None and person['verification_expires'] and \
72         person['verification_expires'] < time.time()):
73             # Only allow one verification at a time
74             if person['verification_expires'] is not None and \
75                person['verification_expires'] > time.time():
76                 raise PLCPermissionDenied, "Verification request already pending"
77
78             if verification_expires is None:
79                 verification_expires = int(time.time() + (24 * 60 * 60))
80
81             person['verification_key'] = random_key
82             person['verification_expires'] = verification_expires
83             person.sync()
84
85             # Send e-mail to user
86             To = ("%s %s" % (person['first_name'], person['last_name']), person['email'])
87             Cc = None
88
89             message_id = 'Verify account'
90
91
92         elif verification_key is not None:
93             if person['verification_key'] is None or \
94                person['verification_expires'] is None:
95                 raise PLCPermissionDenied, "Invalid Verification key"
96             elif person['verification_key'] != verification_key:
97                 raise PLCPermissionDenied, "Verification key incorrect"
98             else:
99                 person['verification_key'] = None
100                 person['verification_expires'] = None
101                 person.sync()
102
103                 # Get the PI(s) of each site that the user is registering with
104                 person_ids = set()
105                 for site in person_sites:
106                     person_ids.update(site['person_ids'])
107                 persons = Persons(self.api, person_ids)
108                 pis = filter(lambda person: 'pi' in person['roles'] and person['enabled'], persons)
109
110                 # Send e-mail to PI(s) and copy the user
111                 To = [("%s %s" % (pi['first_name'], pi['last_name']), pi['email']) for pi in pis]
112                 Cc = ("%s %s" % (person['first_name'], person['last_name']), person['email'])
113
114                 if 'pi' in person['roles']:
115                     # And support if user is requesting a PI role
116                     To.append(("%s Support" % self.api.config.PLC_NAME,
117                                self.api.config.PLC_MAIL_SUPPORT_ADDRESS))
118                     message_id = 'New PI account'
119                 else:
120                     message_id = 'New account'
121
122         messages = Messages(self.api, [message_id])
123         if messages:
124             # Send message to user
125             message = messages[0]
126
127             params = {'PLC_NAME': self.api.config.PLC_NAME,
128                       'PLC_MAIL_SUPPORT_ADDRESS': self.api.config.PLC_MAIL_SUPPORT_ADDRESS,
129                       'PLC_WWW_HOST': self.api.config.PLC_WWW_HOST,
130                       'PLC_WWW_SSL_PORT': self.api.config.PLC_WWW_SSL_PORT,
131                       'person_id': person['person_id'],
132                       # Will be used in a URL, so must quote appropriately
133                       'verification_key': urllib.quote_plus(random_key),
134                       'site_name': site_name,
135                       'first_name': person['first_name'],
136                       'last_name': person['last_name'],
137                       'email': person['email'],
138                       'roles': ", ".join(person['roles'])}
139
140             sendmail(self.api,
141                      To = To,
142                      Cc = Cc,
143                      Subject = message['subject'] % params,
144                      Body = message['template'] % params)
145         else:
146             print >> log, "Warning: No message template '%s'" % message_id
147
148         # Logging variables
149         self.event_objects = {'Person': [person['person_id']]}
150         self.message = message_id
151
152         if verification_key is not None and person['verification_expires'] and \
153         person['verification_expires'] < time.time():
154             raise PLCPermissionDenied, "Verification key has expired. Another email has been sent."
155
156         return 1