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.AlchemyObject 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().
34 'person_id': Parameter(int, "User identifier", primary_key=True),
35 'keystone_id': Parameter(int, "Keystone User identifier"),
36 'first_name': Parameter(str, "Given name", max = 128),
37 'last_name': Parameter(str, "Surname", max = 128),
38 'title': Parameter(str, "Title", max = 128, nullok = True),
39 'email': Parameter(str, "Primary e-mail address", max = 254),
40 'phone': Parameter(str, "Telephone number", max = 64, nullok = True),
41 'url': Parameter(str, "Home page", max = 254, nullok = True),
42 'bio': Parameter(str, "Biography", max = 254, nullok = True),
43 'enabled': Parameter(bool, "Has been enabled"),
44 'password': Parameter(str, "Account password in crypt() form", max = 254),
45 'verification_key': Parameter(str, "Reset password key", max = 254, nullok = True),
46 'verification_expires': Parameter(int, "Date and time when verification_key expires", nullok = True),
47 'last_updated': Parameter(int, "Date and time of last update", ro = True),
48 'date_created': Parameter(int, "Date and time when account was created", ro = True),
49 'role_ids': Parameter([int], "List of role identifiers", joined=True),
50 'roles': Parameter([str], "List of roles", joined=True),
51 'site_ids': Parameter([int], "List of site identifiers", joined=True),
52 'key_ids': Parameter([int], "List of key identifiers", joined=True),
53 'slice_ids': Parameter([int], "List of slice identifiers", joined=True),
54 'peer_id': Parameter(int, "Peer to which this user belongs", nullok = True),
55 'peer_person_id': Parameter(int, "Foreign user identifier at peer", nullok = True),
56 'person_tag_ids' : Parameter ([int], "List of tags attached to this person", joined=True),
59 def validate_email(self, email):
61 Validate email address. Stolen from Mailman.
64 invalid_email = PLCInvalidArgument("Invalid e-mail address")
69 email_re = re.compile('\A[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9._\-]+\.[a-zA-Z]+\Z')
70 if not email_re.match(email):
75 def can_update(self, person):
77 Returns true if we can update the specified person. We can
82 3. We are a PI and the person is a user or tech or at
86 assert isinstance(person, Person)
88 if self.person_id == person.person_id:
91 if 'admin' in self['roles']:
94 if 'pi' in self['roles']:
95 if set(self['site_ids']).intersection(person['site_ids']):
96 # non-admin users cannot update a person who is neither a PI or ADMIN
97 return (not set(['pi','admin']).intersection(person['roles']))
101 def can_view(self, person):
103 Returns true if we can view the specified person. We can
106 1. We are the person.
108 3. We are a PI or Tech and the person is at one of our sites.
111 assert isinstance(person, Person)
113 if self.can_update(person):
116 # pis and techs can see all people on their site
117 if set(['pi','tech']).intersection(self['roles']):
118 if set(self['site_ids']).intersection(person['site_ids']):
123 #add_role = Row.add_object(Role, 'person_role')
124 #remove_role = Row.remove_object(Role, 'person_role')
126 #add_key = Row.add_object(Key, 'person_key')
127 #remove_key = Row.remove_object(Key, 'person_key')
129 def sync(self, commit=True, validate=True):
130 AlchemyObj.sync(self, commit=commit, validate=validate)
131 nova_fields = ['enabled', 'email', 'password']
132 nova_can_update = lambda (field, value): field in nova_fields
133 nova_person = dict(filter(nova_can_update, self.items()))
134 nova_person['name'] = "%s %s" % (self.get('first_name', ''), self.get('last_name', ''))
135 if 'person_id' not in self:
136 self.object = self.api.client_shell.keystone.users.create(**self)
137 self['keystone_id'] = self.object.id
138 AlchemyObj.insert(self, dict(self))
140 self.object = self.api.client_shell.keystone.users.update(self['person_id'], nova_person)
141 AlchemyObj.update(self, {'person_id': self['person_id']}, dict(self))
145 assert 'person_id' in self
146 assert 'keystone_id' in self
148 # delete keystone record
149 nova_user = self.api.client_shell.keystone.users.find(id=self['keystone_id'])
150 self.api.client_shell.keystone.users.delete(nova_user)
152 # delete relationships
153 SlicePerson().delete.filter({'person_id': self['person_id']})
156 AlchemyObj.delete(self, dict(self))
162 roles = self.api.client_shell.keystone.roles.roles_for_user(self.object, self.tenant)
163 return [role.name for role in roles]
165 def get_tenants_ids(self):
168 tenants = [self.tenantId]
171 def get_key_ids(self):
176 Representation of row(s) from the persons table in the
180 def __init__(self, api, person_filter = None, columns = None):
182 if not person_filter:
183 #persons = self.api.client_shell.keystone.users.findall()
184 persons = Person().select()
185 elif isinstance(person_filter, (list, tuple, set)):
186 #persons = self.api.client_shell.keystone.users.findall()
187 #persons = [person for person in persons if person.id in person_filter]
188 ints = filter(lambda x: isinstance(x, (int, long)), person_filter)
189 strs = filter(lambda x: isinstance(x, StringTypes), person_filter)
190 person_filter = {'person_id': ints, 'email': strs}
191 persons = Person().select(filter=person_filter)
192 elif isinstance(person_filter, dict):
193 persons = Person().select(filter=person_filter)
194 #persons = self.api.client_shell.keystone.users.findall(**person_filter)
195 elif isinstance (person_filter, StringTypes):
196 persons = Person().select(filter={'email': person_filter})
198 raise PLCInvalidArgument, "Wrong person filter %r"%person_filter
200 for person in persons:
201 keystone_user = self.api.client_shell.keystone.persons.find(id=person['keystone_id'])
202 tenant = self.api.client_shell.keystone.tenants.find(id=keystone_user.tenantId)
203 if not columns or 'roles' in columns:
204 roles = self.api.client_shell.keystone.roles.roles_for_user(keystone_user, tenant)
205 person['roles'] = person.get_roles()
206 if not columns or 'tenant_ids' in columns:
207 person['tenant_ids'] = person.get_tenants_ids()
208 if not columns or 'key_ids' in columns:
209 person['key_ids'] = person.get_keys_ids()
210 if not columns or 'slice_ids' in columns:
211 person_slices = SlicePerson().select(filter={'person_id': person.person_id})
212 person['slice_ids'] = [rec.slice_id for rec in person_slices]