From 26b31fedbca79829607270314bad60152b2ab0e9 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Thu, 2 Jun 2016 16:58:19 -0400 Subject: [PATCH] Expose any external account as a user property This adds a field to the user object to denote whether and what kind of user account it is. For normal accounts this field will be null. For social auth accounts it will be "social" for radius accounts it will be "radius" This change also prevents a radius user from changing their local password thus bypassing the password caching feature of django-radius --- awx/api/serializers.py | 23 ++++++++++++++++++++++- awx/sso/backends.py | 13 +++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 104eb8a521..16a1b9fe36 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -680,13 +680,14 @@ class UserSerializer(BaseSerializer): password = serializers.CharField(required=False, default='', write_only=True, help_text='Write-only field used to change the password.') ldap_dn = serializers.CharField(source='profile.ldap_dn', read_only=True) + external_account = serializers.SerializerMethodField(help_text='Set if the account is managed by an external service') is_system_auditor = serializers.BooleanField(default=False) class Meta: model = User fields = ('*', '-name', '-description', '-modified', '-summary_fields', 'username', 'first_name', 'last_name', - 'email', 'is_superuser', 'is_system_auditor', 'password', 'ldap_dn') + 'email', 'is_superuser', 'is_system_auditor', 'password', 'ldap_dn', 'external_account') def to_representation(self, obj): ret = super(UserSerializer, self).to_representation(obj) @@ -720,6 +721,8 @@ class UserSerializer(BaseSerializer): getattr(settings, 'SOCIAL_AUTH_GITHUB_TEAM_KEY', None) or getattr(settings, 'SOCIAL_AUTH_SAML_ENABLED_IDPS', None)) and obj.social_auth.all(): new_password = None + if obj.pk and getattr(settings, 'RADIUS_SERVER', '') and not obj.has_usable_password(): + new_password = None if new_password: obj.set_password(new_password) obj.save(update_fields=['password']) @@ -727,6 +730,24 @@ class UserSerializer(BaseSerializer): obj.set_unusable_password() obj.save(update_fields=['password']) + def get_external_account(self, obj): + account_type = None + if getattr(settings, 'AUTH_LDAP_SERVER_URI', None) and feature_enabled('ldap'): + try: + if obj.pk and obj.profile.ldap_dn and not obj.has_usable_password(): + account_type = "ldap" + except AttributeError: + pass + if (getattr(settings, 'SOCIAL_AUTH_GOOGLE_OAUTH2_KEY', None) or + getattr(settings, 'SOCIAL_AUTH_GITHUB_KEY', None) or + getattr(settings, 'SOCIAL_AUTH_GITHUB_ORG_KEY', None) or + getattr(settings, 'SOCIAL_AUTH_GITHUB_TEAM_KEY', None) or + getattr(settings, 'SOCIAL_AUTH_SAML_ENABLED_IDPS', None)) and obj.social_auth.all(): + account_type = "social" + if obj.pk and getattr(settings, 'RADIUS_SERVER', '') and not obj.has_usable_password(): + account_type = "radius" + return account_type + def create(self, validated_data): new_password = validated_data.pop('password', None) obj = super(UserSerializer, self).create(validated_data) diff --git a/awx/sso/backends.py b/awx/sso/backends.py index 9e227624ec..e36b616084 100644 --- a/awx/sso/backends.py +++ b/awx/sso/backends.py @@ -6,6 +6,7 @@ import logging # Django from django.dispatch import receiver +from django.contrib.auth.models import User from django.conf import settings as django_settings # django-auth-ldap @@ -104,6 +105,18 @@ class RADIUSBackend(BaseRADIUSBackend): return None return super(RADIUSBackend, self).get_user(user_id) + def get_django_user(self, username, password=None): + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + user = User(username=username) + + if password is not None: + user.set_unusable_password() + user.save() + + return user + class TowerSAMLIdentityProvider(BaseSAMLIdentityProvider): '''