Skip to content

Commit

Permalink
Merge pull request #8069 from ryanpetrello/saml-autopop
Browse files Browse the repository at this point in the history
add the ability to prevent SAML auto-population behavior

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
  • Loading branch information
2 parents cce66e3 + a6d26d7 commit 70f1bff
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 4 deletions.
1 change: 1 addition & 0 deletions awx/settings/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ def IS_TESTING(argv=None):
'awx.sso.pipeline.update_user_orgs',
'awx.sso.pipeline.update_user_teams',
)
SAML_AUTO_CREATE_OBJECTS = True

SOCIAL_AUTH_LOGIN_URL = '/'
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/sso/complete/'
Expand Down
11 changes: 11 additions & 0 deletions awx/sso/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,17 @@ def get_saml_entity_id():
return settings.TOWER_URL_BASE


register(
'SAML_AUTO_CREATE_OBJECTS',
field_class=fields.BooleanField,
default=True,
label=_('Automatically Create Organizations and Teams on SAML Login'),
help_text=_('When enabled (the default), mapped Organizations and Teams '
'will be created automatically on successful SAML login.'),
category=_('SAML'),
category_slug='saml',
)

register(
'SOCIAL_AUTH_SAML_CALLBACK_URL',
field_class=fields.CharField,
Expand Down
27 changes: 24 additions & 3 deletions awx/sso/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from social_core.exceptions import AuthException

# Django
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext_lazy as _
from django.db.models import Q

Expand Down Expand Up @@ -80,11 +81,18 @@ def _update_m2m_from_expression(user, related, expr, remove=True):

def _update_org_from_attr(user, related, attr, remove, remove_admins, remove_auditors):
from awx.main.models import Organization
from django.conf import settings

org_ids = []

for org_name in attr:
org = Organization.objects.get_or_create(name=org_name)[0]
try:
if settings.SAML_AUTO_CREATE_OBJECTS:
org = Organization.objects.get_or_create(name=org_name)[0]
else:
org = Organization.objects.get(name=org_name)
except ObjectDoesNotExist:
continue

org_ids.append(org.id)
getattr(org, related).members.add(user)
Expand Down Expand Up @@ -199,11 +207,24 @@ def update_user_teams_by_saml_attr(backend, details, user=None, *args, **kwargs)

if organization_alias:
organization_name = organization_alias
org = Organization.objects.get_or_create(name=organization_name)[0]

try:
if settings.SAML_AUTO_CREATE_OBJECTS:
org = Organization.objects.get_or_create(name=organization_name)[0]
else:
org = Organization.objects.get(name=organization_name)
except ObjectDoesNotExist:
continue

if team_alias:
team_name = team_alias
team = Team.objects.get_or_create(name=team_name, organization=org)[0]
try:
if settings.SAML_AUTO_CREATE_OBJECTS:
team = Team.objects.get_or_create(name=team_name, organization=org)[0]
else:
team = Team.objects.get(name=team_name, organization=org)
except ObjectDoesNotExist:
continue

team_ids.append(team.id)
team.member_role.members.add(user)
Expand Down
47 changes: 46 additions & 1 deletion awx/sso/tests/functional/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,15 @@ def orgs(self):
return (o1, o2, o3)

@pytest.fixture
def mock_settings(self):
def mock_settings(self, request):
fixture_args = request.node.get_closest_marker('fixture_args')
if fixture_args and 'autocreate' in fixture_args.kwargs:
autocreate = fixture_args.kwargs['autocreate']
else:
autocreate = True

class MockSettings():
SAML_AUTO_CREATE_OBJECTS = autocreate
SOCIAL_AUTH_SAML_ORGANIZATION_ATTR = {
'saml_attr': 'memberOf',
'saml_admin_attr': 'admins',
Expand Down Expand Up @@ -304,3 +311,41 @@ def test_update_user_teams_alias_by_saml_attr(self, orgs, users, kwargs, mock_se
assert Team.objects.get(
name='Yellow_Alias', organization__name='Default4_Alias').member_role.members.count() == 1

@pytest.mark.fixture_args(autocreate=False)
def test_autocreate_disabled(self, users, kwargs, mock_settings):
kwargs['response']['attributes']['memberOf'] = ['Default1', 'Default2', 'Default3']
kwargs['response']['attributes']['groups'] = ['Blue', 'Red', 'Green']
with mock.patch('django.conf.settings', mock_settings):
for u in users:
update_user_orgs_by_saml_attr(None, None, u, **kwargs)
update_user_teams_by_saml_attr(None, None, u, **kwargs)
assert Organization.objects.count() == 0
assert Team.objects.count() == 0

# precreate everything
o1 = Organization.objects.create(name='Default1')
o2 = Organization.objects.create(name='Default2')
o3 = Organization.objects.create(name='Default3')
Team.objects.create(name='Blue', organization_id=o1.id)
Team.objects.create(name='Blue', organization_id=o2.id)
Team.objects.create(name='Blue', organization_id=o3.id)
Team.objects.create(name='Red', organization_id=o1.id)
Team.objects.create(name='Green', organization_id=o1.id)
Team.objects.create(name='Green', organization_id=o3.id)

for u in users:
update_user_orgs_by_saml_attr(None, None, u, **kwargs)
update_user_teams_by_saml_attr(None, None, u, **kwargs)

assert o1.member_role.members.count() == 3
assert o2.member_role.members.count() == 3
assert o3.member_role.members.count() == 3

assert Team.objects.get(name='Blue', organization__name='Default1').member_role.members.count() == 3
assert Team.objects.get(name='Blue', organization__name='Default2').member_role.members.count() == 3
assert Team.objects.get(name='Blue', organization__name='Default3').member_role.members.count() == 3

assert Team.objects.get(name='Red', organization__name='Default1').member_role.members.count() == 3

assert Team.objects.get(name='Green', organization__name='Default1').member_role.members.count() == 3
assert Team.objects.get(name='Green', organization__name='Default3').member_role.members.count() == 3

0 comments on commit 70f1bff

Please sign in to comment.