Remove RADIUS authentication (#15548)

Remove RADIUS authentication from AWX

Do not remove models fields and tables let it for a stage where all the work of removing external auth finished AAP-27707

Co-authored-by: Hao Liu <44379968+TheRealHaoLiu@users.noreply.github.com>
This commit is contained in:
Djebran Lezzoum 2024-10-02 15:57:26 +02:00 committed by jessicamack
parent e4c11561cc
commit 2c2694ce89
21 changed files with 26 additions and 252 deletions

View File

@ -78,7 +78,7 @@ register(
label=_('Allow External Users to Create OAuth2 Tokens'),
help_text=_(
'For security reasons, users from external auth providers (LDAP, SAML, '
'SSO, Radius, and others) are not allowed to create OAuth2 tokens. '
'SSO, and others) are not allowed to create OAuth2 tokens. '
'To change this behavior, enable this setting. Existing tokens will '
'not be deleted when this setting is toggled off.'
),

View File

@ -0,0 +1,23 @@
from django.db import migrations
RADIUS_AUTH_CONF_KEYS = [
'RADIUS_SERVER',
'RADIUS_PORT',
'RADIUS_SECRET',
]
def remove_radius_auth_conf(apps, scheme_editor):
setting = apps.get_model('conf', 'Setting')
setting.objects.filter(key__in=RADIUS_AUTH_CONF_KEYS).delete()
class Migration(migrations.Migration):
dependencies = [
('conf', '0010_change_to_JSONField'),
]
operations = [
migrations.RunPython(remove_radius_auth_conf),
]

View File

@ -246,11 +246,9 @@ 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.values_list('provider') and not user.has_usable_password()
# NOTE: this if-else block ensures existing enterprise users are still able to
# NOTE: this if 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':
if category == 'saml':
ret = ret or user.social_auth.all()
return ret

View File

@ -13,8 +13,6 @@ from rest_framework.reverse import reverse as drf_reverse
from awx.main.utils.encryption import decrypt_value, get_encryption_key
from awx.api.versioning import reverse
from awx.main.models.oauth import OAuth2Application as Application, OAuth2AccessToken as AccessToken
from awx.main.tests.functional import immediate_on_commit
from awx.sso.models import UserEnterpriseAuth
from oauth2_provider.models import RefreshToken
@ -33,52 +31,6 @@ def test_personal_access_token_creation(oauth_application, post, alice):
assert 'refresh_token' in resp_json
@pytest.mark.django_db
@pytest.mark.parametrize('allow_oauth, status', [(True, 201), (False, 403)])
def test_token_creation_disabled_for_external_accounts(oauth_application, post, alice, allow_oauth, status):
UserEnterpriseAuth(user=alice, provider='radius').save()
url = drf_reverse('api:oauth_authorization_root_view') + 'token/'
with override_settings(RADIUS_SERVER='example.org', ALLOW_OAUTH2_FOR_EXTERNAL_USERS=allow_oauth):
resp = post(
url,
data='grant_type=password&username=alice&password=alice&scope=read',
content_type='application/x-www-form-urlencoded',
HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([oauth_application.client_id, oauth_application.client_secret])))),
status=status,
)
if allow_oauth:
assert AccessToken.objects.count() == 1
else:
assert 'OAuth2 Tokens cannot be created by users associated with an external authentication provider' in smart_str(resp.content) # noqa
assert AccessToken.objects.count() == 0
@pytest.mark.django_db
def test_existing_token_enabled_for_external_accounts(oauth_application, get, post, admin):
UserEnterpriseAuth(user=admin, provider='radius').save()
url = drf_reverse('api:oauth_authorization_root_view') + 'token/'
with override_settings(RADIUS_SERVER='example.org', ALLOW_OAUTH2_FOR_EXTERNAL_USERS=True):
resp = post(
url,
data='grant_type=password&username=admin&password=admin&scope=read',
content_type='application/x-www-form-urlencoded',
HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([oauth_application.client_id, oauth_application.client_secret])))),
status=201,
)
token = json.loads(resp.content)['access_token']
assert AccessToken.objects.count() == 1
with immediate_on_commit():
resp = get(drf_reverse('api:user_me_list', kwargs={'version': 'v2'}), HTTP_AUTHORIZATION='Bearer ' + token, status=200)
assert json.loads(resp.content)['results'][0]['username'] == 'admin'
with override_settings(RADIUS_SERVER='example.org', ALLOW_OAUTH2_FOR_EXTERNAL_USER=False):
with immediate_on_commit():
resp = get(drf_reverse('api:user_me_list', kwargs={'version': 'v2'}), HTTP_AUTHORIZATION='Bearer ' + token, status=200)
assert json.loads(resp.content)['results'][0]['username'] == 'admin'
@pytest.mark.django_db
def test_pat_creation_no_default_scope(oauth_application, post, admin):
# tests that the default scope is overriden

