- import Faults
[plcapi.git] / PLC / Persons.py
index c61e8bd..4261bfc 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Persons.py,v 1.17 2006/11/08 22:45:20 mlhuang Exp $
+# $Id: Persons.py,v 1.28 2007/01/05 19:50:20 tmack Exp $
 #
 
 from types import StringTypes
 #
 
 from types import StringTypes
@@ -16,10 +16,12 @@ import re
 import crypt
 
 from PLC.Faults import *
 import crypt
 
 from PLC.Faults import *
+from PLC.Debug import log
 from PLC.Parameter import Parameter
 from PLC.Filter import Filter
 from PLC.Table import Row, Table
 from PLC.Keys import Key, Keys
 from PLC.Parameter import Parameter
 from PLC.Filter import Filter
 from PLC.Table import Row, Table
 from PLC.Keys import Key, Keys
+from PLC.Messages import Message, Messages
 import PLC.Sites
 
 class Person(Row):
 import PLC.Sites
 
 class Person(Row):
@@ -43,15 +45,32 @@ class Person(Row):
         'bio': Parameter(str, "Biography", max = 254, nullok = True),
         'enabled': Parameter(bool, "Has been enabled"),
         'password': Parameter(str, "Account password in crypt() form", max = 254),
         'bio': Parameter(str, "Biography", max = 254, nullok = True),
         'enabled': Parameter(bool, "Has been enabled"),
         'password': Parameter(str, "Account password in crypt() form", max = 254),
-        'last_updated': Parameter(int, "Date and time of last update", ro = True),
+        'verification_key': Parameter(str, "Reset password key", max = 254),
+       'verification_expires': Parameter(str, "Date/Time when verification_key expires", max = 254),
+       'last_updated': Parameter(int, "Date and time of last update", ro = True),
         'date_created': Parameter(int, "Date and time when account was created", ro = True),
         'role_ids': Parameter([int], "List of role identifiers"),
         'roles': Parameter([str], "List of roles"),
         'site_ids': Parameter([int], "List of site identifiers"),
         'key_ids': Parameter([int], "List of key identifiers"),
         'slice_ids': Parameter([int], "List of slice identifiers"),
         'date_created': Parameter(int, "Date and time when account was created", ro = True),
         'role_ids': Parameter([int], "List of role identifiers"),
         'roles': Parameter([str], "List of roles"),
         'site_ids': Parameter([int], "List of site identifiers"),
         'key_ids': Parameter([int], "List of key identifiers"),
         'slice_ids': Parameter([int], "List of slice identifiers"),
+        'peer_id': Parameter(int, "Peer at which this slice was created", nullok = True),
         }
 
         }
 
+    # for Cache
+    class_key = 'email'
+    foreign_fields = ['first_name', 'last_name', 'title', 'email', 'phone', 'url',
+                     'bio', 'enabled', 'password', ]
+    # forget about these ones, they are read-only anyway
+    # handling them causes Cache to re-sync all over again 
+    # 'last_updated', 'date_created'
+    foreign_xrefs = [
+        {'field' : 'key_ids',  'class': 'Key',  'table' : 'person_key' } ,
+        {'field' : 'site_ids', 'class': 'Site', 'table' : 'person_site'},
+#       xxx this is not handled by Cache yet
+#        'role_ids': Parameter([int], "List of role identifiers"),
+]
+
     def validate_email(self, email):
         """
         Validate email address. Stolen from Mailman.
     def validate_email(self, email):
         """
         Validate email address. Stolen from Mailman.
@@ -81,8 +100,8 @@ class Person(Row):
             raise invalid_email
 
         conflicts = Persons(self.api, [email])
             raise invalid_email
 
         conflicts = Persons(self.api, [email])
-        for person_id, person in conflicts.iteritems():
-            if 'person_id' not in self or self['person_id'] != person_id:
+        for person in conflicts:
+            if 'person_id' not in self or self['person_id'] != person['person_id']:
                 raise PLCInvalidArgument, "E-mail address already in use"
 
         return email
                 raise PLCInvalidArgument, "E-mail address already in use"
 
         return email
@@ -104,6 +123,13 @@ class Person(Row):
             salt = md5.md5(salt).hexdigest()[:8] 
             return crypt.crypt(password.encode(self.api.encoding), magic + salt + "$")
 
             salt = md5.md5(salt).hexdigest()[:8] 
             return crypt.crypt(password.encode(self.api.encoding), magic + salt + "$")
 
+    # timestamps
+    # verification_expires in the DB but not exposed here
+    def validate_date_created (self, timestamp):
+       return self.validate_timestamp (timestamp)
+    def validate_last_updated (self, timestamp):
+       return self.validate_timestamp (timestamp)
+
     def can_update(self, person):
         """
         Returns true if we can update the specified person. We can
     def can_update(self, person):
         """
         Returns true if we can update the specified person. We can
