Skip to content

Commit

Permalink
Optimizing SMAL login process
Browse files Browse the repository at this point in the history
  • Loading branch information
john-westcott-iv committed Dec 8, 2022
1 parent 6468d15 commit 7ac513f
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 189 deletions.
8 changes: 1 addition & 7 deletions awx/settings/defaults.py
Expand Up @@ -478,13 +478,7 @@
'awx.sso.social_pipeline.prevent_inactive_login',
)
SOCIAL_AUTH_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ('awx.sso.social_pipeline.update_user_orgs', 'awx.sso.social_pipeline.update_user_teams')
SOCIAL_AUTH_SAML_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + (
'awx.sso.saml_pipeline.update_user_orgs_by_saml_attr',
'awx.sso.saml_pipeline.update_user_teams_by_saml_attr',
'awx.sso.saml_pipeline.update_user_orgs',
'awx.sso.saml_pipeline.update_user_teams',
'awx.sso.saml_pipeline.update_user_flags',
)
SOCIAL_AUTH_SAML_PIPELINE = _SOCIAL_AUTH_PIPELINE_BASE + ('awx.sso.saml_pipeline.populate_user', 'awx.sso.saml_pipeline.update_user_flags')
SAML_AUTO_CREATE_OBJECTS = True

SOCIAL_AUTH_LOGIN_URL = '/'
Expand Down
40 changes: 6 additions & 34 deletions awx/sso/backends.py
Expand Up @@ -35,8 +35,7 @@

# Ansible Tower
from awx.sso.models import UserEnterpriseAuth

from .common import reconcile_users_org_team_mappings
from awx.sso.common import create_org_and_teams, reconcile_users_org_team_mappings

logger = logging.getLogger('awx.sso.backends')

Expand Down Expand Up @@ -389,43 +388,16 @@ def on_populate_user(sender, **kwargs):

org_map = getattr(backend.settings, 'ORGANIZATION_MAP', {})
team_map = getattr(backend.settings, 'TEAM_MAP', {})

# Move this junk into save of the settings for performance later, there is no need to do that here
# with maybe the exception of someone defining this in settings before the server is started?
# ==============================================================================================================

# Get all of the IDs and names of orgs in the DB and create any new org defined in LDAP that does not exist in the DB
existing_orgs = {}
for (org_id, org_name) in Organization.objects.all().values_list('id', 'name'):
existing_orgs[org_name] = org_id

# Create any orgs (if needed) for all entries in the org and team maps
for org_name in set(list(org_map.keys()) + [item.get('organization', None) for item in team_map.values()]):
if org_name and org_name not in existing_orgs:
logger.info("LDAP adapter is creating org {}".format(org_name))
try:
new_org = Organization.objects.create(name=org_name)
except IntegrityError:
# Another thread must have created this org before we did so now we need to get it
new_org = Organization.objects.get(name=org_name)
# Add the org name to the existing orgs since we created it and we may need it to build the teams below
existing_orgs[org_name] = new_org.id

# Do the same for teams
existing_team_names = list(Team.objects.all().values_list('name', flat=True))
orgs_list = set(list(org_map.keys()) + [item.get('organization', None) for item in team_map.values()])
team_map = {}
for team_name, team_opts in team_map.items():
if not team_opts.get('organization', None):
# You can't save the LDAP config in the UI w/o an org (or '' or null as the org) so if we somehow got this condition its an error
logger.error("Team named {} in LDAP team map settings is invalid due to missing organization".format(team_name))
continue
if team_name not in existing_team_names:
try:
Team.objects.create(name=team_name, organization_id=existing_orgs[team_opts['organization']])
except IntegrityError:
# If another process got here before us that is ok because we don't need the ID from this team or anything
pass
# End move some day
# ==============================================================================================================
team_map[team_name] = team_opts['organization']

create_org_and_teams(orgs_list, team_map, 'LDAP')

