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