@@ -265,6 +291,71 @@ class Person(Row):
         self['site_ids'].remove(site_id)
         self['site_ids'].insert(0, site_id)
 
         self['site_ids'].remove(site_id)
         self['site_ids'].insert(0, site_id)
 
+    def send_initiate_password_reset_email(self):
+       # email user next step instructions
+        to_addr = {}
+        to_addr[self['email']] = "%s %s" % \
+            (self['first_name'], self['last_name'])
+        from_addr = {}
+        from_addr[self.api.config.PLC_MAIL_SUPPORT_ADDRESS] = \
+        "%s %s" % ('Planetlab', 'Support')
+
+       # fill in template
+        messages = Messages(self.api, ['ASSWORD_RESET_INITIATE'])
+        if not messages:
+            print >> log, "No such message template"
+           return 1
+
+        message = messages[0]
+        subject = message['subject']
+        template = message['template'] % \
+            (self.api.config.PLC_WWW_HOST,
+             self['verification_key'], self['person_id'],
+             self.api.config.PLC_MAIL_SUPPORT_ADDRESS,
+             self.api.config.PLC_WWW_HOST)
+
+        self.api.mailer.mail(to_addr, None, from_addr, subject, template)
+    
+    def send_account_registered_email(self, site):
+       
+       to_addr = {}
+       cc_addr = {}
+       from_addr = {}
+       from_addr[self.api.config.PLC_MAIL_SUPPORT_ADDRESS] = \
+        "%s %s" % ('Planetlab', 'Support')
+
+       # email user
+       user_full_name = "%s %s" % (self['first_name'], self['last_name'])
+       to_addr[self['email']] = "%s" % user_full_name
+
+       # if the account had a admin role or a pi role, email support.
+        if set(['admin', 'pi']).intersection(self['roles']):
+            to_addr[self.api.config.PLC_MAIL_SUPPORT_ADDRESS] = \
+                "%s %s" % ('Planetlab', 'Support')
+       
+       # cc site pi's
+       site_persons = Persons(self.api, site['person_ids'])
+        for person in site_persons:
+            if 'pi' in person['roles'] and not person['email'] in to_addr.keys():
+                cc_addr[person['email']] = "%s %s" % \
+               (person['first_name'], person['last_name'])
+
+       # fill in template
+       messages = Messages(self.api, ['ACCOUNT_REGISTERED'])
+        if not messages:
+           print >> log, "No such message template"
+            return 1
+
+        message = messages[0]
+        subject = message['subject'] % (user_full_name, site['name'])
+        template = message['template'] % \
+           (user_full_name, site['name'], ", ".join(self['roles']),
+            self.api.config.PLC_WWW_HOST, self['person_id'],
+             self.api.config.PLC_MAIL_SUPPORT_ADDRESS,
+             self.api.config.PLC_WWW_HOST)
+                               
+       self.api.mailer.mail(to_addr, cc_addr, from_addr, subject, template)
+
     def delete(self, commit = True):
         """
         Delete existing account.
     def delete(self, commit = True):
         """
         Delete existing account.
@@ -272,7 +363,7 @@ class Person(Row):
 
         # Delete all keys
         keys = Keys(self.api, self['key_ids'])
 
         # Delete all keys
         keys = Keys(self.api, self['key_ids'])
-        for key in keys.values():
+        for key in keys:
             key.delete(commit = False)
 
         # Clean up miscellaneous join tables
             key.delete(commit = False)
 
         # Clean up miscellaneous join tables
@@ -290,11 +381,11 @@ class Persons(Table):
     database.
     """
 
     database.
     """
 
-    def __init__(self, api, person_filter = None):
-        Table.__init__(self, api, Person)
+    def __init__(self, api, person_filter = None, columns = None):
+        Table.__init__(self, api, Person, columns)
 
         sql = "SELECT %s FROM view_persons WHERE deleted IS False" % \
 
         sql = "SELECT %s FROM view_persons WHERE deleted IS False" % \
-              ", ".join(Person.fields)
+              ", ".join(self.columns)
 
         if person_filter is not None:
             if isinstance(person_filter, (list, tuple, set)):
 
         if person_filter is not None:
             if isinstance(person_filter, (list, tuple, set)):
@@ -306,5 +397,4 @@ class Persons(Table):
             elif isinstance(person_filter, dict):
                 person_filter = Filter(Person.fields, person_filter)
                 sql += " AND (%s)" % person_filter.sql(api, "AND")
             elif isinstance(person_filter, dict):
                 person_filter = Filter(Person.fields, person_filter)
                 sql += " AND (%s)" % person_filter.sql(api, "AND")
-
         self.selectall(sql)
         self.selectall(sql)