2 # Functions for interacting with the persons table in the database
5 from types import StringTypes
7 from hashlib import md5
11 from random import Random
15 from PLC.Faults import *
16 from PLC.Debug import log
17 from PLC.Parameter import Parameter, Mixed
18 from PLC.Table import Row, Table
19 from PLC.Roles import Role, Roles
20 from PLC.Keys import Key, Keys
21 from PLC.Messages import Message, Messages
22 from PLC.Storage.AlchemyObj import AlchemyObj
24 class Person(AlchemyObj):
26 Representation of a row in the persons table. To use, optionally
27 instantiate with a dict of values. Update as you would a
28 dict. Commit to the database with sync().
32 'person_id': Parameter(int, "User identifier"),
33 'keystone_id': Parameter(int, "Keystone User identifier"),
34 'first_name': Parameter(str, "Given name", max = 128),
35 'last_name': Parameter(str, "Surname", max = 128),
36 'title': Parameter(str, "Title", max = 128, nullok = True),
37 'email': Parameter(str, "Primary e-mail address", max = 254),
38 'phone': Parameter(str, "Telephone number", max = 64, nullok = True),
39 'url': Parameter(str, "Home page", max = 254, nullok = True),
40 'bio': Parameter(str, "Biography", max = 254, nullok = True),
41 'enabled': Parameter(bool, "Has been enabled"),
42 'password': Parameter(str, "Account password in crypt() form", max = 254),
43 'verification_key': Parameter(str, "Reset password key", max = 254, nullok = True),
44 'verification_expires': Parameter(int, "Date and time when verification_key expires", nullok = True),
45 'last_updated': Parameter(int, "Date and time of last update", ro = True),
46 'date_created': Parameter(int, "Date and time when account was created", ro = True),
47 'role_ids': Parameter([int], "List of role identifiers", joined=True),
48 'roles': Parameter([str], "List of roles", joined=True),
49 'site_ids': Parameter([int], "List of site identifiers", joined=True),
50 'key_ids': Parameter([int], "List of key identifiers", joined=True),
51 'slice_ids': Parameter([int], "List of slice identifiers", joined=True),
52 'peer_id': Parameter(int, "Peer to which this user belongs", nullok = True),
53 'peer_person_id': Parameter(int, "Foreign user identifier at peer", nullok = True),
54 'person_tag_ids' : Parameter ([int], "List of tags attached to this person", joined=True),
57 def validate_email(self, email):
59 Validate email address. Stolen from Mailman.
62 invalid_email = PLCInvalidArgument("Invalid e-mail address")
67 email_re = re.compile('\A[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9._\-]+\.[a-zA-Z]+\Z')
68 if not email_re.match(email):
73 def can_update(self, person):
75 Returns true if we can update the specified person. We can
80 3. We are a PI and the person is a user or tech or at
84 assert isinstance(person, Person)
86 if self.person_id == person.person_id:
89 if 'admin' in self['roles']:
92 if 'pi' in self['roles']:
93 if set(self['tenants']).intersection(person['tenants']):
94 # non-admin users cannot update a person who is neither a PI or ADMIN
95 return (not set(['pi','admin']).intersection(person['roles']))
99 def can_view(self, person):
101 Returns true if we can view the specified person. We can
104 1. We are the person.
106 3. We are a PI or Tech and the person is at one of our sites.
109 assert isinstance(person, Person)
111 if self.can_update(person):
114 # pis and techs can see all people on their site
115 if set(['pi','tech']).intersection(self['roles']):
116 if set(self['site_ids']).intersection(person['site_ids']):
121 #add_role = Row.add_object(Role, 'person_role')
122 #remove_role = Row.remove_object(Role, 'person_role')
124 #add_key = Row.add_object(Key, 'person_key')
125 #remove_key = Row.remove_object(Key, 'person_key')
127 def sync(self, insert=False, validate=True):
128 NovaObject.sync(self, insert, validate)
129 nova_fields = ['enabled', 'email', 'password']
131 nova_can_update = lambda (field, value): field in nova_fields
132 nova_person = dict(filter(nova_can_update, self.items()))
133 nova_person['name'] = "%s %s" % (self.get('first_name', ''), self.get('last_name', ''))
134 if insert == True or 'person_id' not in self:
135 self.object = self.api.client_shell.keystone.users.create(**self)
136 self['keystone_id'] = self.object.id
137 AlchemyObj.insert(self, dict(self))
139 self.object = self.api.client_shell.keystone.users.update(self['person_id'], nova_person)
140 AlchemyObj.update(self, {'person_id': self['person_id']}, dict(self))
144 # delete relationships
145 SlicePerson().delete.filter({'person_id': self['person_id']})
148 user = self.api.client_shell.keystone.users.find(**self)
149 self.api.client_shell.keystone.users.delete(user)
150 AlchemyObj.delete(self, dict(self))
156 roles = self.api.client_shell.keystone.roles.roles_for_user(self.object, self.tenant)
157 return [role.name for role in roles]
159 def get_tenants_ids(self):
162 tenants = [self.tenantId]
165 def get_key_ids(self):
170 Representation of row(s) from the persons table in the
174 def __init__(self, api, person_filter = None, columns = None):
176 if not person_filter:
177 #persons = self.api.client_shell.keystone.users.findall()
178 persons = Person().select()
179 elif isinstance(person_filter, (list, tuple, set)):
180 #persons = self.api.client_shell.keystone.users.findall()
181 #persons = [person for person in persons if person.id in person_filter]
182 ints = filter(lambda x: isinstance(x, (int, long)), person_filter)
183 strs = filter(lambda x: isinstance(x, StringTypes), person_filter)
184 person_filter = {'person_id': ints, 'email': strs}
185 persons = Person().select(filter=person_filter)
186 elif isinstance(person_filter, dict):
187 persons = Person().select(filter=person_filter)
188 #persons = self.api.client_shell.keystone.users.findall(**person_filter)
189 elif isinstance (person_filter, StringTypes):
190 persons = Person().select(filter={'email': person_filter})
192 raise PLCInvalidArgument, "Wrong person filter %r"%person_filter
194 for person in persons:
195 person = Person(self.api, object = person)
198 person.tenant = self.api.client_shell.keystone.tenants.find(id=person.tenantId)
199 if not columns or 'roles' in columns:
200 person['roles'] = person.get_roles()
201 if not columns or 'tenant_ids' in columns:
202 person['tenant_ids'] = person.get_tenants_ids()
203 if not columns or 'key_ids' in columns:
204 person['key_ids'] = person.get_keys_ids()
205 if not columns or 'slice_ids' in columns:
206 person_slices = SlicePerson().select(filter={'person_id': person.person_id})
207 person['slice_ids'] = [rec.slice_id for rec in person_slices]