View File

@ -7,7 +7,6 @@ import pytest
# AWX
from awx.api.versioning import reverse
from awx.conf.models import Setting
from awx.conf.registry import settings_registry
TEST_GIF_LOGO = 'data:image/gif;base64,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' # NOQA
@ -66,38 +65,6 @@ def test_awx_task_env_validity(get, patch, admin, value, expected):
assert resp.data['AWX_TASK_ENV'] == dict()
@pytest.mark.django_db
def test_radius_settings(get, put, patch, delete, admin, settings):
url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'radius'})
response = get(url, user=admin, expect=200)
put(url, user=admin, data=response.data, expect=200)
# Set secret via the API.
patch(url, user=admin, data={'RADIUS_SECRET': 'mysecret'}, expect=200)
response = get(url, user=admin, expect=200)
assert response.data['RADIUS_SECRET'] == '$encrypted$'
assert Setting.objects.filter(key='RADIUS_SECRET').first().value.startswith('$encrypted$')
assert settings.RADIUS_SECRET == 'mysecret'
# Set secret via settings wrapper.
settings_wrapper = settings._awx_conf_settings
settings_wrapper.RADIUS_SECRET = 'mysecret2'
response = get(url, user=admin, expect=200)
assert response.data['RADIUS_SECRET'] == '$encrypted$'
assert Setting.objects.filter(key='RADIUS_SECRET').first().value.startswith('$encrypted$')
assert settings.RADIUS_SECRET == 'mysecret2'
# If we send back $encrypted$, the setting is not updated.
patch(url, user=admin, data={'RADIUS_SECRET': '$encrypted$'}, expect=200)
response = get(url, user=admin, expect=200)
assert response.data['RADIUS_SECRET'] == '$encrypted$'
assert Setting.objects.filter(key='RADIUS_SECRET').first().value.startswith('$encrypted$')
assert settings.RADIUS_SECRET == 'mysecret2'
# If we send an empty string, the setting is also set to an empty string.
patch(url, user=admin, data={'RADIUS_SECRET': ''}, expect=200)
response = get(url, user=admin, expect=200)
assert response.data['RADIUS_SECRET'] == ''
assert Setting.objects.filter(key='RADIUS_SECRET').first().value == ''
assert settings.RADIUS_SECRET == ''
@pytest.mark.django_db
def test_ui_settings(get, put, patch, delete, admin):
url = reverse('api:setting_singleton_detail', kwargs={'category_slug': 'ui'})

View File

