Expand scope of enterprise user types.

This commit is contained in:
Aaron Tan 2017-06-19 16:57:12 -04:00
parent 8061667ace
commit 70cccb0e57
5 changed files with 57 additions and 11 deletions

View File

@ -98,6 +98,20 @@ def user_is_system_auditor(user, tf):
User.add_to_class('is_system_auditor', user_is_system_auditor)
def user_is_in_enterprise_category(user, category):
ret = (category,) in user.enterprise_auth.all().values_list('provider') and not user.has_usable_password()
# NOTE: this if-else block ensures existing enterprise users are still able to
# log in. Remove it in a future release
if category == 'radius':
ret = ret or not user.has_usable_password()
elif category == 'saml':
ret = ret or user.social_auth.all()
return ret
User.add_to_class('is_in_enterprise_category', user_is_in_enterprise_category)
# Import signal handlers only after models have been defined.
import awx.main.signals # noqa

View File

@ -126,23 +126,25 @@ class LDAPBackend(BaseLDAPBackend):
return set()
def _decorate_enterprise_user(user, provider):
user.set_unusable_password()
user.save()
enterprise_auth = UserEnterpriseAuth(user=user, provider=provider)
enterprise_auth.save()
return enterprise_auth
def _get_or_set_enterprise_user(username, password, provider):
created = False
try:
user = User.objects.all().prefetch_related('enterprise_auth').get(username=username)
except User.DoesNotExist:
user = User(username=username)
user.set_unusable_password()
user.save()
enterprise_auth = UserEnterpriseAuth(user=user, provider=provider)
enterprise_auth.save()
enterprise_auth = _decorate_enterprise_user(user, provider)
logger.debug("Created enterprise user %s via %s backend." %
(username, enterprise_auth.get_provider_display()))
created = True
# NOTE: remove has_usable_password logic in a future release.
if created or\
not user.has_usable_password() or\
(provider,) in user.enterprise_auth.all().values_list('provider') and not user.has_usable_password():
if created or user.is_in_enterprise_category(provider):
return user
logger.warn("Enterprise user %s already defined in Tower." % username)
@ -258,7 +260,17 @@ class SAMLAuth(BaseSAMLAuth):
if not feature_enabled('enterprise_auth'):
logger.error("Unable to authenticate, license does not support SAML authentication")
return None
return super(SAMLAuth, self).authenticate(*args, **kwargs)
created = False
try:
user = User.objects.get(username=kwargs.get('username', ''))
if user and not user.is_in_enterprise_category('saml'):
return None
except User.DoesNotExist:
created = True
user = super(SAMLAuth, self).authenticate(*args, **kwargs)
if user and created:
_decorate_enterprise_user(user, 'saml')
return user
def get_user(self, user_id):
if not all([django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT,

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sso', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='userenterpriseauth',
name='provider',
field=models.CharField(max_length=32, choices=[(b'radius', 'RADIUS'), (b'tacacs+', 'TACACS+'), (b'saml', 'SAML')]),
),
]

View File

@ -13,6 +13,7 @@ class UserEnterpriseAuth(models.Model):
PROVIDER_CHOICES = (
('radius', _('RADIUS')),
('tacacs+', _('TACACS+')),
('saml', _('SAML')),
)
class Meta:

View File

@ -16,9 +16,9 @@ On the other hand, the rest of authentication methods use the same types of logi
Tower will try authenticating against each enabled authentication method *in the specified order*, meaning if the same username and password is valid in multiple enabled auth methods (For example, both LDAP and TACACS+), Tower will only use the first positive match (In the above example, log a user in via LDAP and skip TACACS+).
## Notes:
* RADIUS users and TACACS+ users are categorized as 'Enterprise' users. The following rules apply to Enterprise users:
* SAML users, RADIUS users and TACACS+ users are categorized as 'Enterprise' users. The following rules apply to Enterprise users:
* Enterprise users can only be created via the first successful login attempt from remote authentication backend.
* Enterprise users cannot be created/authenticated if non-enterprise users with the same name has already been created in Tower.
* Tower passwords of Enterprise users should always be empty and cannot be set by any user if there are enterprise backends enabled.
* If enterprise backends (RADIUS and TACACS+ for now) are disabled, an Enterprise user can be converted to a normal Tower user by setting password field. But this operation is irreversible (The converted Tower user can no longer be treated as Enterprise user)
* If enterprise backends are disabled, an Enterprise user can be converted to a normal Tower user by setting password field. But this operation is irreversible (The converted Tower user can no longer be treated as Enterprise user)