mirror of
https://github.com/ZwareBear/awx.git
synced 2026-03-20 07:43:35 -05:00
Expand scope of enterprise user types.
This commit is contained in:
@@ -98,6 +98,20 @@ def user_is_system_auditor(user, tf):
|
|||||||
|
|
||||||
User.add_to_class('is_system_auditor', user_is_system_auditor)
|
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 signal handlers only after models have been defined.
|
||||||
import awx.main.signals # noqa
|
import awx.main.signals # noqa
|
||||||
|
|
||||||
|
|||||||
@@ -126,23 +126,25 @@ class LDAPBackend(BaseLDAPBackend):
|
|||||||
return set()
|
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):
|
def _get_or_set_enterprise_user(username, password, provider):
|
||||||
created = False
|
created = False
|
||||||
try:
|
try:
|
||||||
user = User.objects.all().prefetch_related('enterprise_auth').get(username=username)
|
user = User.objects.all().prefetch_related('enterprise_auth').get(username=username)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
user = User(username=username)
|
user = User(username=username)
|
||||||
user.set_unusable_password()
|
enterprise_auth = _decorate_enterprise_user(user, provider)
|
||||||
user.save()
|
|
||||||
enterprise_auth = UserEnterpriseAuth(user=user, provider=provider)
|
|
||||||
enterprise_auth.save()
|
|
||||||
logger.debug("Created enterprise user %s via %s backend." %
|
logger.debug("Created enterprise user %s via %s backend." %
|
||||||
(username, enterprise_auth.get_provider_display()))
|
(username, enterprise_auth.get_provider_display()))
|
||||||
created = True
|
created = True
|
||||||
# NOTE: remove has_usable_password logic in a future release.
|
if created or user.is_in_enterprise_category(provider):
|
||||||
if created or\
|
|
||||||
not user.has_usable_password() or\
|
|
||||||
(provider,) in user.enterprise_auth.all().values_list('provider') and not user.has_usable_password():
|
|
||||||
return user
|
return user
|
||||||
logger.warn("Enterprise user %s already defined in Tower." % username)
|
logger.warn("Enterprise user %s already defined in Tower." % username)
|
||||||
|
|
||||||
@@ -258,7 +260,17 @@ class SAMLAuth(BaseSAMLAuth):
|
|||||||
if not feature_enabled('enterprise_auth'):
|
if not feature_enabled('enterprise_auth'):
|
||||||
logger.error("Unable to authenticate, license does not support SAML authentication")
|
logger.error("Unable to authenticate, license does not support SAML authentication")
|
||||||
return None
|
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):
|
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,
|
if not all([django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT,
|
||||||
|
|||||||
19
awx/sso/migrations/0002_expand_provider_options.py
Normal file
19
awx/sso/migrations/0002_expand_provider_options.py
Normal 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')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -13,6 +13,7 @@ class UserEnterpriseAuth(models.Model):
|
|||||||
PROVIDER_CHOICES = (
|
PROVIDER_CHOICES = (
|
||||||
('radius', _('RADIUS')),
|
('radius', _('RADIUS')),
|
||||||
('tacacs+', _('TACACS+')),
|
('tacacs+', _('TACACS+')),
|
||||||
|
('saml', _('SAML')),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
@@ -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+).
|
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:
|
## 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 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.
|
* 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.
|
* 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)
|
||||||
|
|||||||
Reference in New Issue
Block a user