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['tenants']).intersection(person['tenants']):
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, insert=False, validate=True):
130 NovaObject.sync(self, insert, validate)
131 nova_fields = ['enabled', 'email', 'password']
133 nova_can_update = lambda (field, value): field in nova_fields
134 nova_person = dict(filter(nova_can_update, self.items()))
135 nova_person['name'] = "%s %s" % (self.get('first_name', ''), self.get('last_name', ''))
136 if insert == True or 'person_id' not in self:
137 self.object = self.api.client_shell.keystone.users.create(**self)
138 self['keystone_id'] = self.object.id
139 AlchemyObj.insert(self, dict(self))
141 self.object = self.api.client_shell.keystone.users.update(self['person_id'], nova_person)
142 AlchemyObj.update(self, {'person_id': self['person_id']}, dict(self))
146 assert 'person_id' in self
147 # delete relationships
148 SlicePerson().delete.filter({'person_id': self['person_id']})
151 user = self.api.client_shell.keystone.users.find(**self)
152 self.api.client_shell.keystone.users.delete(user)
153 AlchemyObj.delete(self, dict(self))
159 roles = self.api.client_shell.keystone.roles.roles_for_user(self.object, self.tenant)
160 return [role.name for role in roles]
162 def get_tenants_ids(self):
165 tenants = [self.tenantId]
168 def get_key_ids(self):
173 Representation of row(s) from the persons table in the
177 def __init__(self, api, person_filter = None, columns = None):
179 if not person_filter:
180 #persons = self.api.client_shell.keystone.users.findall()
181 persons = Person().select()
182 elif isinstance(person_filter, (list, tuple, set)):
183 #persons = self.api.client_shell.keystone.users.findall()
184 #persons = [person for person in persons if person.id in person_filter]
185 ints = filter(lambda x: isinstance(x, (int, long)), person_filter)
186 strs = filter(lambda x: isinstance(x, StringTypes), person_filter)
187 person_filter = {'person_id': ints, 'email': strs}
188 persons = Person().select(filter=person_filter)
189 elif isinstance(person_filter, dict):
190 persons = Person().select(filter=person_filter)
191 #persons = self.api.client_shell.keystone.users.findall(**person_filter)
192 elif isinstance (person_filter, StringTypes):
193 persons = Person().select(filter={'email': person_filter})
195 raise PLCInvalidArgument, "Wrong person filter %r"%person_filter
197 for person in persons:
198 person = Person(self.api, object = person)
201 person.tenant = self.api.client_shell.keystone.tenants.find(id=person.tenantId)
202 if not columns or 'roles' in columns:
203 person['roles'] = person.get_roles()
204 if not columns or 'tenant_ids' in columns:
205 person['tenant_ids'] = person.get_tenants_ids()
206 if not columns or 'key_ids' in columns:
207 person['key_ids'] = person.get_keys_ids()
208 if not columns or 'slice_ids' in columns:
209 person_slices = SlicePerson().select(filter={'person_id': person.person_id})
210 person['slice_ids'] = [rec.slice_id for rec in person_slices]