diff --git a/g3w-admin/base/settings/base.py b/g3w-admin/base/settings/base.py index 9b42c437e..fcf39d36d 100644 --- a/g3w-admin/base/settings/base.py +++ b/g3w-admin/base/settings/base.py @@ -48,7 +48,7 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.gis', - #'django.contrib.sites', + 'django.contrib.sites', ] THIRD_PARTY_APPS = [ @@ -75,7 +75,13 @@ 'about', 'django_bleach', 'django_registration', - 'captcha' + 'captcha', + "allauth", + "allauth.account", + "allauth.socialaccount", + "allauth.socialaccount.providers.microsoft", + "allauth.socialaccount.providers.google", + "allauth.usersessions" ] G3WADMIN_APPS = [ @@ -104,6 +110,10 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.contrib.sites.middleware.CurrentSiteMiddleware', 'qdjango.process_events_middleware.process_events_middleware', + + # Allauth middleware + 'allauth.account.middleware.AccountMiddleware', + 'allauth.usersessions.middleware.UserSessionsMiddleware' ] @@ -127,10 +137,6 @@ 'loaders': [ 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader' - #('django.template.loaders.cached.Loader', [ - # 'django.template.loaders.filesystem.Loader', - # 'django.template.loaders.app_directories.Loader' - #]), ], }, }, @@ -142,7 +148,6 @@ ATOMIC_REQUESTS = True # Password validation -# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { @@ -162,6 +167,7 @@ AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', 'guardian.backends.ObjectPermissionBackend', + 'allauth.account.auth_backends.AuthenticationBackend' ) GUARDIAN_RAISE_403 = True @@ -241,8 +247,6 @@ # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.9/howto/static-files/ - STATIC_URL = '/static/' SITE_TITLE = 'g3w-admin' @@ -252,7 +256,7 @@ QDJANGO_PRJ_CACHE = True QDJANGO_PRJ_CACHE_KEY = 'qdjango_prj_' -# data for proxy server +# Data for proxy server PROXY_SERVER = False # LOGGING_CONFIG = None @@ -303,7 +307,6 @@ SITE_ID = 1 - INTERNAL_IPS = [ '127.0.0.1', ] @@ -344,7 +347,6 @@ # QPLOTLY DEFAULT SETTINGS # ------------------------ - LOAD_QPLOTLY_FROM_PROJECT = False # VENDOR KEYS SETTINGS @@ -387,4 +389,16 @@ # Settings for ReCaptcha v2. Default checkbox RECAPTCHA_VERSION2_TYPE = 'checkbox' # or 'invisible' -SILENCED_SYSTEM_CHECKS = ['captcha.recaptcha_test_key_error'] \ No newline at end of file +SILENCED_SYSTEM_CHECKS = ['captcha.recaptcha_test_key_error'] + +# SOCIAL ACCOUNT LOGIN SETTINGS +# ----------------------------- +SOCIALACCOUNT_LOGIN_ON_GET = True +SOCIALACCOUNT_ADAPTER = 'usersmanage.vendors.allauth.adapter.G3WSocialAccountAdapter' + +# Activate/deactivate user login session tracking +USERSESSIONS_TRACK_ACTIVITY = False + +SOCIALACCOUNT_ONLY = True +SOCIALACCOUNT_USER_ROLE = 'Viewer Level 1' +ACCOUNT_EMAIL_VERIFICATION = 'none' \ No newline at end of file diff --git a/g3w-admin/base/settings/local_settings_example.py b/g3w-admin/base/settings/local_settings_example.py index c4589d510..4b124f9b3 100644 --- a/g3w-admin/base/settings/local_settings_example.py +++ b/g3w-admin/base/settings/local_settings_example.py @@ -163,3 +163,7 @@ 'worker_type': 'process', }, } + +# For social login +# Activate/deactivate user login session tracking +USERSESSIONS_TRACK_ACTIVITY = False \ No newline at end of file diff --git a/g3w-admin/base/urls.py b/g3w-admin/base/urls.py index 4bea8cf2c..541c84fba 100644 --- a/g3w-admin/base/urls.py +++ b/g3w-admin/base/urls.py @@ -168,8 +168,11 @@ ), name="django_registration_disallowed", ), + path('allauthg/', include('allauth.urls')), ] + + ############################################################# # PASSWORD RESET (user password reset by email) # USERNAME RECOVERY (username recovery by email) diff --git a/g3w-admin/templates/login.html b/g3w-admin/templates/login.html index f66ccc7de..78c0fc87f 100644 --- a/g3w-admin/templates/login.html +++ b/g3w-admin/templates/login.html @@ -1,4 +1,5 @@ {% extends "base_login.html" %} +{% load socialaccount %} {% load static %} {% load i18n %} @@ -22,42 +23,47 @@

{% trans 'LOGIN ERROR' %}!