# Compute in memory what the state is of the different LDAP orgs
org_roles_and_ldap_attributes = {'admin_role': 'admins', 'auditor_role': 'auditors', 'member_role': 'users'}
Expand Down
66 changes: 63 additions & 3 deletions awx/sso/common.py
Expand Up @@ -9,6 +9,13 @@
logger = logging.getLogger('awx.sso.common')


def get_orgs_by_ids():
existing_orgs = {}
for (org_id, org_name) in Organization.objects.all().values_list('id', 'name'):
existing_orgs[org_name] = org_id
return existing_orgs


def reconcile_users_org_team_mappings(user, desired_org_states, desired_team_states, source):
#
# desired_org_states:
Expand Down Expand Up @@ -49,9 +56,9 @@ def reconcile_users_org_team_mappings(user, desired_org_states, desired_team_sta
for row in model_roles:
for role_name in roles:
desired_state = desired_states.get(row.name, {})
if desired_state[role_name] is None:
# The mapping was not defined for this [org/team]/role so we can just pass
pass
if role_name not in desired_state or desired_state[role_name] is None:
# The mapping was not defined for this [org/team]/role so we can just move on
continue

# If somehow the auth adapter knows about an items role but that role is not defined in the DB we are going to print a pretty error
# This is your classic safety net that we should never hit; but here you are reading this comment... good luck and Godspeed.
Expand All @@ -70,3 +77,56 @@ def reconcile_users_org_team_mappings(user, desired_org_states, desired_team_sta
if role_id in users_roles:
logger.debug("{} adapter removing user {} permission of {} from {} {}".format(source, user.username, role_name, object_type, row.name))
user.roles.remove(role_id)


def create_org_and_teams(org_list, team_map, adapter):
#
# org_list is a set of organization names
# team_map is a dict of {<team_name>: <org name>}
#
# Move this junk into save of the settings for performance later, there is no need to do that here
# with maybe the exception of someone defining this in settings before the server is started?
# ==============================================================================================================

# Get all of the IDs and names of orgs in the DB and create any new org defined in LDAP that does not exist in the DB
existing_orgs = get_orgs_by_ids()

# Create any orgs (if needed) for all entries in the org and team maps
for org_name in org_list:
if org_name and org_name not in existing_orgs:
logger.info("{} adapter is creating org {}".format(adapter, org_name))
try:
new_org = get_or_create_with_default_galaxy_cred(name=org_name)
except IntegrityError:
# Another thread must have created this org before we did so now we need to get it
new_org = get_or_create_with_default_galaxy_cred(name=org_name)
# Add the org name to the existing orgs since we created it and we may need it to build the teams below
existing_orgs[org_name] = new_org.id

# Do the same for teams
existing_team_names = list(Team.objects.all().values_list('name', flat=True))
for team_name in team_map.keys():
if team_name not in existing_team_names:
logger.info("{} adapter is creating team {} in org {}".format(adapter, team_name, team_map[team_name]))
try:
Team.objects.create(name=team_name, organization_id=existing_orgs[team_map[team_name]])
except IntegrityError:
# If another process got here before us that is ok because we don't need the ID from this team or anything
pass
# End move some day
# ==============================================================================================================


def get_or_create_with_default_galaxy_cred(**kwargs):
from awx.main.models import Organization, Credential

(org, org_created) = Organization.objects.get_or_create(**kwargs)
if org_created:
logger.debug("Created org {} (id {}) from {}".format(org.name, org.id, kwargs))
public_galaxy_credential = Credential.objects.filter(managed=True, name='Ansible Galaxy').first()
if public_galaxy_credential is not None:
org.galaxy_credentials.add(public_galaxy_credential)
logger.debug("Added default Ansible Galaxy credential to org")
else:
logger.debug("Could not find default Ansible Galaxy credential to add to org")
return org

0 comments on commit 7ac513f

Please sign in to comment.