@ -392,7 +392,6 @@ REST_FRAMEWORK = {
}
AUTHENTICATION_BACKENDS = (
'awx.sso.backends.RADIUSBackend',
'social_core.backends.google.GoogleOAuth2',
'social_core.backends.github.GithubOAuth2',
'social_core.backends.github.GithubOrganizationOAuth2',
@ -417,12 +416,6 @@ OAUTH2_PROVIDER = {'ACCESS_TOKEN_EXPIRE_SECONDS': 31536000000, 'AUTHORIZATION_CO
ALLOW_OAUTH2_FOR_EXTERNAL_USERS = False
# Radius server settings (default to empty string to skip using Radius auth).
# Note: These settings may be overridden by database settings.
RADIUS_SERVER = ''
RADIUS_PORT = 1812
RADIUS_SECRET = ''
# Enable / Disable HTTP Basic Authentication used in the API browser
# Note: Session limits are not enforced when using HTTP Basic Authentication.
# Note: This setting may be overridden by database settings.

View File

@ -7,11 +7,6 @@ import logging
# Django
from django.contrib.auth.models import User
from django.conf import settings as django_settings
from django.utils.encoding import force_str
from django.http import HttpResponse
# radiusauth
from radiusauth.backends import RADIUSBackend as BaseRADIUSBackend
# social
from social_core.backends.saml import OID_USERID
@ -45,27 +40,6 @@ def _get_or_set_enterprise_user(username, password, provider):
logger.warning("Enterprise user %s already defined in Tower." % username)
class RADIUSBackend(BaseRADIUSBackend):
"""
Custom Radius backend to verify license status
"""
def authenticate(self, request, username, password):
if not django_settings.RADIUS_SERVER:
return None
return super(RADIUSBackend, self).authenticate(request, username, password)
def get_user(self, user_id):
if not django_settings.RADIUS_SERVER:
return None
user = super(RADIUSBackend, self).get_user(user_id)
if not user.has_usable_password():
return user
def get_django_user(self, username, password=None, groups=[], is_staff=False, is_superuser=False):
return _get_or_set_enterprise_user(force_str(username), force_str(password), 'radius')
class TowerSAMLIdentityProvider(BaseSAMLIdentityProvider):
"""
Custom Identity Provider to make attributes to what we expect.

View File

@ -188,7 +188,6 @@ def is_remote_auth_enabled():
settings_that_turn_on_remote_auth = [
'SOCIAL_AUTH_SAML_ENABLED_IDPS',
'RADIUS_SERVER',
]
# Also include any SOCAIL_AUTH_*KEY (except SAML)
for social_auth_key in dir(settings):

View File

@ -143,46 +143,6 @@ if settings.ALLOW_LOCAL_RESOURCE_MANAGEMENT:
category_slug='authentication',
)
###############################################################################
# RADIUS AUTHENTICATION SETTINGS
###############################################################################
register(
'RADIUS_SERVER',
field_class=fields.CharField,
allow_blank=True,
default='',
label=_('RADIUS Server'),
help_text=_('Hostname/IP of RADIUS server. RADIUS authentication is disabled if this setting is empty.'),
category=_('RADIUS'),
category_slug='radius',
placeholder='radius.example.com',
)
register(
'RADIUS_PORT',
field_class=fields.IntegerField,
min_value=1,
max_value=65535,
default=1812,
label=_('RADIUS Port'),
help_text=_('Port of RADIUS server.'),
category=_('RADIUS'),
category_slug='radius',
)
register(
'RADIUS_SECRET',
field_class=fields.CharField,
allow_blank=True,
default='',
label=_('RADIUS Secret'),
help_text=_('Shared secret for authenticating to RADIUS server.'),
category=_('RADIUS'),
category_slug='radius',
encrypted=True,
)
###############################################################################
# GOOGLE OAUTH2 AUTHENTICATION SETTINGS
###############################################################################

View File

@ -107,7 +107,6 @@ class AuthenticationBackendsField(fields.StringListField):
# authentication backend.
REQUIRED_BACKEND_SETTINGS = collections.OrderedDict(
[
('awx.sso.backends.RADIUSBackend', ['RADIUS_SERVER']),
('social_core.backends.google.GoogleOAuth2', ['SOCIAL_AUTH_GOOGLE_OAUTH2_KEY', 'SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET']),
('social_core.backends.github.GithubOAuth2', ['SOCIAL_AUTH_GITHUB_KEY', 'SOCIAL_AUTH_GITHUB_SECRET']),
('social_core.backends.open_id_connect.OpenIdConnectAuth', ['SOCIAL_AUTH_OIDC_KEY', 'SOCIAL_AUTH_OIDC_SECRET', 'SOCIAL_AUTH_OIDC_OIDC_ENDPOINT']),

View File

@ -335,7 +335,6 @@ class TestCommonFunctions:
# Set none of the social auth settings
('JUNK_SETTING', False),
('SOCIAL_AUTH_SAML_ENABLED_IDPS', True),
('RADIUS_SERVER', True),
# Set some SOCIAL_SOCIAL_AUTH_OIDC_KEYAUTH_*_KEY settings
('SOCIAL_AUTH_AZUREAD_OAUTH2_KEY', True),
('SOCIAL_AUTH_GITHUB_ENTERPRISE_KEY', True),

View File

@ -18,7 +18,6 @@ page.register_page(
resources.settings_github_team,
resources.settings_google_oauth2,
resources.settings_jobs,
resources.settings_radius,
resources.settings_saml,
resources.settings_system,
resources.settings_ui,

View File

@ -217,7 +217,6 @@ class Resources(object):
_settings_jobs = 'settings/jobs/'
_settings_logging = 'settings/logging/'
_settings_named_url = 'settings/named-url/'
_settings_radius = 'settings/radius/'
_settings_saml = 'settings/saml/'
_settings_system = 'settings/system/'
_settings_ui = 'settings/ui/'

View File

@ -11,12 +11,9 @@ When a user wants to log into AWX, she can explicitly choose some of the support
* Microsoft Azure Active Directory (AD) OAuth2
On the other hand, the other authentication methods use the same types of login info (username and password), but authenticate using external auth systems rather than AWX's own database. If some of these methods are enabled, AWX will try authenticating using the enabled methods *before AWX's own authentication method*. The order of precedence is:
* RADIUS
* SAML
## Notes:
SAML users and RADIUS 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 AWX.
* AWX passwords of Enterprise users should always be empty and cannot be set by any user if there are enterprise backends enabled.

View File

@ -1,5 +1,3 @@
Through the AWX user interface, you can set up a simplified login through various authentication types: GitHub, Google, and RADIUS. After you create and register your developer application with the appropriate service, you can set up authorizations for them.
1. From the left navigation bar, click **Settings**.
2. The left side of the Settings window is a set of configurable Authentication settings. Select from the following options:

View File

@ -56,23 +56,3 @@ For application registering basics in Azure AD, refer to the `Azure AD Identity
.. _`Azure AD Identity Platform (v2)`: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-overview
.. _ag_auth_radius:
RADIUS settings
------------------
.. index::
pair: authentication; RADIUS Authentication Settings
AWX can be configured to centrally use RADIUS as a source for authentication information.
1. Click **Settings** from the left navigation bar.
2. On the left side of the Settings window, click **RADIUS settings** from the list of Authentication options.
3. Click **Edit** and enter the Host or IP of the Radius server in the **Radius Server** field. If this field is left blank, Radius authentication is disabled.
4. Enter the port and secret information in the next two fields.
5. Click **Save** when done.

View File

@ -11,12 +11,6 @@ Authentication methods help simplify logins for end users--offering single sign-
Account authentication can be configured in the AWX User Interface and saved to the PostgreSQL database. For instructions, refer to the :ref:`ag_configure_awx` section.
Account authentication in AWX can be configured to centrally use OAuth2, while enterprise-level account authentication can be configured for :ref:`Azure <ag_auth_azure>`, :ref:`RADIUS <ag_auth_radius>` as a source for authentication information. See :ref:`ag_ent_auth` for more detail.
For websites, such as Microsoft Azure, Google or GitHub, that provide account information, account information is often implemented using the OAuth standard. OAuth is a secure authorization protocol which is commonly used in conjunction with account authentication to grant 3rd party applications a "session token" allowing them to make API calls to providers on the users behalf.
The :ref:`RADIUS <ag_auth_radius>` distributed client/server system allows you to secure networks against unauthorized access and can be implemented in network environments requiring high levels of security while maintaining network access for remote users.
.. _ag_auth_github:

View File

@ -1,24 +0,0 @@
Copyright (c) 2015, Rob Golding. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Rob Golding, nor the names of its contributors may
be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,28 +0,0 @@
Copyright 2002-2008 Wichert Akkerman. All rights reserved.
Copyright 2007-2008 Simplon. All rights reserved.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the University nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@ -20,7 +20,6 @@ django-guid
django-oauth-toolkit<2.0.0 # Version 2.0.0 has breaking changes that will need to be worked out before upgrading
django-polymorphic
django-pglocks
django-radius
django-solo
django-split-settings
djangorestframework>=3.15.0

View File

@ -155,8 +155,6 @@ django-pglocks==1.0.4
# via -r /awx_devel/requirements/requirements.in
django-polymorphic==3.1.0
# via -r /awx_devel/requirements/requirements.in
django-radius==1.5.1
# via -r /awx_devel/requirements/requirements.in
django-solo==2.2.0
# via -r /awx_devel/requirements/requirements.in
django-split-settings==1.3.2
@ -397,8 +395,6 @@ pyopenssl==24.0.0
# twisted
pyparsing==2.4.6
# via -r /awx_devel/requirements/requirements.in
pyrad==2.4
# via django-radius
python-daemon==3.0.1
# via
# -r /awx_devel/requirements/requirements.in