From af1150bb86524b6dfc70bc6e4b982c871c1596f2 Mon Sep 17 00:00:00 2001
From: "Jiangjie.Bai" <bugatti_it@163.com>
Date: Tue, 31 May 2022 15:39:49 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20OIDC=20=E7=94=A8=E6=88=B7=E6=B7=BB?=
 =?UTF-8?q?=E5=8A=A0=E5=B1=9E=E6=80=A7=E6=98=A0=E5=B0=84=E5=80=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 apps/authentication/backends/oidc/backends.py | 24 ++++++++++++-------
 apps/jumpserver/conf.py                       |  3 +++
 apps/jumpserver/settings/auth.py              |  1 +
 apps/settings/serializers/auth/oidc.py        |  5 ++++
 4 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/apps/authentication/backends/oidc/backends.py b/apps/authentication/backends/oidc/backends.py
index daa614ec8..ec5765510 100644
--- a/apps/authentication/backends/oidc/backends.py
+++ b/apps/authentication/backends/oidc/backends.py
@@ -18,6 +18,7 @@ from django.urls import reverse
 from django.conf import settings
 
 from common.utils import get_logger
+from users.utils import construct_user_email
 
 from ..base import JMSBaseAuthBackend
 from .utils import validate_and_return_id_token, build_absolute_uri
@@ -39,17 +40,22 @@ class UserMixin:
         logger.debug(log_prompt.format('start'))
 
         sub = claims['sub']
-        name = claims.get('name', sub)
-        username = claims.get('preferred_username', sub)
-        email = claims.get('email', "{}@{}".format(username, 'jumpserver.openid'))
-        logger.debug(
-            log_prompt.format(
-                "sub: {}|name: {}|username: {}|email: {}".format(sub, name, username, email)
-            )
-        )
+
+        # Construct user attrs value
+        user_attrs = {}
+        for field, attr in settings.AUTH_OPENID_USER_ATTR_MAP.items():
+            user_attrs[field] = claims.get(attr, sub)
+        email = user_attrs.get('email', '')
+        email = construct_user_email(user_attrs.get('username'), email, 'jumpserver.openid')
+        user_attrs.update({'email': email})
+
+        logger.debug(log_prompt.format(user_attrs))
+
+        username = user_attrs.get('username')
+        name = user_attrs.get('name')
 
         user, created = get_user_model().objects.get_or_create(
-            username=username, defaults={"name": name, "email": email}
+            username=username, defaults=user_attrs
         )
         logger.debug(log_prompt.format("user: {}|created: {}".format(user, created)))
         logger.debug(log_prompt.format("Send signal => openid create or update user"))
diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py
index 6f84397ac..85c3b4598 100644
--- a/apps/jumpserver/conf.py
+++ b/apps/jumpserver/conf.py
@@ -191,6 +191,9 @@ class Config(dict):
         'AUTH_OPENID_CLIENT_AUTH_METHOD': 'client_secret_basic',
         'AUTH_OPENID_SHARE_SESSION': True,
         'AUTH_OPENID_IGNORE_SSL_VERIFICATION': True,
+        'AUTH_OPENID_USER_ATTR_MAP': {
+            'name': 'name', 'username': 'preferred_username', 'email': 'email'
+        },
 
         # OpenID 新配置参数 (version >= 1.5.9)
         'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://oidc.example.com/',
diff --git a/apps/jumpserver/settings/auth.py b/apps/jumpserver/settings/auth.py
index fb067078f..010e92f31 100644
--- a/apps/jumpserver/settings/auth.py
+++ b/apps/jumpserver/settings/auth.py
@@ -73,6 +73,7 @@ AUTH_OPENID_USE_NONCE = CONFIG.AUTH_OPENID_USE_NONCE
 AUTH_OPENID_SHARE_SESSION = CONFIG.AUTH_OPENID_SHARE_SESSION
 AUTH_OPENID_IGNORE_SSL_VERIFICATION = CONFIG.AUTH_OPENID_IGNORE_SSL_VERIFICATION
 AUTH_OPENID_ALWAYS_UPDATE_USER = CONFIG.AUTH_OPENID_ALWAYS_UPDATE_USER
+AUTH_OPENID_USER_ATTR_MAP = CONFIG.AUTH_OPENID_USER_ATTR_MAP
 AUTH_OPENID_AUTH_LOGIN_URL_NAME = 'authentication:openid:login'
 AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:openid:login-callback'
 AUTH_OPENID_AUTH_LOGOUT_URL_NAME = 'authentication:openid:logout'
diff --git a/apps/settings/serializers/auth/oidc.py b/apps/settings/serializers/auth/oidc.py
index 5ab73ea04..ad30729a0 100644
--- a/apps/settings/serializers/auth/oidc.py
+++ b/apps/settings/serializers/auth/oidc.py
@@ -31,6 +31,11 @@ class CommonSettingSerializer(serializers.Serializer):
     AUTH_OPENID_IGNORE_SSL_VERIFICATION = serializers.BooleanField(
         required=False, label=_('Ignore ssl verification')
     )
+    AUTH_OPENID_USER_ATTR_MAP = serializers.DictField(
+        required=True, label=_('User attr map'),
+        help_text=_('User attr map present how to map OpenID user attr to '
+                    'jumpserver, username,name,email is jumpserver attr')
+    )
 
 
 class KeycloakSettingSerializer(CommonSettingSerializer):