def test_search_community_with_additional_fields_on_portal_with_query( self): """ This is the case when a client has customized user properties """ login(self.portal, u'ulearn.testuser1') # We provide here the required initialization for a user custom properties catalog provideUtility(TestUserExtendedPropertiesSoupCatalogFactory(), name='user_properties_exttest') api.portal.set_registry_record( name= 'genweb.controlpanel.core.IGenwebCoreControlPanelSettings.user_properties_extender', value=u'user_properties_exttest') # Modify an user to accomodate new properties from extended catalog # Force it as we are not faking the extension of the user properties # (Plone side utility overriding blabla) add_user_to_catalog('ulearn.testuser1', { 'position': u'Jefe', 'unit_organizational': u'Finance' }) users = searchUsersFunction(self.portal, self.request, 'ulearn.testuser1') self.assertTrue(len(users['content']) == 1) self.assertEqual(users['content'][0]['id'], u'ulearn.testuser1') self.assertEqual(users['content'][0]['position'], u'Jefe') self.assertEqual(users['content'][0]['unit_organizational'], u'Finance') self.assertEqual(users['content'][0]['telefon'], u'123456') logout()
def UpdateUserPropertiesOnLogin(event): user = api.user.get_current() try: properties = get_all_user_properties(user) add_user_to_catalog(user, properties, overwrite=True) except: # To avoid testing test_functional code, since the # test_user doesn't have properties and stops the tests. pass
def render(self): context = aq_inner(self.context) all_user_properties = context.acl_users.mutable_properties.enumerateUsers() for user in all_user_properties: user.update(dict(username=user['id'])) user.update(dict(fullname=user['title'])) user_obj = api.user.get(user['id']) if user_obj: add_user_to_catalog(user_obj, user) else: print('No user found in user repository (LDAP) {}'.format(user['id'])) print('Updated properties catalog for {}'.format(user['id']))
def test_user_properties_searchable_text(self): """ Test the searchable_text of the user_properties soup for make sure you can search on several properties at once. """ self.create_default_test_users() reset_user_catalog() add_user_to_catalog(u'victor.fernandez', dict(fullname=u'Víctor Fernández de Alba', location='Nexus')) login(self.portal, u'ulearn.testuser1') soup = get_soup('user_properties', self.portal) self.assertTrue([r for r in soup.query(Eq('searchable_text', 'victor Nex*'))]) logout() login(self.portal, 'admin') self.delete_default_test_users() logout()
def render(self, result_threshold=100): query = self.request.form.get('q', '') last_query = self.request.form.get('last_query', '') last_query_count = self.request.form.get('last_query_count', 0) if query: portal = api.portal.get() self.request.response.setHeader("Content-type", "application/json") soup = get_soup('user_properties', portal) searching_surname = len(re.match(r'^[^\ \.]+(?: |\.)*(.*?)$', query).groups()[0]) normalized_query = query.replace('.', ' ') + '*' users_in_soup = [dict(id=r.attrs.get('username'), displayName=r.attrs.get('fullname')) for r in soup.query(Or(Eq('username', normalized_query), Eq('fullname', normalized_query)))] too_much_results = len(users_in_soup) > result_threshold is_useless_request = query.startswith(last_query) and len(users_in_soup) == last_query_count if is_useless_request and (not too_much_results or searching_surname): current_user = api.user.get_current() oauth_token = current_user.getProperty('oauth_token', '') maxclient, settings = getUtility(IMAXClient)() maxclient.setActor(current_user.getId()) maxclient.setToken(oauth_token) max_users = maxclient.people.get(qs={'limit': 0, 'username': query}) users_in_max = [dict(id=user.get('username'), displayName=user.get('displayName')) for user in max_users] for user in users_in_max: add_user_to_catalog(user['id'], dict(displayName=user['displayName'])) return json.dumps(dict(results=users_in_max, last_query=query, last_query_count=len(users_in_max))) else: return json.dumps(dict(results=users_in_soup, last_query=query, last_query_count=len(users_in_soup))) else: return json.dumps(dict(error='No query found', last_query='', last_query_count=0))
def test_user_legit_mode(self): """ """ self.create_default_test_users() reset_user_catalog() # Add a legit user add_user_to_catalog(u'victor.fernandez', dict(fullname=u'Víctor')) normalized_query = 'victor fer*' soup = get_soup('user_properties', self.portal) # Search for it via the searchable_text result = [r for r in soup.query(Eq('searchable_text', normalized_query))] self.assertEqual(result[0].attrs['id'], 'victor.fernandez') self.assertEqual(len(result), 1) # Add a non legit user from the initial set add_user_to_catalog(u'victor.fernandez.1', dict(fullname=u'Víctor'), notlegit=True) # The result is still the legit one alone result = [r for r in soup.query(Eq('searchable_text', normalized_query))] self.assertEqual(result[0].attrs['id'], 'victor.fernandez') self.assertEqual(len(result), 1) # The non legit only can be accessed directly by querying the notlegit # index and the legit one does not show, of course result = [r for r in soup.query(Eq('notlegit', True))] self.assertEqual(result[0].attrs['id'], 'victor.fernandez.1') self.assertEqual(len(result), 1) # The non legit only can be accessed directly by querying the fields # directly result = [r for r in soup.query(And(Or(Eq('username', normalized_query), Eq('fullname', normalized_query)), Eq('notlegit', True)))] self.assertEqual(result[0].attrs['id'], 'victor.fernandez.1') self.assertEqual(len(result), 1) # If the non legit became legit at some point of time via a subscriber api.user.get('victor.fernandez.1').setMemberProperties(mapping={'fullname': u'Test', 'location': u'Barcelona', 'telefon': u'654321 123 123'}) # Then it does not show as not legit result = [r for r in soup.query(And(Or(Eq('username', normalized_query), Eq('fullname', normalized_query)), Eq('notlegit', True)))] self.assertEqual(len(result), 0) # And it shows as legit result = [r for r in soup.query(Eq('searchable_text', normalized_query))] self.assertEqual(len(result), 2)
def PUT(self): """ Modify displayName user /api/people/{username} data = {'displayName':'Nom Cognom'} """ existing_user = api.user.get(username=self.params['username'].lower()) maxclient, settings = getUtility(IMAXClient)() maxclient.setActor(settings.max_restricted_username) maxclient.setToken(settings.max_restricted_token) if existing_user: if 'displayName' in self.params: # Update portal membership user properties existing_user.setMemberProperties( {'fullname': self.params['displayName']}) properties = get_all_user_properties(existing_user) add_user_to_catalog(existing_user, properties, overwrite=True) username = self.params['username'].lower() # Update max maxclient.people[username].put( displayName=properties['fullname']) status = maxclient.last_response_code else: status = 500 else: status = 404 if status == 404: return ApiResponse.from_string('User {} not found'.format( self.params['username'].lower()), code=status) elif status == 200: return ApiResponse.from_string('User {} updated'.format( self.params['username'].lower()), code=status) elif status == 500: return ApiResponse.from_string( 'User {} not updated. Not displayName.'.format( self.params['username'].lower()), code=status)
def test_user_properties_searchable_text(self): """ Test the searchable_text of the user_properties soup for make sure you can search on several properties at once. """ self.create_default_test_users() reset_user_catalog() add_user_to_catalog( u'victor.fernandez', dict(fullname=u'Víctor Fernández de Alba', location='Nexus')) login(self.portal, u'ulearn.testuser1') soup = get_soup('user_properties', self.portal) self.assertTrue( [r for r in soup.query(Eq('searchable_text', 'victor Nex*'))]) logout() login(self.portal, 'admin') self.delete_default_test_users() logout()
def test_search_community_with_additional_fields_on_portal_with_query(self): """ This is the case when a client has customized user properties """ login(self.portal, u'ulearn.testuser1') # We provide here the required initialization for a user custom properties catalog provideUtility(TestUserExtendedPropertiesSoupCatalogFactory(), name='user_properties_exttest') api.portal.set_registry_record(name='genweb.controlpanel.core.IGenwebCoreControlPanelSettings.user_properties_extender', value=u'user_properties_exttest') # Modify an user to accomodate new properties from extended catalog # Force it as we are not faking the extension of the user properties # (Plone side utility overriding blabla) add_user_to_catalog('ulearn.testuser1', {'position': u'Jefe', 'unit_organizational': u'Finance'}) users = searchUsersFunction(self.portal, self.request, 'ulearn.testuser1') self.assertTrue(len(users['content']) == 1) self.assertEqual(users['content'][0]['id'], u'ulearn.testuser1') self.assertEqual(users['content'][0]['position'], u'Jefe') self.assertEqual(users['content'][0]['unit_organizational'], u'Finance') self.assertEqual(users['content'][0]['telefon'], u'123456') logout()
def test_user_search_on_acl(self): """ cas 1: Primer caràcter (només 1) is_useless_request == False too_much_results ? searching_surname == False cas 2.1: Segona lletra en endavant i not searching_surname i is_useless_request i too_much_results ==> soup cas 2.1: Segona lletra en endavant i not searching_surname i is_useless_request i not too_much_results ==> MAX cas 2.2: Segona lletra en endavant i not searching_surname i not is_useless_request ==> Seguim filtrant query soup cas 3: Segona lletra en endavant i searching_surname i not is_useless_request ==> soup cas 3: Segona lletra en endavant i searching_surname i is_useless_request ==> MAX First request, no additional last_query nor last_query_count Create a bunch of users into the system, but clearing the catalog Only one remains """ self.create_default_test_users() reset_user_catalog() add_user_to_catalog(u'victor.fernandez', dict(fullname=u'Víctor')) login(self.portal, u'ulearn.testuser1') search_view = getMultiAdapter((self.portal, self.request), name='omega13usersearch') self.request.form = dict(q='v') result = search_view.render() result = json.loads(result) self.assertEqual(result['last_query_count'], 1) self.assertEqual(result['last_query'], 'v') # Force the search to be useless to force a MAX update self.request.form = dict(q='victor.fer', last_query='v', last_query_count=1) result = search_view.render() result = json.loads(result) self.assertTrue(result['last_query_count'] > 5) self.assertEqual(result['last_query'], 'victor.fer') soup = get_soup('user_properties', self.portal) self.assertTrue( len([r for r in soup.query(Eq('username', 'victor fer*'))]) > 5) # Amb un altre usuari (janet) add_user_to_catalog(u'janet.dura', dict(fullname=u'Janet')) self.request.form = dict(q='janet', last_query='', last_query_count=0) result = search_view.render() result = json.loads(result) self.assertEqual(result['last_query_count'], 1) self.assertEqual(result['results'], [{ u'displayName': u'Janet', u'id': u'janet.dura' }]) self.request.form = dict(q='janeth', last_query='janet', last_query_count=1) result = search_view.render() result = json.loads(result) self.assertEqual(result['last_query_count'], 0) # Freeze this part as we cannot rely in that user being always there # self.request.form = dict(q='janeth.tosca', last_query='janeth', last_query_count=0) # result = search_view.render() # result = json.loads(result) # self.assertEqual(result['last_query_count'], 1) # self.assertEqual(result['results'], [{"displayName": "janeth.toscana", "id": "janeth.toscana"}]) logout() login(self.portal, 'admin') self.delete_default_test_users() logout()
def update_user_properties_hook(user, event): """ This subscriber hooks on user creation and adds user properties to the soup-based catalog for later searches """ add_user_to_catalog(user, event.properties, overwrite=True)
def create_user_hook(user, event): """ This subscriber hooks on user creation and adds user properties to the soup-based catalog for later searches """ add_user_to_catalog(user)
def POST(self): """ Syncs user local registry with remote ldap attributes """ maxclient, settings = getUtility(IMAXClient)() maxclient.setActor(settings.max_restricted_username) maxclient.setToken(settings.max_restricted_token) users = self.params['users'] notfound_errors = [] properties_errors = [] max_errors = [] users_sync = [] for userid in users: username = userid.lower() logger.info( '- API REQUEST /api/people/sync: Synchronize user {}'.format( username)) user_memberdata = api.user.get(username=username) try: plone_user = user_memberdata.getUser() except: logger.info( '- API REQUEST /api/people/sync: ERROR sync user {}'. format(username)) notfound_errors.append(username) continue # Delete user cache for prop in plone_user.getOrderedPropertySheets(): try: ldap = prop ldap._invalidateCache(plone_user) plone_user._getPAS().ZCacheable_invalidate( view_name='_findUser-' + username) ldap._getLDAPUserFolder(plone_user)._expireUser(plone_user) break except: continue response = {} try: user_memberdata = api.user.get(username=username) plone_user = user_memberdata.getUser() except: notfound_errors.append(username) logger.error( 'User {} cannot be found in LDAP repository'.format( username)) else: try: properties = get_all_user_properties(plone_user) add_user_to_catalog(plone_user, properties, overwrite=True) except: logger.error( 'Cannot update properties catalog for user {}'.format( username)) properties_errors.append(username) try: fullname = properties.get('fullname', '') maxclient.people.post(username=username, displayName=fullname) # If user hasn't been created right now, update displayName if maxclient.last_response_code == 200: maxclient.people[username].put(displayName=fullname) users_sync.append(username) logger.info( '- API REQUEST /api/people/sync: OK sync user {}'. format(username)) except: logger.error( 'User {} couldn\'t be created or updated on max'. format(username)) max_errors.append(username) response = {} if notfound_errors: response['not_found'] = notfound_errors if properties_errors: response['properties_errors'] = properties_errors if max_errors: response['max_errors'] = max_errors response['synced_users'] = users_sync return ApiResponse(response)
def render(self, result_threshold=100): query = self.request.form.get('q', '') last_query = self.request.form.get('last_query', '') last_query_count = self.request.form.get('last_query_count', 0) if query: portal = api.portal.get() self.request.response.setHeader('Content-type', 'application/json') soup = get_soup('user_properties', portal) searching_surname = len(re.match(r'^[^\ \.]+(?: |\.)*(.*?)$', query).groups()[0]) if isinstance(query, str): query = query.decode('utf-8') normalized_query = unicodedata.normalize('NFKD', query).encode('ascii', errors='ignore') normalized_query = normalized_query.replace('.', ' ') + '*' def user_entry(record): username = record.attrs.get('username') fullname = record.attrs.get('fullname') return dict( id=username, displayName=fullname if fullname else username ) def searchable_text(): return soup.query(Eq('searchable_text', normalized_query)) def not_legit_users(): return soup.query(And( Or( Eq('username', normalized_query), Eq('fullname', normalized_query) ), Eq('notlegit', True) )) users_in_soup = [user_entry(r) for r in searchable_text()] + \ [user_entry(r) for r in not_legit_users()] too_much_results = len(users_in_soup) > result_threshold is_useless_request = query.startswith(last_query) and len(users_in_soup) == int(last_query_count) if is_useless_request and (not too_much_results or searching_surname): current_user = api.user.get_current() oauth_token = current_user.getProperty('oauth_token', '') maxclient, settings = getUtility(IMAXClient)() maxclient.setActor(current_user.getId()) maxclient.setToken(oauth_token) max_users = maxclient.people.get(qs={'limit': 0, 'username': query}) users_in_max = [dict(id=user.get('username'), displayName=user.get('displayName')) for user in max_users] for user in users_in_max: add_user_to_catalog(user['id'], dict(displayName=user['displayName']), notlegit=True) return json.dumps(dict(results=users_in_max, last_query=query, last_query_count=len(users_in_max))) else: return json.dumps(dict(results=users_in_soup, last_query=query, last_query_count=len(users_in_soup))) else: return json.dumps(dict(error='No query found', last_query='', last_query_count=0))
def test_user_search_on_acl(self): """ cas 1: Primer caràcter (només 1) is_useless_request == False too_much_results ? searching_surname == False cas 2.1: Segona lletra en endavant i not searching_surname i is_useless_request i too_much_results ==> soup cas 2.1: Segona lletra en endavant i not searching_surname i is_useless_request i not too_much_results ==> MAX cas 2.2: Segona lletra en endavant i not searching_surname i not is_useless_request ==> Seguim filtrant query soup cas 3: Segona lletra en endavant i searching_surname i not is_useless_request ==> soup cas 3: Segona lletra en endavant i searching_surname i is_useless_request ==> MAX First request, no additional last_query nor last_query_count Create a bunch of users into the system, but clearing the catalog Only one remains """ self.create_default_test_users() reset_user_catalog() add_user_to_catalog(u'victor.fernandez', dict(fullname=u'Víctor')) login(self.portal, u'ulearn.testuser1') search_view = getMultiAdapter((self.portal, self.request), name='omega13usersearch') self.request.form = dict(q='v') result = search_view.render() result = json.loads(result) self.assertEqual(result['last_query_count'], 1) self.assertEqual(result['last_query'], 'v') # Force the search to be useless to force a MAX update self.request.form = dict(q='victor.fer', last_query='v', last_query_count=1) result = search_view.render() result = json.loads(result) self.assertTrue(result['last_query_count'] > 5) self.assertEqual(result['last_query'], 'victor.fer') soup = get_soup('user_properties', self.portal) self.assertTrue(len([r for r in soup.query(Eq('username', 'victor fer*'))]) > 5) # Amb un altre usuari (janet) add_user_to_catalog(u'janet.dura', dict(fullname=u'Janet')) self.request.form = dict(q='janet', last_query='', last_query_count=0) result = search_view.render() result = json.loads(result) self.assertEqual(result['last_query_count'], 1) self.assertEqual(result['results'], [{u'displayName': u'Janet', u'id': u'janet.dura'}]) self.request.form = dict(q='janeth', last_query='janet', last_query_count=1) result = search_view.render() result = json.loads(result) self.assertEqual(result['last_query_count'], 0) # Freeze this part as we cannot rely in that user being always there # self.request.form = dict(q='janeth.tosca', last_query='janeth', last_query_count=0) # result = search_view.render() # result = json.loads(result) # self.assertEqual(result['last_query_count'], 1) # self.assertEqual(result['results'], [{"displayName": "janeth.toscana", "id": "janeth.toscana"}]) logout() login(self.portal, 'admin') self.delete_default_test_users() logout()
def test_user_legit_mode(self): """ """ self.create_default_test_users() reset_user_catalog() # Add a legit user add_user_to_catalog(u'victor.fernandez', dict(fullname=u'Víctor')) normalized_query = 'victor fer*' soup = get_soup('user_properties', self.portal) # Search for it via the searchable_text result = [ r for r in soup.query(Eq('searchable_text', normalized_query)) ] self.assertEqual(result[0].attrs['id'], 'victor.fernandez') self.assertEqual(len(result), 1) # Add a non legit user from the initial set add_user_to_catalog(u'victor.fernandez.1', dict(fullname=u'Víctor'), notlegit=True) # The result is still the legit one alone result = [ r for r in soup.query(Eq('searchable_text', normalized_query)) ] self.assertEqual(result[0].attrs['id'], 'victor.fernandez') self.assertEqual(len(result), 1) # The non legit only can be accessed directly by querying the notlegit # index and the legit one does not show, of course result = [r for r in soup.query(Eq('notlegit', True))] self.assertEqual(result[0].attrs['id'], 'victor.fernandez.1') self.assertEqual(len(result), 1) # The non legit only can be accessed directly by querying the fields # directly result = [ r for r in soup.query( And( Or(Eq('username', normalized_query), Eq('fullname', normalized_query)), Eq('notlegit', True))) ] self.assertEqual(result[0].attrs['id'], 'victor.fernandez.1') self.assertEqual(len(result), 1) # If the non legit became legit at some point of time via a subscriber api.user.get('victor.fernandez.1').setMemberProperties( mapping={ 'fullname': u'Test', 'location': u'Barcelona', 'telefon': u'654321 123 123' }) # Then it does not show as not legit result = [ r for r in soup.query( And( Or(Eq('username', normalized_query), Eq('fullname', normalized_query)), Eq('notlegit', True))) ] self.assertEqual(len(result), 0) # And it shows as legit result = [ r for r in soup.query(Eq('searchable_text', normalized_query)) ] self.assertEqual(len(result), 2)
def render(self, result_threshold=100): query = self.request.form.get('q', '') last_query = self.request.form.get('last_query', '') last_query_count = self.request.form.get('last_query_count', 0) if query: portal = api.portal.get() self.request.response.setHeader('Content-type', 'application/json') soup = get_soup('user_properties', portal) searching_surname = len( re.match(r'^[^\ \.]+(?: |\.)*(.*?)$', query).groups()[0]) if isinstance(query, str): query = query.decode('utf-8') normalized_query = unicodedata.normalize('NFKD', query).encode( 'ascii', errors='ignore') normalized_query = normalized_query.replace('.', ' ') + '*' def user_entry(record): username = record.attrs.get('username') fullname = record.attrs.get('fullname') return dict(id=username, displayName=fullname if fullname else username) def searchable_text(): return soup.query(Eq('searchable_text', normalized_query)) def not_legit_users(): return soup.query( And( Or(Eq('username', normalized_query), Eq('fullname', normalized_query)), Eq('notlegit', True))) users_in_soup = [user_entry(r) for r in searchable_text()] + \ [user_entry(r) for r in not_legit_users()] too_much_results = len(users_in_soup) > result_threshold is_useless_request = query.startswith(last_query) and len( users_in_soup) == int(last_query_count) if is_useless_request and (not too_much_results or searching_surname): current_user = api.user.get_current() oauth_token = current_user.getProperty('oauth_token', '') maxclient, settings = getUtility(IMAXClient)() maxclient.setActor(current_user.getId()) maxclient.setToken(oauth_token) max_users = maxclient.people.get(qs={ 'limit': 0, 'username': query }) users_in_max = [ dict(id=user.get('username'), displayName=user.get('displayName')) for user in max_users ] for user in users_in_max: add_user_to_catalog(user['id'], dict(displayName=user['displayName']), notlegit=True) return json.dumps( dict(results=users_in_max, last_query=query, last_query_count=len(users_in_max))) else: return json.dumps( dict(results=users_in_soup, last_query=query, last_query_count=len(users_in_soup))) else: return json.dumps( dict(error='No query found', last_query='', last_query_count=0))