Skip to content
Snippets Groups Projects
Commit 4ba21a05 authored by Jörn Künsemöller's avatar Jörn Künsemöller
Browse files

TMSS-1160: only fetch project roles for projects known to TMSS for efficiency

parent d520a98c
No related branches found
No related tags found
1 merge request!724TMSS-1160: change default config to point to production Keycloak
...@@ -44,7 +44,12 @@ def get_users_by_role_in_project(role, project): ...@@ -44,7 +44,12 @@ def get_users_by_role_in_project(role, project):
""" """
returns the list of users that have the specified role in the specified project returns the list of users that have the specified role in the specified project
""" """
project_persons = get_project_persons() # we fetch all and cache them instead of hitting Keycloak every time this gets called.
# But we don't care about all the legacy projects, so only request what's known to TMSS.
# This also works but is a little less efficient (for a relatively small number of projects):
# project_persons = get_project_persons(include_projects=(project,))
projects_known_to_tmss = models.Project.objects.values_list('name', flat=True)
project_persons = get_project_persons(include_projects=tuple(projects_known_to_tmss))
if project in project_persons: if project in project_persons:
return project_persons[project][role] return project_persons[project][role]
else: else:
...@@ -52,10 +57,11 @@ def get_users_by_role_in_project(role, project): ...@@ -52,10 +57,11 @@ def get_users_by_role_in_project(role, project):
@cachetools.func.ttl_cache(ttl=600) @cachetools.func.ttl_cache(ttl=600)
def get_project_persons(): def get_project_persons(include_projects: tuple = None):
""" """
returns a mapping of projects names to a dict that contains the users that returns a mapping of projects names to a dict that contains the users that
have a particular role in that project. have a particular role in that project.
Optionally filter for a list of projects to include in the response.
""" """
project_persons_map = {} project_persons_map = {}
with KeycloakAdminAPISession() as ksession: with KeycloakAdminAPISession() as ksession:
...@@ -64,47 +70,48 @@ def get_project_persons(): ...@@ -64,47 +70,48 @@ def get_project_persons():
if group['name'] == 'Project': if group['name'] == 'Project':
projects = group['subGroups'] projects = group['subGroups']
for project in projects: for project in projects:
project_detail = ksession.get(url='%s/groups/%s/' % (KEYCLOAK_API_BASE_URL, project['id'])) if include_projects is None or project['name'] in include_projects:
attributes = project_detail.get('attributes', {}) project_detail = ksession.get(url='%s/groups/%s/' % (KEYCLOAK_API_BASE_URL, project['id']))
attributes = project_detail.get('attributes', {})
legacy_role_keys = {'pi': 'lofarProjectPI',
'friend_of_project': 'lofarProjectFriend', legacy_role_keys = {'pi': 'lofarProjectPI',
'contact': 'lofarProjectContactauthor'} 'friend_of_project': 'lofarProjectFriend',
'contact': 'lofarProjectContactauthor'}
for project_role in models.ProjectRole.Choices:
# get role attribute from project: for project_role in models.ProjectRole.Choices:
role = project_role.value # get role attribute from project:
users = attributes.get(role, []) role = project_role.value
# fall back to legacy-style attribute: users = attributes.get(role, [])
if not users and role in legacy_role_keys: # fall back to legacy-style attribute:
users = attributes.get(legacy_role_keys[role], []) if not users and role in legacy_role_keys:
users = attributes.get(legacy_role_keys[role], [])
# convert user list (LDAP DNs) to something we can use in TMSS (email)
user_map = get_user_mapping() # convert user list (LDAP DNs) to something we can use in TMSS (email)
mapped_users = [user_map[user] for user in users if user in user_map] # email list of referenced users user_map = get_user_mapping()
unmappable_users = [user for user in users if user not in user_map] # list of references for which no account was found mapped_users = [user_map[user] for user in users if user in user_map] # email list of referenced users
for unmappable_user in unmappable_users: unmappable_users = [user for user in users if user not in user_map] # list of references for which no account was found
# Note: Usually Keycloak should return DN references to user accounts. For PI's, someone had the for unmappable_user in unmappable_users:
# great idea to allow to specify a freeform string instead, to refer to people who may or may not # Note: Usually Keycloak should return DN references to user accounts. For PI's, someone had the
# have an account. Even if the person has a user account, there is no way to replicate the exact # great idea to allow to specify a freeform string instead, to refer to people who may or may not
# string 'representation' Keycloak returns, since the string may contain typos, or info that is not # have an account. Even if the person has a user account, there is no way to replicate the exact
# stored in the user accounts (like titles). # string 'representation' Keycloak returns, since the string may contain typos, or info that is not
# The following unsafe hack tries to determine whether there is a user account that matches the # stored in the user accounts (like titles).
# name given in the string (ignore titles since they are not part of the user account): # The following unsafe hack tries to determine whether there is a user account that matches the
# unmappable_user_fixed = re.sub('Dr\.', '', unmappable_user) # name given in the string (ignore titles since they are not part of the user account):
# unmappable_user_fixed = re.sub('Prof\.', '', unmappable_user_fixed) # unmappable_user_fixed = re.sub('Dr\.', '', unmappable_user)
# unmappable_user_fixed = re.sub('ir\.', '', unmappable_user_fixed) # unmappable_user_fixed = re.sub('Prof\.', '', unmappable_user_fixed)
# unmappable_user_fixed = re.sub('Ir\.', '', unmappable_user_fixed) # unmappable_user_fixed = re.sub('ir\.', '', unmappable_user_fixed)
# unmappable_user_fixed = re.sub('apl\.', '', unmappable_user_fixed) # unmappable_user_fixed = re.sub('Ir\.', '', unmappable_user_fixed)
# unmappable_user_fixed = re.sub(' +', ' ', unmappable_user_fixed) # unmappable_user_fixed = re.sub('apl\.', '', unmappable_user_fixed)
# # unmappable_user_fixed = re.sub(' +', ' ', unmappable_user_fixed)
# if unmappable_user_fixed in user_map: #
# mapped_users.append(user_map[unmappable_user_fixed]) # if unmappable_user_fixed in user_map:
# else: # mapped_users.append(user_map[unmappable_user_fixed])
logger.warning("Could not match Keycloak user reference '%s' to a known user." % unmappable_user) # else:
if not unmappable_user.startswith('cn='): logger.warning("Could not match Keycloak user reference '%s' to a known user." % unmappable_user)
logger.warning("LOFAR allowed to reference a person by a freeform string instead of a user account. '%s' seems to be such a legacy reference. This needs to be fixed in the identity management." % unmappable_user) if not unmappable_user.startswith('cn='):
project_persons_map.setdefault(project['name'], {})[role] = mapped_users logger.warning("LOFAR allowed to reference a person by a freeform string instead of a user account. '%s' seems to be such a legacy reference. This needs to be fixed in the identity management." % unmappable_user)
project_persons_map.setdefault(project['name'], {})[role] = mapped_users
return project_persons_map return project_persons_map
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment