From dd29bc40d90c8b6e24bc3e6f934795c208cef8b2 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Mon, 2 Nov 2015 11:59:17 -0500 Subject: [PATCH] Add logging and improve validation for certain auth backends * Abstract authention to provide a hook for emitting an error message * Perform some license validation that wasn't present before for enterprise licenses --- awx/main/backend.py | 67 ++++++++++++++++++++++++++++++++++++- awx/settings/defaults.py | 4 +-- awx/settings/postprocess.py | 4 +-- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/awx/main/backend.py b/awx/main/backend.py index ad789af9fc..c8a59350c0 100644 --- a/awx/main/backend.py +++ b/awx/main/backend.py @@ -1,17 +1,29 @@ # Copyright (c) 2015 Ansible, Inc. # All Rights Reserved. +# Python +import logging + # Django from django.dispatch import receiver +from django.conf import settings as django_settings # django-auth-ldap from django_auth_ldap.backend import LDAPSettings as BaseLDAPSettings from django_auth_ldap.backend import LDAPBackend as BaseLDAPBackend from django_auth_ldap.backend import populate_user +# radiusauth +from radiusauth.backends import RADIUSBackend as BaseRADIUSBackend + +# social +from social.backends.saml import SAMLAuth as BaseSAMLAuth + # Ansible Tower from awx.api.license import feature_enabled +logger = logging.getLogger('awx.main.backend') + class LDAPSettings(BaseLDAPSettings): defaults = dict(BaseLDAPSettings.defaults.items() + { @@ -19,6 +31,7 @@ class LDAPSettings(BaseLDAPSettings): 'TEAM_MAP': {}, }.items()) + class LDAPBackend(BaseLDAPBackend): ''' Custom LDAP backend for AWX. @@ -37,7 +50,10 @@ class LDAPBackend(BaseLDAPBackend): settings = property(_get_settings, _set_settings) def authenticate(self, username, password): - if not self.settings.SERVER_URI or not feature_enabled('ldap'): + if not self.settings.SERVER_URI: + return None + if self.settings.SERVER_URI and not feature_enabled('ldap'): + logger.error("LDAP authenticate failed for missing license feature") return None return super(LDAPBackend, self).authenticate(username, password) @@ -60,6 +76,55 @@ class LDAPBackend(BaseLDAPBackend): def get_group_permissions(self, user, obj=None): return set() +class RADIUSBackend(BaseRADIUSBackend): + ''' + Custom Radius backend to verify license status + ''' + + def authenticate(self, username, password): + if not django_settings.RADIUS_SERVER: + return None + if not feature_enabled('enterprise_auth'): + logger.error("RADIUS authenticate failed for missing license feature") + return None + return super(RADIUSBackend, self).authenticate(username, password) + + def get_user(self, user_id): + if not django_settings.RADIUS_SERVER: + return None + if not feature_enabled('enterprise_auth'): + logger.error("RADIUS get_user failed for missing license feature") + return None + return super(RADIUSBackend, self).get_user(user_id) + + +class SAMLAuth(BaseSAMLAuth): + ''' + Custom SAMLAuth backend to verify license status + ''' + + def authenticate(self, username, password): + if not all([django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT, + django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, django_settings.SOCIAL_AUTH_SAML_ORG_INFO, + django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT, + django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS]): + return None + if not feature_enabled('enterprise_auth'): + logger.error("SAML authenticate failed for missing license feature") + return None + return super(SAMLAuth, self).authenticate(username, password) + + 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, + django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, django_settings.SOCIAL_AUTH_SAML_ORG_INFO, + django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT, + django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS]): + return None + if not feature_enabled('enterprise_auth'): + logger.error("SAML get_user failed for missing license feature") + return None + return super(SAMLAuth, self).get_user(user_id) + def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=False): ''' Hepler function to update m2m relationship based on LDAP group membership. diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 4dd6a017d7..06e4f9cae1 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -218,12 +218,12 @@ REST_FRAMEWORK = { AUTHENTICATION_BACKENDS = ( 'awx.main.backend.LDAPBackend', - 'radiusauth.backends.RADIUSBackend', + 'awx.main.backend.RADIUSBackend', 'social.backends.google.GoogleOAuth2', 'social.backends.github.GithubOAuth2', 'social.backends.github.GithubOrganizationOAuth2', 'social.backends.github.GithubTeamOAuth2', - 'social.backends.saml.SAMLAuth', + 'awx.main.backend.SAMLAuth', 'django.contrib.auth.backends.ModelBackend', ) diff --git a/awx/settings/postprocess.py b/awx/settings/postprocess.py index a3db97724d..cd8fcfbfa2 100644 --- a/awx/settings/postprocess.py +++ b/awx/settings/postprocess.py @@ -10,7 +10,7 @@ if not AUTH_LDAP_SERVER_URI: AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'awx.main.backend.LDAPBackend'] if not RADIUS_SERVER: - AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'radiusauth.backends.RADIUSBackend'] + AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'awx.main.backend.RADIUSBackend'] if not all([SOCIAL_AUTH_GOOGLE_OAUTH2_KEY, SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET]): AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'social.backends.google.GoogleOAuth2'] @@ -28,7 +28,7 @@ if not all([SOCIAL_AUTH_SAML_SP_ENTITY_ID, SOCIAL_AUTH_SAML_SP_PUBLIC_CERT, SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, SOCIAL_AUTH_SAML_ORG_INFO, SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, SOCIAL_AUTH_SAML_SUPPORT_CONTACT, SOCIAL_AUTH_SAML_ENABLED_IDPS]): - AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'social.backends.saml.SAMLAuth'] + AUTHENTICATION_BACKENDS = [x for x in AUTHENTICATION_BACKENDS if x != 'awx.main.backend.SAMLAuth'] if not AUTH_BASIC_ENABLED: REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = [x for x in REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] if x != 'rest_framework.authentication.BasicAuthentication']