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