From c3c0f87c015a5e1d6a12a8f8e48d48ada55fd8e4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 17 May 2022 21:17:17 +0800 Subject: [PATCH 1/7] =?UTF-8?q?perf:=20domain=20gateway=20=E4=B9=9F?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/domain.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/assets/serializers/domain.py b/apps/assets/serializers/domain.py index 6932572f8..fa1a39574 100644 --- a/apps/assets/serializers/domain.py +++ b/apps/assets/serializers/domain.py @@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from common.validators import alphanumeric from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from common.drf.serializers import SecretReadableMixin from ..models import Domain, Gateway from .base import AuthSerializerMixin @@ -67,7 +68,7 @@ class GatewaySerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): } -class GatewayWithAuthSerializer(GatewaySerializer): +class GatewayWithAuthSerializer(SecretReadableMixin, GatewaySerializer): class Meta(GatewaySerializer.Meta): extra_kwargs = { 'password': {'write_only': False}, From b76920a4bf09cdec43739182e5446195cfbacd25 Mon Sep 17 00:00:00 2001 From: "Jiangjie.Bai" Date: Tue, 17 May 2022 22:09:08 +0800 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E7=BB=84=E7=BB=87?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E7=BB=9F=E8=AE=A1=E6=97=B6=20org=20=E4=B8=BA?= =?UTF-8?q?None=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/orgs/signal_handlers/cache.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/orgs/signal_handlers/cache.py b/apps/orgs/signal_handlers/cache.py index 9de31bbb0..8bd2ca609 100644 --- a/apps/orgs/signal_handlers/cache.py +++ b/apps/orgs/signal_handlers/cache.py @@ -91,10 +91,11 @@ class OrgResourceStatisticsRefreshUtil: @classmethod def refresh_if_need(cls, instance): cache_field_name = cls.model_cache_field_mapper.get(type(instance)) - if cache_field_name: - org_cache = OrgResourceStatisticsCache(instance.org) - org_cache.expire(*cache_field_name) - OrgResourceStatisticsCache(Organization.root()).expire(*cache_field_name) + if not cache_field_name: + return + OrgResourceStatisticsCache(Organization.root()).expire(*cache_field_name) + if instance.org: + OrgResourceStatisticsCache(instance.org).expire(*cache_field_name) @receiver(post_save) From fe3059c1fd0904e3996db0734a3f3bccd4bc6bc4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 17 May 2022 23:49:06 +0800 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/system_user.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index ab07533d0..278a99d87 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -5,6 +5,7 @@ from django.db.models import Count from common.mixins.serializers import BulkSerializerMixin from common.utils import ssh_pubkey_gen from common.drf.fields import EncryptedField +from common.drf.serializers import SecretReadableMixin from common.validators import alphanumeric_re, alphanumeric_cn_re, alphanumeric_win_re from orgs.mixins.serializers import BulkOrgResourceModelSerializer from ..models import SystemUser, Asset @@ -252,7 +253,7 @@ class MiniSystemUserSerializer(serializers.ModelSerializer): fields = SystemUserSerializer.Meta.fields_mini -class SystemUserWithAuthInfoSerializer(SystemUserSerializer): +class SystemUserWithAuthInfoSerializer(SecretReadableMixin, SystemUserSerializer): class Meta(SystemUserSerializer.Meta): fields_mini = ['id', 'name', 'username'] fields_write_only = ['password', 'public_key', 'private_key'] @@ -268,6 +269,9 @@ class SystemUserWithAuthInfoSerializer(SystemUserSerializer): 'assets_amount': {'label': _('Asset')}, 'login_mode_display': {'label': _('Login mode display')}, 'created_by': {'read_only': True}, + 'password': {'write_only': False}, + 'private_key': {'write_only': False}, + 'token': {'write_only': False} } From 44ffd099244a3a9de7b6c4dad7d06c88b2624cf8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 17 May 2022 22:21:37 +0800 Subject: [PATCH 4/7] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E7=9A=84=20decode=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/utils/crypto.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/common/utils/crypto.py b/apps/common/utils/crypto.py index 4fd9360f6..f6a690a82 100644 --- a/apps/common/utils/crypto.py +++ b/apps/common/utils/crypto.py @@ -91,12 +91,13 @@ class AESCrypto: def encrypt(self, text): aes = self.aes() - return str(base64.encodebytes(aes.encrypt(self.to_16(text))), - encoding='utf8').replace('\n', '') # 加密 + cipher = base64.encodebytes(aes.encrypt(self.to_16(text))) + return str(cipher, encoding='utf8').replace('\n', '') # 加密 def decrypt(self, text): aes = self.aes() - return str(aes.decrypt(base64.decodebytes(bytes(text, encoding='utf8'))).rstrip(b'\0').decode("utf8")) # 解密 + text_decoded = base64.decodebytes(bytes(text, encoding='utf8')) + return str(aes.decrypt(text_decoded).rstrip(b'\0').decode("utf8")) class AESCryptoGCM: @@ -234,6 +235,8 @@ def rsa_decrypt(cipher_text, rsa_private_key=None): def rsa_decrypt_by_session_pkey(value): from jumpserver.utils import current_request + if not current_request: + return value private_key_name = settings.SESSION_RSA_PRIVATE_KEY_NAME private_key = current_request.session.get(private_key_name) @@ -254,7 +257,11 @@ def decrypt_password(value): key_cipher, password_cipher = cipher aes_key = rsa_decrypt_by_session_pkey(key_cipher) aes = get_aes_crypto(aes_key, 'ECB') - password = aes.decrypt(password_cipher) + try: + password = aes.decrypt(password_cipher) + except UnicodeDecodeError as e: + logging.error("Decript password error: {}, {}".format(password_cipher, e)) + return value return password From e5f4b8000e173b40543fec9be1656ece3b5fc946 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 17 May 2022 17:02:16 +0800 Subject: [PATCH 5/7] stash --- apps/authentication/middleware.py | 30 ++++++++++++------------------ apps/jumpserver/settings/base.py | 1 - 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/apps/authentication/middleware.py b/apps/authentication/middleware.py index d2b4ff19e..96d0017e9 100644 --- a/apps/authentication/middleware.py +++ b/apps/authentication/middleware.py @@ -45,21 +45,7 @@ class MFAMiddleware: class SessionCookieMiddleware(MiddlewareMixin): @staticmethod - def process_response(request, response: HttpResponse): - key = settings.SESSION_COOKIE_NAME_PREFIX_KEY - value = settings.SESSION_COOKIE_NAME_PREFIX - if request.COOKIES.get(key) == value: - return response - response.set_cookie(key, value) - return response - - -class EncryptedMiddleware: - def __init__(self, get_response): - self.get_response = get_response - - @staticmethod - def check_key_pair(request, response): + def set_cookie_public_key(request, response): pub_key_name = settings.SESSION_RSA_PUBLIC_KEY_NAME public_key = request.session.get(pub_key_name) cookie_key = request.COOKIES.get(pub_key_name) @@ -73,7 +59,15 @@ class EncryptedMiddleware: request.session[pri_key_name] = private_key response.set_cookie(pub_key_name, public_key_decode) - def __call__(self, request): - response = self.get_response(request) - self.check_key_pair(request, response) + @staticmethod + def set_session_cooke_prefix(request, response): + key = settings.SESSION_COOKIE_NAME_PREFIX_KEY + value = settings.SESSION_COOKIE_NAME_PREFIX + if request.COOKIES.get(key) == value: + return response + response.set_cookie(key, value) + + def process_response(self, request, response: HttpResponse): + self.set_session_cooke_prefix(request, response) + self.set_session_cooke_prefix(request, response) return response diff --git a/apps/jumpserver/settings/base.py b/apps/jumpserver/settings/base.py index 327c3ea97..c1baf2882 100644 --- a/apps/jumpserver/settings/base.py +++ b/apps/jumpserver/settings/base.py @@ -95,7 +95,6 @@ MIDDLEWARE = [ 'authentication.backends.cas.middleware.CASMiddleware', 'authentication.middleware.MFAMiddleware', 'authentication.middleware.SessionCookieMiddleware', - 'authentication.middleware.EncryptedMiddleware', 'simple_history.middleware.HistoryRequestMiddleware', ] From aa7540045b00d072a1fe5a469c9b68caca9e0905 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 18 May 2022 14:42:54 +0800 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20session=20guar?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/middleware.py | 20 +++++++++++++++++--- apps/authentication/signal_handlers.py | 3 +++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/authentication/middleware.py b/apps/authentication/middleware.py index 96d0017e9..f1f60bbc5 100644 --- a/apps/authentication/middleware.py +++ b/apps/authentication/middleware.py @@ -60,14 +60,28 @@ class SessionCookieMiddleware(MiddlewareMixin): response.set_cookie(pub_key_name, public_key_decode) @staticmethod - def set_session_cooke_prefix(request, response): + def set_cookie_session_prefix(request, response): key = settings.SESSION_COOKIE_NAME_PREFIX_KEY value = settings.SESSION_COOKIE_NAME_PREFIX if request.COOKIES.get(key) == value: return response response.set_cookie(key, value) + @staticmethod + def set_cookie_session_expire(request, response): + if not request.session.get('auth_session_expiration_required'): + return + value = 'age' + if settings.SESSION_EXPIRE_AT_BROWSER_CLOSE_FORCE or \ + not request.session.get('auto_login', False): + value = 'close' + + age = request.session.get_expiry_age() + response.set_cookie('jms_session_expire', value, max_age=age) + request.session.pop('auth_session_expiration_required', None) + def process_response(self, request, response: HttpResponse): - self.set_session_cooke_prefix(request, response) - self.set_session_cooke_prefix(request, response) + self.set_cookie_session_prefix(request, response) + self.set_cookie_public_key(request, response) + self.set_cookie_session_expire(request, response) return response diff --git a/apps/authentication/signal_handlers.py b/apps/authentication/signal_handlers.py index 0d2a617f9..ac155dcf0 100644 --- a/apps/authentication/signal_handlers.py +++ b/apps/authentication/signal_handlers.py @@ -35,6 +35,9 @@ def on_user_auth_login_success(sender, user, request, **kwargs): session.delete() cache.set(lock_key, request.session.session_key, None) + # 标记登录,设置 cookie,前端可以控制刷新, Middleware 会拦截这个生成 cookie + request.session['auth_session_expiration_required'] = 1 + @receiver(openid_user_login_success) def on_oidc_user_login_success(sender, request, user, create=False, **kwargs): From c8d7c7c56f454bcdfadd4d4a409b81116ff41f0d Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 18 May 2022 18:32:53 +0800 Subject: [PATCH 7/7] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Doidc=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E4=B8=8D=E5=8C=BA=E5=88=86=E5=A4=A7=E5=B0=8F=E5=86=99?= =?UTF-8?q?=20(#8267)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng626 <1304903146@qq.com> --- apps/authentication/backends/oidc/backends.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/authentication/backends/oidc/backends.py b/apps/authentication/backends/oidc/backends.py index 8d4f2f629..daa614ec8 100644 --- a/apps/authentication/backends/oidc/backends.py +++ b/apps/authentication/backends/oidc/backends.py @@ -281,6 +281,11 @@ class OIDCAuthPasswordBackend(OIDCBaseBackend): try: claims_response.raise_for_status() claims = claims_response.json() + preferred_username = claims.get('preferred_username') + if preferred_username and \ + preferred_username.lower() == username.lower() and \ + preferred_username != username: + return except Exception as e: error = "Json claims response error, claims response " \ "content is: {}, error is: {}".format(claims_response.content, str(e)) @@ -309,5 +314,3 @@ class OIDCAuthPasswordBackend(OIDCBaseBackend): openid_user_login_failed.send( sender=self.__class__, request=request, username=username, reason="User is invalid" ) - return None -