From fdc997959557e3d2f557c6c299ba0006e47ce5dd Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Tue, 10 Nov 2015 11:57:37 -0500 Subject: [PATCH] Fix up some SAML issues * Fix an issue I created overriding authenticate * Fix up attribute mapping using an identity provider class. The methods built into django-social-auth for saml are probably not going to work. We also now expose those mappings in a settings attrs map that the user can override. --- awx/main/backend.py | 30 ++++++++++++++++++++++++++++-- awx/settings/defaults.py | 8 ++++++++ awx/sso/middleware.py | 2 -- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/awx/main/backend.py b/awx/main/backend.py index 751ca7bbe3..6354da0984 100644 --- a/awx/main/backend.py +++ b/awx/main/backend.py @@ -18,6 +18,7 @@ from radiusauth.backends import RADIUSBackend as BaseRADIUSBackend # social from social.backends.saml import SAMLAuth as BaseSAMLAuth +from social.backends.saml import SAMLIdentityProvider as BaseSAMLIdentityProvider # Ansible Tower from awx.api.license import feature_enabled @@ -100,13 +101,38 @@ class RADIUSBackend(BaseRADIUSBackend): return None return super(RADIUSBackend, self).get_user(user_id) +class TowerSAMLIdentityProvider(BaseSAMLIdentityProvider): + ''' + Custom Identity Provider to make attributes to what we expect + ''' + + def get_user_permanent_id(self, attributes): + return attributes[django_settings.SOCIAL_AUTH_SAML_ATTRS_PERMANENT_ID] + + def get_user_details(self, attributes): + """ + Given the SAML attributes extracted from the SSO response, get + the user data like name. + """ + import os + attrs = dict() + for social_attr in django_settings.SOCIAL_AUTH_SAML_ATTRS_MAP: + map_attr = django_settings.SOCIAL_AUTH_SAML_ATTRS_MAP[social_attr] + attrs[social_attr] = unicode(attributes[map_attr][0]) if map_attr in attributes else None + return attrs + class SAMLAuth(BaseSAMLAuth): ''' Custom SAMLAuth backend to verify license status ''' - def authenticate(self, username, password): + def get_idp(self, idp_name): + """Given the name of an IdP, get a SAMLIdentityProvider instance""" + idp_config = self.setting('ENABLED_IDPS')[idp_name] + return TowerSAMLIdentityProvider(idp_name, **idp_config) + + def authenticate(self, *args, **kwargs): 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, @@ -115,7 +141,7 @@ 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(username, password) + return super(SAMLAuth, self).authenticate(*args, **kwargs) 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, diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 2e41172ce0..e1f67ff414 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -387,6 +387,14 @@ SOCIAL_AUTH_SAML_TECHNICAL_CONTACT = {} SOCIAL_AUTH_SAML_SUPPORT_CONTACT = {} SOCIAL_AUTH_SAML_ENABLED_IDPS = {} +SOCIAL_AUTH_SAML_ATTRS_PERMANENT_ID = "name_id" +SOCIAL_AUTH_SAML_ATTRS_MAP = { + "first_name": "User.FirstName", + "last_name": "User.LastName", + "username": "User.email", + "email": "User.email", +} + SOCIAL_AUTH_LOGIN_URL = '/' SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/sso/complete/' SOCIAL_AUTH_LOGIN_ERROR_URL = '/sso/error/' diff --git a/awx/sso/middleware.py b/awx/sso/middleware.py index 9923680cb2..c261ab6502 100644 --- a/awx/sso/middleware.py +++ b/awx/sso/middleware.py @@ -22,8 +22,6 @@ from awx.main.models import AuthToken class SocialAuthMiddleware(SocialAuthExceptionMiddleware): def process_request(self, request): - request.META['SERVER_PORT'] = 80 # FIXME - token_key = request.COOKIES.get('token', '') token_key = urllib.quote(urllib.unquote(token_key).strip('"'))