From 06ec3f9bcf47398fbc361027e22ac30571550e17 Mon Sep 17 00:00:00 2001 From: Tony Mack Date: Fri, 21 Sep 2012 17:19:59 -0400 Subject: [PATCH] implement GetPersons() --- PLC/API.py | 16 +- PLC/Methods/GetPersons.py | 59 +------ PLC/NovaTable.py | 3 +- PLC/PersonTags.py | 2 +- PLC/Persons.py | 315 ++++---------------------------------- PLC/Sites.py | 4 +- 6 files changed, 45 insertions(+), 354 deletions(-) diff --git a/PLC/API.py b/PLC/API.py index 113d5154..b3b60d66 100644 --- a/PLC/API.py +++ b/PLC/API.py @@ -113,14 +113,14 @@ class PLCAPI: # other_methods_map : dict {methodname: fullpath} # e.g. 'Accessors' -> 'PLC.Accessors.Accessors' other_methods_map={} - for subdir in [ 'Accessors' ]: - path="PLC."+subdir - # scan e.g. PLC.Accessors.__all__ - pkg = __import__(path).__dict__[subdir] - for modulename in getattr(pkg,"__all__"): - fullpath=path+"."+modulename - for method in getattr(import_deep(fullpath),"methods"): - other_methods_map[method] = fullpath + #for subdir in [ 'Accessors' ]: + # path="PLC."+subdir + # # scan e.g. PLC.Accessors.__all__ + # pkg = __import__(path).__dict__[subdir] + # for modulename in getattr(pkg,"__all__"): + # fullpath=path+"."+modulename + # for method in getattr(import_deep(fullpath),"methods"): + # other_methods_map[method] = fullpath all_methods = native_methods + other_methods_map.keys() diff --git a/PLC/Methods/GetPersons.py b/PLC/Methods/GetPersons.py index cb64845c..07a0cd06 100644 --- a/PLC/Methods/GetPersons.py +++ b/PLC/Methods/GetPersons.py @@ -1,9 +1,8 @@ from PLC.Faults import * from PLC.Method import Method -from PLC.Parameter import Parameter, Mixed from PLC.Filter import Filter +from PLC.Parameter import Parameter, Mixed from PLC.Persons import Person, Persons -from PLC.Sites import Site, Sites from PLC.Auth import Auth hidden_fields = ['password', 'verification_key', 'verification_expires'] @@ -25,10 +24,8 @@ class GetPersons(Method): accepts = [ Auth(), - Mixed([Mixed(Person.fields['person_id'], - Person.fields['email'])], + Mixed(Person.fields['id'], Parameter(str,"email"), - Parameter(int,"person_id"), Filter(Person.fields)), Parameter([str], "List of fields to return", nullok = True) ] @@ -39,54 +36,10 @@ class GetPersons(Method): returns = [return_fields] def call(self, auth, person_filter = None, return_fields = None): - # If we are not admin, make sure to only return viewable accounts - if isinstance(self.caller, Person) and \ - 'admin' not in self.caller['roles']: - # Get accounts that we are able to view - valid_person_ids = [self.caller['person_id']] - if ('pi' in self.caller['roles'] or 'tech' in self.caller['roles']) \ - and self.caller['site_ids']: - sites = Sites(self.api, self.caller['site_ids']) - for site in sites: - valid_person_ids += site['person_ids'] - if not valid_person_ids: - return [] - - # this may look suspicious; what if person_filter is not None ? - # turns out the results are getting filtered again below, so we're safe - # although this part of the code does not always trigger, it's probably - # a sensible performance enhancement for all the times - # when GetPersons() gets called without an argument - if person_filter is None: - person_filter = valid_person_ids - - # Filter out password field - if return_fields: - return_fields = filter(lambda field: field not in hidden_fields, - return_fields) - else: - return_fields = self.return_fields.keys() - - # Must query at least person_id, site_ids, and role_ids (see - # Person.can_view() and below). - if return_fields is not None: - added_fields = set(['person_id', 'site_ids', 'role_ids','roles']).difference(return_fields) - return_fields += added_fields - else: - added_fields = [] - persons = Persons(self.api, person_filter, return_fields) # Filter out accounts that are not viewable - if isinstance(self.caller, Person) and \ - 'admin' not in self.caller['roles']: - persons = filter(self.caller.can_view, persons) - - # Remove added fields if not specified - if added_fields: - for person in persons: - for field in added_fields: - if field in person: - del person[field] - - return persons + #if isinstance(self.caller, Person) and \ + # 'admin' not in self.caller['roles']: + # persons = filter(self.caller.can_view, persons) + return persons.dicts() diff --git a/PLC/NovaTable.py b/PLC/NovaTable.py index 1033512b..167758bd 100644 --- a/PLC/NovaTable.py +++ b/PLC/NovaTable.py @@ -1,7 +1,8 @@ from PLC.Logger import logger class NovaObject(dict): - + fields = {} + tags = {} def __init__(self, api, fields = {}, object=None): dict.__init__(self, fields) self.api = api diff --git a/PLC/PersonTags.py b/PLC/PersonTags.py index 9327abf3..4bd38cf1 100644 --- a/PLC/PersonTags.py +++ b/PLC/PersonTags.py @@ -18,7 +18,7 @@ class PersonTag(Row): primary_key = 'person_tag_id' fields = { 'person_tag_id': Parameter(int, "Person setting identifier"), - 'person_id': Person.fields['person_id'], + 'person_id': Person.fields['id'], 'email': Person.fields['email'], 'tag_type_id': TagType.fields['tag_type_id'], 'tagname': TagType.fields['tagname'], diff --git a/PLC/Persons.py b/PLC/Persons.py index d4a3f570..a0e6340b 100644 --- a/PLC/Persons.py +++ b/PLC/Persons.py @@ -1,9 +1,6 @@ # # Functions for interacting with the persons table in the database # -# Mark Huang -# Copyright (C) 2006 The Trustees of Princeton University -# from types import StringTypes try: @@ -18,60 +15,32 @@ import crypt from PLC.Faults import * from PLC.Debug import log from PLC.Parameter import Parameter, Mixed -from PLC.Filter import Filter from PLC.Table import Row, Table from PLC.Roles import Role, Roles from PLC.Keys import Key, Keys from PLC.Messages import Message, Messages +from PLC.NovaTable import NovaObject, NovaTable -class Person(Row): +class Person(NovaObject): """ Representation of a row in the persons table. To use, optionally instantiate with a dict of values. Update as you would a dict. Commit to the database with sync(). """ - table_name = 'persons' - primary_key = 'person_id' - join_tables = ['person_key', 'person_role', 'person_site', 'slice_person', 'person_session', 'peer_person'] fields = { - 'person_id': Parameter(int, "User identifier"), - 'first_name': Parameter(str, "Given name", max = 128), - 'last_name': Parameter(str, "Surname", max = 128), - 'title': Parameter(str, "Title", max = 128, nullok = True), + 'id': Parameter(str, "User identifier"), + 'name': Parameter(str, "Given name", max = 128), 'email': Parameter(str, "Primary e-mail address", max = 254), - 'phone': Parameter(str, "Telephone number", max = 64, nullok = True), - 'url': Parameter(str, "Home page", max = 254, nullok = True), - 'bio': Parameter(str, "Biography", max = 254, nullok = True), 'enabled': Parameter(bool, "Has been enabled"), 'password': Parameter(str, "Account password in crypt() form", max = 254), - 'verification_key': Parameter(str, "Reset password key", max = 254, nullok = True), - 'verification_expires': Parameter(int, "Date and time when verification_key expires", nullok = True), '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"), - 'peer_id': Parameter(int, "Peer to which this user belongs", nullok = True), - 'peer_person_id': Parameter(int, "Foreign user identifier at peer", nullok = True), - 'person_tag_ids' : Parameter ([int], "List of tags attached to this person"), - } - related_fields = { - 'roles': [Mixed(Parameter(int, "Role identifier"), - Parameter(str, "Role name"))], - 'sites': [Mixed(Parameter(int, "Site identifier"), - Parameter(str, "Site name"))], - 'keys': [Mixed(Parameter(int, "Key identifier"), - Filter(Key.fields))], - 'slices': [Mixed(Parameter(int, "Slice identifier"), - Parameter(str, "Slice name"))] } - view_tags_name = "view_person_tags" - # tags are used by the Add/Get/Update methods to expose tags - # this is initialized here and updated by the accessors factory - tags = { } def validate_email(self, email): """ @@ -87,41 +56,8 @@ class Person(Row): if not email_re.match(email): raise invalid_email - # check only against users on the same peer - if 'peer_id' in self: - namespace_peer_id = self['peer_id'] - else: - namespace_peer_id = None - - conflicts = Persons(self.api, {'email':email,'peer_id':namespace_peer_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 - def validate_password(self, password): - """ - Encrypt password if necessary before committing to the - database. - """ - - magic = "$1$" - - if len(password) > len(magic) and \ - password[0:len(magic)] == magic: - return password - else: - # Generate a somewhat unique 8 character salt string - salt = str(time.time()) + str(Random().random()) - salt = md5(salt).hexdigest()[:8] - return crypt.crypt(password.encode(self.api.encoding), magic + salt + "$") - - validate_date_created = Row.validate_timestamp - validate_last_updated = Row.validate_timestamp - validate_verification_expires = Row.validate_timestamp - def can_update(self, person): """ Returns true if we can update the specified person. We can @@ -176,232 +112,33 @@ class Person(Row): add_key = Row.add_object(Key, 'person_key') remove_key = Row.remove_object(Key, 'person_key') - def set_primary_site(self, site, commit = True): - """ - Set the primary site for an existing user. - """ - - assert 'person_id' in self - assert 'site_id' in site - - person_id = self['person_id'] - site_id = site['site_id'] - self.api.db.do("UPDATE person_site SET is_primary = False" \ - " WHERE person_id = %(person_id)d", - locals()) - self.api.db.do("UPDATE person_site SET is_primary = True" \ - " WHERE person_id = %(person_id)d" \ - " AND site_id = %(site_id)d", - locals()) - - if commit: - self.api.db.commit() - - assert 'site_ids' in self - assert site_id in self['site_ids'] - - # Make sure that the primary site is first in the list - self['site_ids'].remove(site_id) - self['site_ids'].insert(0, site_id) - - def update_last_updated(self, commit = True): - """ - Update last_updated field with current time - """ - - assert 'person_id' in self - assert self.table_name - - self.api.db.do("UPDATE %s SET last_updated = CURRENT_TIMESTAMP " % (self.table_name) + \ - " where person_id = %d" % (self['person_id']) ) - self.sync(commit) - - def associate_roles(self, auth, field, value): - """ - Adds roles found in value list to this person (using AddRoleToPerson). - Deletes roles not found in value list from this person (using DeleteRoleFromPerson). - """ - - assert 'role_ids' in self - assert 'person_id' in self - assert isinstance(value, list) - - (role_ids, role_names) = self.separate_types(value)[0:2] - - # Translate roles into role_ids - if role_names: - roles = Roles(self.api, role_names).dict('role_id') - role_ids += roles.keys() - - # Add new ids, remove stale ids - if self['role_ids'] != role_ids: - from PLC.Methods.AddRoleToPerson import AddRoleToPerson - from PLC.Methods.DeleteRoleFromPerson import DeleteRoleFromPerson - new_roles = set(role_ids).difference(self['role_ids']) - stale_roles = set(self['role_ids']).difference(role_ids) - - for new_role in new_roles: - AddRoleToPerson.__call__(AddRoleToPerson(self.api), auth, new_role, self['person_id']) - for stale_role in stale_roles: - DeleteRoleFromPerson.__call__(DeleteRoleFromPerson(self.api), auth, stale_role, self['person_id']) - - - def associate_sites(self, auth, field, value): - """ - Adds person to sites found in value list (using AddPersonToSite). - Deletes person from site not found in value list (using DeletePersonFromSite). - """ - - from PLC.Sites import Sites + def sync(self, insert=False, validate=True): + NovaObject.sync(self, insert, validate) + if insert == True or id not in self: + self.object = self.api.client_shell.keystone.tenants.create(**self) + - assert 'site_ids' in self - assert 'person_id' in self - assert isinstance(value, list) - - (site_ids, site_names) = self.separate_types(value)[0:2] - - # Translate roles into role_ids - if site_names: - sites = Sites(self.api, site_names, ['site_id']).dict('site_id') - site_ids += sites.keys() - - # Add new ids, remove stale ids - if self['site_ids'] != site_ids: - from PLC.Methods.AddPersonToSite import AddPersonToSite - from PLC.Methods.DeletePersonFromSite import DeletePersonFromSite - new_sites = set(site_ids).difference(self['site_ids']) - stale_sites = set(self['site_ids']).difference(site_ids) - - for new_site in new_sites: - AddPersonToSite.__call__(AddPersonToSite(self.api), auth, self['person_id'], new_site) - for stale_site in stale_sites: - DeletePersonFromSite.__call__(DeletePersonFromSite(self.api), auth, self['person_id'], stale_site) - - - def associate_keys(self, auth, field, value): - """ - Deletes key_ids not found in value list (using DeleteKey). - Adds key if key_fields w/o key_id is found (using AddPersonKey). - Updates key if key_fields w/ key_id is found (using UpdateKey). - """ - assert 'key_ids' in self - assert 'person_id' in self - assert isinstance(value, list) - - (key_ids, blank, keys) = self.separate_types(value) - - if self['key_ids'] != key_ids: - from PLC.Methods.DeleteKey import DeleteKey - stale_keys = set(self['key_ids']).difference(key_ids) - - for stale_key in stale_keys: - DeleteKey.__call__(DeleteKey(self.api), auth, stale_key) - - if keys: - from PLC.Methods.AddPersonKey import AddPersonKey - from PLC.Methods.UpdateKey import UpdateKey - updated_keys = filter(lambda key: 'key_id' in key, keys) - added_keys = filter(lambda key: 'key_id' not in key, keys) - - for key in added_keys: - AddPersonKey.__call__(AddPersonKey(self.api), auth, self['person_id'], key) - for key in updated_keys: - key_id = key.pop('key_id') - UpdateKey.__call__(UpdateKey(self.api), auth, key_id, key) - - - def associate_slices(self, auth, field, value): - """ - Adds person to slices found in value list (using AddPersonToSlice). - Deletes person from slices found in value list (using DeletePersonFromSlice). - """ - - from PLC.Slices import Slices - - assert 'slice_ids' in self - assert 'person_id' in self - assert isinstance(value, list) - - (slice_ids, slice_names) = self.separate_types(value)[0:2] - - # Translate roles into role_ids - if slice_names: - slices = Slices(self.api, slice_names, ['slice_id']).dict('slice_id') - slice_ids += slices.keys() - - # Add new ids, remove stale ids - if self['slice_ids'] != slice_ids: - from PLC.Methods.AddPersonToSlice import AddPersonToSlice - from PLC.Methods.DeletePersonFromSlice import DeletePersonFromSlice - new_slices = set(slice_ids).difference(self['slice_ids']) - stale_slices = set(self['slice_ids']).difference(slice_ids) - - for new_slice in new_slices: - AddPersonToSlice.__call__(AddPersonToSlice(self.api), auth, self['person_id'], new_slice) - for stale_slice in stale_slices: - DeletePersonFromSlice.__call__(DeletePersonFromSlice(self.api), auth, self['person_id'], stale_slice) - - - def delete(self, commit = True): - """ - Delete existing user. - """ - - # Delete all keys - keys = Keys(self.api, self['key_ids']) - for key in keys: - key.delete(commit = False) - - # Clean up miscellaneous join tables - for table in self.join_tables: - self.api.db.do("DELETE FROM %s WHERE person_id = %d" % \ - (table, self['person_id'])) - - # Mark as deleted - self['deleted'] = True - - # delete will fail if timestamp fields aren't validated, so lets remove them - for field in ['verification_expires', 'date_created', 'last_updated']: - if field in self: - self.pop(field) - - # don't validate, so duplicates can be consistently removed - self.sync(commit, validate=False) - -class Persons(Table): +class Persons(NovaTable): """ Representation of row(s) from the persons table in the database. """ def __init__(self, api, person_filter = None, columns = None): - Table.__init__(self, api, Person, columns) - - view = "view_persons" - for tagname in self.tag_columns: - view= "%s left join %s using (%s)"%(view,Person.tagvalue_view_name(tagname), - Person.primary_key) - - sql = "SELECT %s FROM %s WHERE deleted IS False" % \ - (", ".join(self.columns.keys()+self.tag_columns.keys()),view) - - if person_filter is not None: - if isinstance(person_filter, (list, tuple, set)): - # Separate the list into integers and strings - ints = filter(lambda x: isinstance(x, (int, long)), person_filter) - strs = filter(lambda x: isinstance(x, StringTypes), person_filter) - person_filter = Filter(Person.fields, {'person_id': ints, 'email': strs}) - sql += " AND (%s) %s" % person_filter.sql(api, "OR") - elif isinstance(person_filter, dict): - person_filter = Filter(Person.fields, person_filter) - sql += " AND (%s) %s" % person_filter.sql(api, "AND") - elif isinstance (person_filter, StringTypes): - person_filter = Filter(Person.fields, {'email':person_filter}) - sql += " AND (%s) %s" % person_filter.sql(api, "AND") - elif isinstance (person_filter, (int, long)): - person_filter = Filter(Person.fields, {'person_id':person_filter}) - sql += " AND (%s) %s" % person_filter.sql(api, "AND") - else: - raise PLCInvalidArgument, "Wrong person filter %r"%person_filter + self.api = api + if not person_filter: + persons = self.api.client_shell.keystone.users.findall() + elif isinstance(person_filter, (list, tuple, set)): + # Separate the list into integers and strings + persons = self.api.client_shell.keystone.users.findall() + persons = [person for person in persons if person.id in site_filter] + elif isinstance(person_filter, dict): + persons = [self.api.client_shell.keystone.users.findall(**site_filter)] + elif isinstance (person_filter, StringTypes): + persons = [self.api.client_shell.keystone.users.find(**site_filter)] + else: + raise PLCInvalidArgument, "Wrong person filter %r"%person_filter - self.selectall(sql) + for person in persons: + person = Person(self.api, object = person) + self.append(person) diff --git a/PLC/Sites.py b/PLC/Sites.py index b252a719..456ab497 100644 --- a/PLC/Sites.py +++ b/PLC/Sites.py @@ -44,8 +44,8 @@ class Sites(NovaTable): sites = self.api.client_shell.keystone.tenants.findall() elif isinstance(site_filter, StringTypes): sites = [self.api.client_shell.keystone.tenants.find(id=site_filter)] - elif isinstance(site_filter, StringTypes): - sites = [self.api.client_shell.keystone.tenants.find(**site_filter)] + elif isinstance(site_filter, dict): + sites = [self.api.client_shell.keystone.tenants.findall(**site_filter)] elif isinstance(site_filter, (list, tuple, set)): sites = self.api.client_shell.keystone.tenants.findall() sites = [site for site in sites if site.id in site_filter] -- 2.47.0