{% trans 'Username and/or password uncorrect' %} {% endif %} -
- {% csrf_token %} -
- - -
-
- - -
-
{{ form.captcha }}
-
-
- -
-
- {% if SETTINGS.RESET_USER_PASSWORD %} -
-
- {% trans 'Forgot your password?' %} + + {% csrf_token %} +
+ +
-
-
- - {% endif %} - {% if SETTINGS.REGISTRATION_OPEN %} -
-
- {% trans 'Sign up' %} +
{{ form.captcha }}
+
+
+ +
-
- {% endif %} - + +
+ {% include "socialaccount/provider_list.html" with process="login" %} +
+ + {% if SETTINGS.RESET_USER_PASSWORD %} + + + {% endif %} + {% if SETTINGS.REGISTRATION_OPEN %} + + {% endif %} +
{% include 'include/login_page_version.html' %}
diff --git a/g3w-admin/usersmanage/receivers.py b/g3w-admin/usersmanage/receivers.py index a07ffc399..b528716c3 100644 --- a/g3w-admin/usersmanage/receivers.py +++ b/g3w-admin/usersmanage/receivers.py @@ -104,3 +104,5 @@ def send_email_to_user(sender, **kwargs): ) user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL, fail_silently=True) + + diff --git a/g3w-admin/usersmanage/templates/socialaccount/provider_list.html b/g3w-admin/usersmanage/templates/socialaccount/provider_list.html new file mode 100644 index 000000000..b3b60307a --- /dev/null +++ b/g3w-admin/usersmanage/templates/socialaccount/provider_list.html @@ -0,0 +1,22 @@ +{% load allauth socialaccount %} +{% load i18n %} +{% get_providers as socialaccount_providers %} +{% if socialaccount_providers %} +
+

- {% trans 'OR' %} -

+
+ {% for provider in socialaccount_providers %} + {% if provider.id == "openid" %} + {% for brand in provider.get_brands %} + {% provider_login_url provider openid=brand.openid_url process=process as href %} + {% element provider name=brand.name provider_id=provider.id href=href %} + {% endelement %} + {% endfor %} + {% endif %} + {% provider_login_url provider process=process scope=scope auth_params=auth_params as href %} + + {% endfor %} +
+{% endif %} diff --git a/g3w-admin/usersmanage/vendors/__init__.py b/g3w-admin/usersmanage/vendors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/g3w-admin/usersmanage/vendors/allauth/__init__.py b/g3w-admin/usersmanage/vendors/allauth/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/g3w-admin/usersmanage/vendors/allauth/adapter.py b/g3w-admin/usersmanage/vendors/allauth/adapter.py new file mode 100644 index 000000000..343d169e6 --- /dev/null +++ b/g3w-admin/usersmanage/vendors/allauth/adapter.py @@ -0,0 +1,58 @@ +# coding=utf-8 +"""" +.. note:: This program is free software; you can redistribute it and/or modify + it under the terms of the Mozilla Public License 2.0. + +""" + +__author__ = 'lorenzetti@gis3w.it' +__date__ = '2024-12-17' +__copyright__ = 'Copyright 2015 - 2024, Gis3w' +__license__ = 'MPL 2.0' + +from django.conf import settings +from django.core.exceptions import ObjectDoesNotExist +from allauth.socialaccount.adapter import DefaultSocialAccountAdapter +from allauth.account.models import EmailAddress +from usersmanage.models import User, Group as AuthGroup, Userbackend, USER_BACKEND_DEFAULT +from usersmanage.configs import G3W_EDITOR1, G3W_EDITOR2, G3W_VIEWER1 + +class G3WSocialAccountAdapter(DefaultSocialAccountAdapter): + + def _set_user_role_backend(self, user): + """ + Set the role and the backend for the user login by social + """ + + # Role to se from settings + role = settings.SOCIALACCOUNT_USER_ROLE \ + if settings.SOCIALACCOUNT_USER_ROLE in (G3W_EDITOR1, G3W_EDITOR2, G3W_VIEWER1) else G3W_VIEWER1 + + AuthGroup.objects.get(name=role).user_set.add(user) + + # Backend + if not hasattr(user, 'userbackend'): + Userbackend(user=user, backend=USER_BACKEND_DEFAULT).save() + + def pre_social_login(self, request, sociallogin): + + # Social account already exists, so this is just a login + if sociallogin.is_existing: + return + + # some social logins don't have an email address + if not sociallogin.email_addresses: + return + try: + existing_user = User.objects.get(email=sociallogin.email_addresses[0].email) + self._set_user_role_backend(existing_user) + except ObjectDoesNotExist: + return + + # if it does, connect this new social login to the existing user + sociallogin.connect(request, existing_user) + + def save_user(self, request, sociallogin, form=None): + user = super(G3WSocialAccountAdapter, self).save_user(request, sociallogin, form=form) + self._set_user_role_backend(user) + return user \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2ab928343..66015f121 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ django-ordered-model==3.3.0 django-ajax-selects==2.2.0 django-modeltranslation==0.17.5 django-filter -django-bleach +django-bleach==3.0.1 pathlib2 lxml psycopg2-binary @@ -30,7 +30,7 @@ django-import-export==3.2.0 coverage urllib3 python-magic -celery==4.4.7 +celery #==5.0.2 deprecation huey==2.4.3 redis==4.5.4 @@ -40,3 +40,5 @@ django-recaptcha==3.0.0 pydantic==2.6.3 weasyprint==63.0 distro==1.7.0 +django-allauth[socialaccount]==0.63.6 +