diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5b20cdb5..de96c930 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -22,6 +22,12 @@ Changed - Upgrade minimum PostgreSQL version to v12 (#1074) - Upgrade to PostgreSQL v16 in CI (#1074) +Removed +------- + +- **General** + - SAML support (#1368) + v0.13.4 (2024-02-16) ==================== diff --git a/config/settings/base.py b/config/settings/base.py index b5dd591c..29130bd6 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -65,7 +65,6 @@ 'dal', # For user search combo box 'dal_select2', 'dj_iconify.apps.DjIconifyConfig', # Iconify for SVG icons - # 'django_saml2_auth', # SAML2 support, temp disabled (see #597, #880) ] # Project apps @@ -424,84 +423,6 @@ ) -# SAML configuration -# ------------------------------------------------------------------------------ - - -# SAML support temporarily disabled (see #597, #880) -ENABLE_SAML = False # env.bool('ENABLE_SAML', False) - -SAML2_AUTH = { - # Required setting - # Pysaml2 Saml client settings - # See: https://pysaml2.readthedocs.io/en/latest/howto/config.html - 'SAML_CLIENT_SETTINGS': { - # Optional entity ID string to be passed in the 'Issuer' element of - # authn request, if required by the IDP. - 'entityid': env.str('SAML_CLIENT_ENTITY_ID', 'SODARcore'), - 'entitybaseurl': env.str( - 'SAML_CLIENT_ENTITY_URL', 'https://localhost:8000' - ), - # The auto(dynamic) metadata configuration URL of SAML2 - 'metadata': { - 'local': [ - env.str('SAML_CLIENT_METADATA_FILE', 'metadata.xml'), - ], - }, - 'service': { - 'sp': { - 'idp': env.str( - 'SAML_CLIENT_IPD', - 'https://sso.hpc.bihealth.org/auth/realms/cubi', - ), - # Keycloak expects client signature - 'authn_requests_signed': 'true', - # Enforce POST binding which is required by keycloak - 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', - }, - }, - 'key_file': env.str('SAML_CLIENT_KEY_FILE', 'key.pem'), - 'cert_file': env.str('SAML_CLIENT_CERT_FILE', 'cert.pem'), - 'xmlsec_binary': env.str('SAML_CLIENT_XMLSEC1', '/usr/bin/xmlsec1'), - 'encryption_keypairs': [ - { - 'key_file': env.str('SAML_CLIENT_KEY_FILE', 'key.pem'), - 'cert_file': env.str('SAML_CLIENT_CERT_FILE', 'cert.pem'), - } - ], - }, - # Custom target redirect URL after the user get logged in. - # Defaults to /admin if not set. This setting will be overwritten if you - # have parameter ?next= specificed in the login URL. - 'DEFAULT_NEXT_URL': '/', - # # Optional settings below - # 'NEW_USER_PROFILE': { - # 'USER_GROUPS': [], # The default group name when a new user logs in - # 'ACTIVE_STATUS': True, # The default active status for new users - # 'STAFF_STATUS': True, # The staff status for new users - # 'SUPERUSER_STATUS': False, # The superuser status for new users - # }, - # 'ATTRIBUTES_MAP': env.dict( - # 'SAML_ATTRIBUTES_MAP', - # default={ - # Change values to corresponding SAML2 userprofile attributes. - # 'email': 'Email', - # 'username': 'UserName', - # 'first_name': 'FirstName', - # 'last_name': 'LastName', - # } - # ), - # 'TRIGGER': { - # 'FIND_USER': 'path.to.your.find.user.hook.method', - # 'NEW_USER': 'path.to.your.new.user.hook.method', - # 'CREATE_USER': 'path.to.your.create.user.hook.method', - # 'BEFORE_LOGIN': 'path.to.your.login.hook.method', - # }, - # Custom URL to validate incoming SAML requests against - # 'ASSERTION_URL': 'https://your.url.here', -} - - # Logging # ------------------------------------------------------------------------------ diff --git a/config/urls.py b/config/urls.py index 6de17e35..1df5d5a7 100644 --- a/config/urls.py +++ b/config/urls.py @@ -7,8 +7,6 @@ from django.urls import path from django.views import defaults as default_views -# SAML support temporarily disabled (see #597, #880) -# import django_saml2_auth.views # Projectroles dependency from projectroles.views import HomeView @@ -55,22 +53,6 @@ path('examples/project/', include('example_project_app.urls')), # Example site app URLs path('examples/site/', include('example_site_app.urls')), - # SAML support temporarily disabled (see #597, #880) - # These are the SAML2 related URLs. You can change "^saml2_auth/" regex to - # any path you want, like "^sso_auth/", "^sso_login/", etc. (required) - # path('saml2_auth/', include('django_saml2_auth.urls')), - # The following line will replace the default user login with SAML2 (optional) - # If you want to specific the after-login-redirect-URL, use parameter "?next=/the/path/you/want" - # with this view. - # path('sso/login/', django_saml2_auth.views.signin), - # The following line will replace the admin login with SAML2 (optional) - # If you want to specific the after-login-redirect-URL, use parameter "?next=/the/path/you/want" - # with this view. - # path('sso/admin/login/', django_saml2_auth.views.signin), - # The following line will replace the default user logout with the signout page (optional) - # path('sso/logout/', django_saml2_auth.views.signout), - # The following line will replace the default admin user logout with the signout page (optional) - # path('sso/admin/logout/', django_saml2_auth.views.signout), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/docs/source/_static/saml/keycloak_client_config.png b/docs/source/_static/saml/keycloak_client_config.png deleted file mode 100644 index e6babb28..00000000 Binary files a/docs/source/_static/saml/keycloak_client_config.png and /dev/null differ diff --git a/docs/source/_static/saml/keycloak_metadata_download.png b/docs/source/_static/saml/keycloak_metadata_download.png deleted file mode 100644 index e4958516..00000000 Binary files a/docs/source/_static/saml/keycloak_metadata_download.png and /dev/null differ diff --git a/docs/source/_static/saml/keycloak_saml_key_download1.png b/docs/source/_static/saml/keycloak_saml_key_download1.png deleted file mode 100644 index 93815076..00000000 Binary files a/docs/source/_static/saml/keycloak_saml_key_download1.png and /dev/null differ diff --git a/docs/source/_static/saml/keycloak_saml_key_download2.png b/docs/source/_static/saml/keycloak_saml_key_download2.png deleted file mode 100644 index 1a3be912..00000000 Binary files a/docs/source/_static/saml/keycloak_saml_key_download2.png and /dev/null differ diff --git a/docs/source/app_projectroles_settings.rst b/docs/source/app_projectroles_settings.rst index 3231a458..3df60dd1 100644 --- a/docs/source/app_projectroles_settings.rst +++ b/docs/source/app_projectroles_settings.rst @@ -475,144 +475,18 @@ This part of the setup is **optional**. ) -SAML SSO Configuration (Optional) -================================= +SAML SSO Configuration (Removed in v1.0) +======================================== -.. danger:: +.. note:: In the current dev version of SODAR Core (v1.1.0-WIP), SAML support has been - temporarily disabled. The repository must be upgraded to a new SAML library - with support for Django v4.2+. This may also cause changes for configuring - SAML authentication. - -Optional Single Sign-On (SSO) authorization via SAML is also available. To -enable this feature, set ``ENABLE_SAML=1`` in your environment. Configuring SAML -for SSO requires proper configuration of the Keycloak SSO server and the SAML -client library. - -Keycloak --------- - -Create a new client in Keycloak and configure it as follows. Please note that -**Client ID** can be chosen however you like, but it must match the setting -in the client. - -.. figure:: _static/saml/keycloak_client_config.png - -To generate the ``metadata.xml`` file required for the client, go to the -**Realm Settings** page and in the **General** tab, click -``SAML 2.0 Identity Provider Metadata`` to download the xml data. Save it -somewhere on the client, the preferred name is ``metadata.xml``. - -.. figure:: _static/saml/keycloak_metadata_download.png - -For the signing of the request send to the Keycloak server you will require a -certificate and key provided by the Keycloak server and incorporated into the -configuration of the client. Switch to the ``SAML Keys``. Make sure to select -``PKCS12`` as **Archive Format**. - -.. figure:: _static/saml/keycloak_saml_key_download1.png -.. figure:: _static/saml/keycloak_saml_key_download2.png - -Convert the archive on the commandline with the follow command and store them in -some place on your client. - -.. code:: - - openssl pkcs12 -in keystore.p12 -password "pass:" -nodes | openssl x509 -out cert.pem - openssl pkcs12 -in keystore.p12 -password "pass:" -nodes -nocerts | openssl rsa -out key.pem - -SODAR Core ----------- - -Make sure that your ``config/settings/base.py`` contains the following -configuration: - -.. code-block:: python - - ENABLE_SAML = env.bool('ENABLE_SAML', False) - SAML2_AUTH = { - # Required setting - # Pysaml2 Saml client settings - # See: https://pysaml2.readthedocs.io/en/latest/howto/config.html - 'SAML_CLIENT_SETTINGS': { - # Optional entity ID string to be passed in the 'Issuer' element of - # authn request, if required by the IDP. - 'entityid': env.str('SAML_CLIENT_ENTITY_ID', 'SODARcore'), - 'entitybaseurl': env.str( - 'SAML_CLIENT_ENTITY_URL', 'https://localhost:8000' - ), - # The auto(dynamic) metadata configuration URL of SAML2 - 'metadata': { - 'local': [ - env.str('SAML_CLIENT_METADATA_FILE', 'metadata.xml'), - ], - }, - 'service': { - 'sp': { - 'idp': env.str( - 'SAML_CLIENT_IPD', - 'https://sso.hpc.bihealth.org/auth/realms/cubi', - ), - # Keycloak expects client signature - 'authn_requests_signed': 'true', - # Enforce POST binding which is required by keycloak - 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', - }, - }, - 'key_file': env.str('SAML_CLIENT_KEY_FILE', 'key.pem'), - 'cert_file': env.str('SAML_CLIENT_CERT_FILE', 'cert.pem'), - 'xmlsec_binary': env.str('SAML_CLIENT_XMLSEC1', '/usr/bin/xmlsec1'), - 'encryption_keypairs': [ - { - 'key_file': env.str('SAML_CLIENT_KEY_FILE', 'key.pem'), - 'cert_file': env.str('SAML_CLIENT_CERT_FILE', 'cert.pem'), - } - ], - }, - # Custom target redirect URL after the user get logged in. - # Defaults to /admin if not set. This setting will be overwritten if you - # have parameter ?next= specified in the login URL. - 'DEFAULT_NEXT_URL': '/', - # # Optional settings below - # 'NEW_USER_PROFILE': { - # 'USER_GROUPS': [], # The default group name when a new user logs in - # 'ACTIVE_STATUS': True, # The default active status for new users - # 'STAFF_STATUS': True, # The staff status for new users - # 'SUPERUSER_STATUS': False, # The superuser status for new users - # }, - # 'ATTRIBUTES_MAP': env.dict( - # 'SAML_ATTRIBUTES_MAP', - # default={ - # Change values to corresponding SAML2 userprofile attributes. - # 'email': 'Email', - # 'username': 'UserName', - # 'first_name': 'FirstName', - # 'last_name': 'LastName', - # } - # ), - # 'TRIGGER': { - # 'FIND_USER': 'path.to.your.find.user.hook.method', - # 'NEW_USER': 'path.to.your.new.user.hook.method', - # 'CREATE_USER': 'path.to.your.create.user.hook.method', - # 'BEFORE_LOGIN': 'path.to.your.login.hook.method', - # }, - # Custom URL to validate incoming SAML requests against - # 'ASSERTION_URL': 'https://your.url.here', - } - -Add the following settings to your environment variables: - -.. code-block:: - - ENABLE_SAML=1 - SAML_CLIENT_ENTITY_ID= - SAML_CLIENT_ENTITY_URL= - SAML_CLIENT_METADATA_FILE= - SAML_CLIENT_IPO= - SAML_CLIENT_KEY_FILE= - SAML_CLIENT_CERT_FILE= - SAML_CLIENT_XMLSEC1= + removed. It will soon be replaced with support OpenID Connect + authentication. The library we previously used is no longer compatible with + Django v4.2 and we are not aware of SODAR Core based projects requiring SAML + at this time. If there are specific needs to use SAML on a SODAR Core based + site, we are happy to review pull requests to re-introduce it. Please note + the implementation has to support Django v4.2+. Global JS/CSS Include Modifications (Optional) diff --git a/docs/source/app_projectroles_usage.rst b/docs/source/app_projectroles_usage.rst index 592a8e64..c46f61f3 100644 --- a/docs/source/app_projectroles_usage.rst +++ b/docs/source/app_projectroles_usage.rst @@ -25,8 +25,7 @@ Core based Django site. One can either log in using a local Django user or, if LDAP/AD is enabled, their LDAP/AD credentials from a supported site. In the latter case, the user domain -must be appended to the user name in form of ``user@DOMAIN``. Single sign-on -with SAML can also be made available. +must be appended to the user name in form of ``user@DOMAIN``. .. figure:: _static/app_projectroles/sodar_login.png :align: center diff --git a/docs/source/major_changes.rst b/docs/source/major_changes.rst index 69454c06..767a2f2b 100644 --- a/docs/source/major_changes.rst +++ b/docs/source/major_changes.rst @@ -18,6 +18,7 @@ Release Highlights - Upgrade to Django v4.2 and Postgres v16 - Add Python 3.11 support +- Remove SAML SSO support Breaking Changes ================ @@ -56,6 +57,17 @@ production databases before upgrading. Python v3.11 support has been officially added in this version. 3.11 is now also the recommended Python version to use. +SAML SSO Support Removed +------------------------ + +Support for SAML SSO authentication has been removed in this release. It will +soon be replaced with support OpenID Connect authentication. The library we +previously used is no longer compatible with Django v4.2 and we are not aware of +SODAR Core based projects requiring SAML at this time. If there are specific +needs to use SAML on a SODAR Core based site, we are happy to review pull +requests to re-introduce it. Please note the implementation has to support +Django v4.2+. + v0.13.4 (2024-02-16) ******************** diff --git a/env.example b/env.example index 0af13c25..815fcd4f 100644 --- a/env.example +++ b/env.example @@ -21,9 +21,6 @@ EMAIL_SUBJECT_PREFIX=[SODAR Core Dev] # LDAP settings ENABLE_LDAP=0 -# SAML settings -ENABLE_SAML=0 - # Projectroles settings PROJECTROLES_ENABLE_PROFILING=True PROJECTROLES_SITE_MODE=SOURCE diff --git a/projectroles/forms.py b/projectroles/forms.py index 2eb11b4e..42eb14bc 100644 --- a/projectroles/forms.py +++ b/projectroles/forms.py @@ -1115,7 +1115,6 @@ def clean(self): ] if ( not settings.PROJECTROLES_ALLOW_LOCAL_USERS - and not settings.ENABLE_SAML and domain not in [ x.lower() for x in getattr(settings, 'LDAP_ALT_DOMAINS', []) diff --git a/projectroles/templates/projectroles/login.html b/projectroles/templates/projectroles/login.html index 2e97b207..301857c6 100644 --- a/projectroles/templates/projectroles/login.html +++ b/projectroles/templates/projectroles/login.html @@ -46,14 +46,6 @@

Login

Login - {% get_django_setting 'ENABLE_SAML' as enable_saml %} - {% if enable_saml %} -
-

To log in with your SSO provider, please click below.

- - Single Sign-On - - {% endif %} {# Optional template for additional login page HTML #} diff --git a/projectroles/tests/test_views.py b/projectroles/tests/test_views.py index a029b67d..359ad5b0 100644 --- a/projectroles/tests/test_views.py +++ b/projectroles/tests/test_views.py @@ -3636,10 +3636,7 @@ def test_post(self): ), ) - @override_settings( - PROJECTROLES_ALLOW_LOCAL_USERS=False, - ENABLE_SAML=False, - ) + @override_settings(PROJECTROLES_ALLOW_LOCAL_USERS=False) def test_post_local_users_not_allowed(self): """Test POST for local user with local users not allowed""" values = { @@ -3652,10 +3649,7 @@ def test_post_local_users_not_allowed(self): self.assertEqual(response.status_code, 200) self.assertEqual(ProjectInvite.objects.all().count(), 0) - @override_settings( - PROJECTROLES_ALLOW_LOCAL_USERS=True, - ENABLE_SAML=False, - ) + @override_settings(PROJECTROLES_ALLOW_LOCAL_USERS=True) def test_post_local_users_allowed(self): """Test POST for local user with local users allowed""" values = { @@ -3673,7 +3667,6 @@ def test_post_local_users_allowed(self): @override_settings( PROJECTROLES_ALLOW_LOCAL_USERS=False, - ENABLE_SAML=False, ENABLE_LDAP=True, AUTH_LDAP_USERNAME_DOMAIN='EXAMPLE', ) @@ -3694,7 +3687,6 @@ def test_post_local_users_email_domain(self): @override_settings( PROJECTROLES_ALLOW_LOCAL_USERS=False, - ENABLE_SAML=False, ENABLE_LDAP=True, LDAP_ALT_DOMAINS=['example.com'], ) diff --git a/requirements/base.txt b/requirements/base.txt index 3d0182f7..3b410669 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -82,5 +82,3 @@ celery>=5.2.7, <5.3 # NOTE: 3.9.5 causes crash with Whitenoise (see issue #1224) django-autocomplete-light==3.9.4 -# SAML2 support for SSO -django-saml2-auth-ai>=2.1.6, <2.2 diff --git a/siteinfo/views.py b/siteinfo/views.py index cfd18e09..cec8898a 100644 --- a/siteinfo/views.py +++ b/siteinfo/views.py @@ -40,7 +40,6 @@ 'EMAIL_SENDER', 'ENABLE_LDAP', 'ENABLE_LDAP_SECONDARY', - 'ENABLE_SAML', 'ENABLED_BACKEND_PLUGINS', 'ICONIFY_JSON_ROOT', 'INSTALLED_APPS', @@ -194,5 +193,5 @@ def get_context_data(self, *args, **kwargs): # Core settings context['settings_core'] = self._get_settings(CORE_SETTINGS) - # TODO: Add LDAP/SAML settings? + # TODO: Add LDAP settings? return context