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.Messages import Message, Messages
19 from PLC.Roles import Role, Roles
20 from PLC.Keys import Key, Keys
21 from PLC.Storage.AlchemyObject import AlchemyObj
23 class Person(AlchemyObj):
25 Representation of a row in the persons table. To use, optionally
26 instantiate with a dict of values. Update as you would a
27 dict. Commit to the database with sync().
33 'person_id': Parameter(int, "User identifier", primary_key=True),
34 'keystone_id': Parameter(int, "Keystone User identifier"),
35 'first_name': Parameter(str, "Given name", max = 128),
36 'last_name': Parameter(str, "Surname", max = 128),
37 'title': Parameter(str, "Title", max = 128, nullok = True),
38 'email': Parameter(str, "Primary e-mail address", max = 254),
39 'phone': Parameter(str, "Telephone number", max = 64, nullok = True),
40 'url': Parameter(str, "Home page", max = 254, nullok = True),
41 'bio': Parameter(str, "Biography", max = 254, nullok = True),
42 'enabled': Parameter(bool, "Has been enabled"),
43 'password': Parameter(str, "Account password in crypt() form", max = 254),
44 'verification_key': Parameter(str, "Reset password key", max = 254, nullok = True),
45 'verification_expires': Parameter(int, "Date and time when verification_key expires", nullok = True),
46 'last_updated': Parameter(int, "Date and time of last update", ro = True),
47 'date_created': Parameter(int, "Date and time when account was created", ro = True),
48 'role_ids': Parameter([int], "List of role identifiers", joined=True),
49 'roles': Parameter([str], "List of roles", joined=True),
50 'site_ids': Parameter([int], "List of site identifiers", joined=True),
51 'key_ids': Parameter([int], "List of key identifiers", joined=True),
52 'slice_ids': Parameter([int], "List of slice identifiers", joined=True),
53 'peer_id': Parameter(int, "Peer to which this user belongs", nullok = True),
54 'peer_person_id': Parameter(int, "Foreign user identifier at peer", nullok = True),
55 'person_tag_ids' : Parameter ([int], "List of tags attached to this person", joined=True),
58 def validate_email(self, email):
60 Validate email address. Stolen from Mailman.
63 invalid_email = PLCInvalidArgument("Invalid e-mail address")
68 email_re = re.compile('\A[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9._\-]+\.[a-zA-Z]+\Z')
69 if not email_re.match(email):
74 def can_update(self, person):
76 Returns true if we can update the specified person. We can
81 3. We are a PI and the person is a user or tech or at
85 assert isinstance(person, Person)
87 if self.person_id == person.person_id:
90 if 'admin' in self['roles']:
93 if 'pi' in self['roles']:
94 if set(self['site_ids']).intersection(person['site_ids']):
95 # non-admin users cannot update a person who is neither a PI or ADMIN
96 return (not set(['pi','admin']).intersection(person['roles']))
100 def can_view(self, person):
102 Returns true if we can view the specified person. We can
105 1. We are the person.
107 3. We are a PI or Tech and the person is at one of our sites.
110 assert isinstance(person, Person)
112 if self.can_update(person):
115 # pis and techs can see all people on their site
116 if set(['pi','tech']).intersection(self['roles']):
117 if set(self['site_ids']).intersection(person['site_ids']):
122 def add_role(self, role_name, login_base=None):
123 user = self.api.client_shell.keystone.users.find(id=self['keystone_id'])
124 roles = Roles(self.api, {'name': role_name})
126 raise PLCInvalidArgument, "Role %s not found" % role_name
130 tenant = self.api.client_shell.keystone.tenants.find(name=login_base)
132 tenant = self.api.client_shell.keystone.tenants.find(id=self['tenantId'])
134 self.api.client_shell.keystone.roles.add_user_role(user, role, tenant)
136 def remove_role(self, role_name, login_base=None):
137 user = self.api.client_shell.keystone.users.find(id=self['keystone_id'])
138 roles = Roles(self.api, {'name': role_name})
140 raise PLCInvalidArgument, "Role %s not found" % role_name
144 tenant = self.api.client_shell.keystone.tenants.find(name=login_base)
146 tenant = self.api.client_shell.keystone.tenants.find(id=self['tenantId'])
148 self.api.client_shell.keystone.roles.remove_user_role(user, role, tenant)
151 #add_key = Row.add_object(Key, 'person_key')
152 #remove_key = Row.remove_object(Key, 'person_key')
154 def sync(self, commit=True, validate=True):
155 AlchemyObj.sync(self, commit=commit, validate=validate)
156 nova_fields = ['enabled', 'email', 'password']
157 nova_can_update = lambda (field, value): field in nova_fields
158 nova_person = dict(filter(nova_can_update, self.items()))
159 nova_person['name'] = "%s %s" % (self.get('first_name', ''), self.get('last_name', ''))
160 if 'person_id' not in self:
161 self.object = self.api.client_shell.keystone.users.create(**self)
162 self['keystone_id'] = self.object.id
163 AlchemyObj.insert(self, dict(self))
165 self.object = self.api.client_shell.keystone.users.update(self['person_id'], nova_person)
166 AlchemyObj.update(self, {'person_id': self['person_id']}, dict(self))
170 assert 'person_id' in self
171 assert 'keystone_id' in self
173 # delete keystone record
174 nova_user = self.api.client_shell.keystone.users.find(id=self['keystone_id'])
175 self.api.client_shell.keystone.users.delete(nova_user)
177 # delete relationships
178 SlicePerson().delete.filter({'person_id': self['person_id']})
181 AlchemyObj.delete(self, dict(self))
184 def get_tenants_ids(self):
187 tenants = [self.tenantId]
190 def get_key_ids(self):
195 Representation of row(s) from the persons table in the
199 def __init__(self, api, person_filter = None, columns = None):
201 if not person_filter:
202 #persons = self.api.client_shell.keystone.users.findall()
203 persons = Person().select()
204 elif isinstance(person_filter, (list, tuple, set)):
205 ints = filter(lambda x: isinstance(x, (int, long)), person_filter)
206 strs = filter(lambda x: isinstance(x, StringTypes), person_filter)
207 person_filter = {'person_id': ints, 'email': strs}
208 persons = Person().select(filter=person_filter)
209 elif isinstance(person_filter, dict):
210 persons = Person().select(filter=person_filter)
211 #persons = self.api.client_shell.keystone.users.findall(**person_filter)
212 elif isinstance (person_filter, StringTypes):
213 persons = Person().select(filter={'email': person_filter})
215 raise PLCInvalidArgument, "Wrong person filter %r"%person_filter
217 for person in persons:
218 keystone_user = self.api.client_shell.keystone.persons.find(id=person['keystone_id'])
219 tenant = self.api.client_shell.keystone.tenants.find(id=keystone_user.tenantId)
220 if not columns or 'roles' in columns:
221 roles = self.api.client_shell.keystone.roles.roles_for_user(keystone_user, tenant)
222 person['roles'] = person.get_roles()
223 if not columns or 'tenant_ids' in columns:
224 person['tenant_ids'] = person.get_tenants_ids()
225 if not columns or 'key_ids' in columns:
226 person['key_ids'] = person.get_keys_ids()
227 if not columns or 'slice_ids' in columns:
228 person_slices = SlicePerson().select(filter={'person_id': person.person_id})
229 person['slice_ids'] = [rec.slice_id for rec in person_slices]