From 0eaaa7b4f6da18a70afa7711c01542157f569e95 Mon Sep 17 00:00:00 2001 From: feng626 <1304903146@qq.com> Date: Fri, 12 Aug 2022 18:36:17 +0800 Subject: [PATCH 01/10] =?UTF-8?q?fix:=20=E7=94=A8=E6=88=B7=E5=BC=82?= =?UTF-8?q?=E5=9C=B0=E7=99=BB=E9=99=86bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/authentication/utils.py b/apps/authentication/utils.py index 836cb55bf..a7f18648b 100644 --- a/apps/authentication/utils.py +++ b/apps/authentication/utils.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- # +import ipaddress from urllib.parse import urljoin from django.conf import settings +from django.utils.translation import ugettext_lazy as _ from common.utils import validate_ip, get_ip_city, get_request_ip from common.utils import get_logger @@ -23,8 +25,9 @@ def check_different_city_login_if_need(user, request): else: city = get_ip_city(ip) or DEFAULT_CITY - city_white = ['LAN', ] - if city not in city_white: + city_white = [_('LAN'), 'LAN'] + is_private = ipaddress.ip_address(ip).is_private + if not is_private: last_user_login = UserLoginLog.objects.exclude(city__in=city_white) \ .filter(username=user.username, status=True).first() From ddf4b61c9fc8785dff4a55f496086f69f9af6648 Mon Sep 17 00:00:00 2001 From: feng626 <1304903146@qq.com> Date: Mon, 15 Aug 2022 16:02:39 +0800 Subject: [PATCH 02/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E7=BB=84=E7=BB=87=E6=89=B9=E9=87=8F=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E8=B5=84=E4=BA=A7500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/orgs/mixins/models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/orgs/mixins/models.py b/apps/orgs/mixins/models.py index 3406271b6..83e4aeac2 100644 --- a/apps/orgs/mixins/models.py +++ b/apps/orgs/mixins/models.py @@ -40,12 +40,11 @@ class OrgManager(models.Manager): set_current_org(org) return self - def bulk_create(self, objs, batch_size=None, ignore_conflicts=False): org = get_current_org() for obj in objs: if org.is_root(): - if not self.org_id: + if not obj.org_id: raise ValidationError('Please save in a organization') else: obj.org_id = org.id From 9a4b32cb3cd49d9e5fe8f6aeef9db0f55f603dcc Mon Sep 17 00:00:00 2001 From: "Jiangjie.Bai" Date: Mon, 15 Aug 2022 16:02:40 +0800 Subject: [PATCH 03/10] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20metadata=20?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=B7=BB=E5=8A=A0=20float?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/drf/metadata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/common/drf/metadata.py b/apps/common/drf/metadata.py index 569854722..5abdf1d35 100644 --- a/apps/common/drf/metadata.py +++ b/apps/common/drf/metadata.py @@ -67,7 +67,7 @@ class SimpleMetadataWithFilters(SimpleMetadata): default = getattr(field, 'default', None) if default is not None and default != empty: - if isinstance(default, (str, int, bool, datetime.datetime, list)): + if isinstance(default, (str, int, bool, float, datetime.datetime, list)): field_info['default'] = default for attr in self.attrs: From 5559f112db9de452ddbedd87756fe2e890113ca1 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 15 Aug 2022 16:21:27 +0800 Subject: [PATCH 04/10] =?UTF-8?q?fix:=20=E7=94=A8=E6=88=B7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=A4=8D=E5=90=88500=20(#8743)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng626 <1304903146@qq.com> --- apps/authentication/api/login_confirm.py | 5 ++++- apps/authentication/errors/redirect.py | 8 +++++++- apps/authentication/mixins.py | 5 ++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/authentication/api/login_confirm.py b/apps/authentication/api/login_confirm.py index 5bddda4e3..866964677 100644 --- a/apps/authentication/api/login_confirm.py +++ b/apps/authentication/api/login_confirm.py @@ -22,7 +22,10 @@ class TicketStatusApi(mixins.AuthMixin, APIView): self.request.session['auth_third_party_done'] = 1 return Response({"msg": "ok"}) except errors.LoginConfirmOtherError as e: - self.send_auth_signal(success=False, user=request.user, username=request.user.name, reason=e.as_data().get('msg')) + reason = e.msg + username = e.username + self.send_auth_signal(success=False, username=username, reason=reason) + # 若为三方登录,此时应退出登录 auth_logout(request) return Response(e.as_data(), status=200) except errors.NeedMoreInfoError as e: diff --git a/apps/authentication/errors/redirect.py b/apps/authentication/errors/redirect.py index bf334133d..466ec708d 100644 --- a/apps/authentication/errors/redirect.py +++ b/apps/authentication/errors/redirect.py @@ -69,10 +69,16 @@ class LoginConfirmWaitError(LoginConfirmBaseError): class LoginConfirmOtherError(LoginConfirmBaseError): error = 'login_confirm_error' - def __init__(self, ticket_id, status): + def __init__(self, ticket_id, status, username): + self.username = username msg = const.login_confirm_error_msg.format(status) super().__init__(ticket_id=ticket_id, msg=msg) + def as_data(self): + ret = super().as_data() + ret['data']['username'] = self.username + return ret + class PasswordTooSimple(NeedRedirectError): default_code = 'passwd_too_simple' diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py index 739048d75..7341b4bd1 100644 --- a/apps/authentication/mixins.py +++ b/apps/authentication/mixins.py @@ -377,7 +377,10 @@ class AuthACLMixin: raise errors.LoginConfirmWaitError(ticket.id) else: # rejected, closed - raise errors.LoginConfirmOtherError(ticket.id, ticket.get_state_display()) + ticket_id = ticket.id + status = ticket.get_state_display() + username = ticket.applicant.username + raise errors.LoginConfirmOtherError(ticket_id, status, username) def get_ticket(self): from tickets.models import ApplyLoginTicket From 88d9078c43f118766d508c487ca3ae408b4f09b5 Mon Sep 17 00:00:00 2001 From: "Jiangjie.Bai" Date: Mon, 15 Aug 2022 16:52:45 +0800 Subject: [PATCH 05/10] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=20OAuth2.0=20?= =?UTF-8?q?=E8=AE=A4=E8=AF=81=E7=9A=84=E5=AD=97=E6=AE=B5=E7=9A=84=E5=BF=85?= =?UTF-8?q?=E5=A1=AB=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/serializers/auth/oauth2.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/settings/serializers/auth/oauth2.py b/apps/settings/serializers/auth/oauth2.py index 2c620d331..279f3c34f 100644 --- a/apps/settings/serializers/auth/oauth2.py +++ b/apps/settings/serializers/auth/oauth2.py @@ -17,39 +17,39 @@ class SettingImageField(serializers.ImageField): class OAuth2SettingSerializer(serializers.Serializer): AUTH_OAUTH2 = serializers.BooleanField( - default=False, required=False, label=_('Enable OAuth2 Auth') + default=False, label=_('Enable OAuth2 Auth') ) AUTH_OAUTH2_LOGO_PATH = SettingImageField( allow_null=True, required=False, label=_('Logo') ) AUTH_OAUTH2_PROVIDER = serializers.CharField( - required=False, max_length=16, label=_('Service provider') + required=True, max_length=16, label=_('Service provider') ) AUTH_OAUTH2_CLIENT_ID = serializers.CharField( - required=False, max_length=1024, label=_('Client Id') + required=True, max_length=1024, label=_('Client Id') ) AUTH_OAUTH2_CLIENT_SECRET = EncryptedField( required=False, max_length=1024, label=_('Client Secret') ) AUTH_OAUTH2_SCOPE = serializers.CharField( - required=False, max_length=1024, label=_('Scope'), allow_blank=True + required=True, max_length=1024, label=_('Scope'), allow_blank=True ) AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT = serializers.CharField( - required=False, max_length=1024, label=_('Provider auth endpoint') + required=True, max_length=1024, label=_('Provider auth endpoint') ) AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT = serializers.CharField( - required=False, max_length=1024, label=_('Provider token endpoint') + required=True, max_length=1024, label=_('Provider token endpoint') ) AUTH_OAUTH2_ACCESS_TOKEN_METHOD = serializers.ChoiceField( default='GET', label=_('Client authentication method'), choices=(('GET', 'GET'), ('POST', 'POST')) ) AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT = serializers.CharField( - required=False, max_length=1024, label=_('Provider userinfo endpoint') + required=True, max_length=1024, label=_('Provider userinfo endpoint') ) AUTH_OAUTH2_USER_ATTR_MAP = serializers.DictField( - required=False, label=_('User attr map') + required=True, label=_('User attr map') ) AUTH_OAUTH2_ALWAYS_UPDATE_USER = serializers.BooleanField( - required=False, label=_('Always update user') + default=True, label=_('Always update user') ) From 78133b0c60448e410f24c5f4a78c30b47fa8509c Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 15 Aug 2022 17:39:54 +0800 Subject: [PATCH 06/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E6=89=8B=E6=9C=BA=E5=8F=B7=E6=A0=A1=E9=AA=8C=20(#8747?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng626 <1304903146@qq.com> --- apps/settings/serializers/auth/sms.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/settings/serializers/auth/sms.py b/apps/settings/serializers/auth/sms.py index d3f96b33f..1278dea06 100644 --- a/apps/settings/serializers/auth/sms.py +++ b/apps/settings/serializers/auth/sms.py @@ -2,6 +2,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from common.drf.fields import EncryptedField +from common.validators import PhoneValidator from common.sdk.sms import BACKENDS __all__ = [ @@ -23,7 +24,10 @@ class SignTmplPairSerializer(serializers.Serializer): class BaseSMSSettingSerializer(serializers.Serializer): - SMS_TEST_PHONE = serializers.CharField(max_length=256, required=False, allow_blank=True, label=_('Test phone')) + SMS_TEST_PHONE = serializers.CharField( + max_length=256, required=False, validators=[PhoneValidator(), ], + allow_blank=True, label=_('Test phone') + ) def to_representation(self, instance): data = super().to_representation(instance) From 2f8a07e665973058cdb4a1d5b1a42220eac26021 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:56:19 +0800 Subject: [PATCH 07/10] =?UTF-8?q?perf:=20=E6=89=B9=E9=87=8F=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E6=96=B0=E5=A2=9E=E8=BF=87=E6=BB=A4=E9=80=89=E9=A1=B9?= =?UTF-8?q?=20(#8749)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng626 <1304903146@qq.com> --- apps/audits/api.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/audits/api.py b/apps/audits/api.py index fb1efe989..ca0e4d86a 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -126,9 +126,12 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet): class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet): serializer_class = CommandExecutionHostsRelationSerializer m2m_field = CommandExecution.hosts.field - filterset_fields = [ - 'id', 'asset', 'commandexecution' - ] + filterset_fields = { + 'id': ['exact'], + 'asset': ['exact'], + 'asset__hostname': ['icontains'], + 'commandexecution': ['exact'], + } search_fields = ('asset__hostname', ) http_method_names = ['options', 'get'] rbac_perms = { From 4cad5affecc147d9028691d9cf81c4d22e0a0adb Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:15:21 +0800 Subject: [PATCH 08/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E7=81=AB=E7=8B=90=E6=B5=8F=E8=A7=88=E5=99=A8=E4=B8=8A?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E5=B1=95=E7=A4=BA=20(#8753)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng626 <1304903146@qq.com> --- apps/tickets/notifications.py | 2 +- apps/tickets/templates/tickets/ticket_approve_diff.html | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/tickets/notifications.py b/apps/tickets/notifications.py index 3fa0e5a82..26997b0dc 100644 --- a/apps/tickets/notifications.py +++ b/apps/tickets/notifications.py @@ -87,7 +87,7 @@ class BaseTicketMessage(UserMessage): @property def spec_items(self): fields = self.ticket._meta.local_fields + self.ticket._meta.local_many_to_many - excludes = ['ticket_ptr'] + excludes = ['ticket_ptr', 'flow'] item_names = [field.name for field in fields if field.name not in excludes] return self._get_fields_items(item_names) diff --git a/apps/tickets/templates/tickets/ticket_approve_diff.html b/apps/tickets/templates/tickets/ticket_approve_diff.html index 9fe6b80e0..8426b34ed 100644 --- a/apps/tickets/templates/tickets/ticket_approve_diff.html +++ b/apps/tickets/templates/tickets/ticket_approve_diff.html @@ -2,6 +2,7 @@

{{ approve_info }}

+{% if content %}
@@ -20,5 +21,6 @@
+{% endif %} From 68841d1f15e7877b50877d8394d8cfb7ec7b51da Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:24:58 +0800 Subject: [PATCH 09/10] =?UTF-8?q?fix:=20=E9=85=8D=E7=BD=AE=E4=BB=85?= =?UTF-8?q?=E5=B7=B2=E5=AD=98=E5=9C=A8=E7=94=A8=E6=88=B7=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=90=8E=20cas=E7=94=A8=E6=88=B7=E9=A6=96=E6=AC=A1=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=8A=A5403=20(#8752)?= 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/cas/urls.py | 3 ++- apps/authentication/backends/cas/views.py | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 apps/authentication/backends/cas/views.py diff --git a/apps/authentication/backends/cas/urls.py b/apps/authentication/backends/cas/urls.py index 39a838b6a..376ce2332 100644 --- a/apps/authentication/backends/cas/urls.py +++ b/apps/authentication/backends/cas/urls.py @@ -3,9 +3,10 @@ from django.urls import path import django_cas_ng.views +from .views import CASLoginView urlpatterns = [ - path('login/', django_cas_ng.views.LoginView.as_view(), name='cas-login'), + path('login/', CASLoginView.as_view(), name='cas-login'), path('logout/', django_cas_ng.views.LogoutView.as_view(), name='cas-logout'), path('callback/', django_cas_ng.views.CallbackView.as_view(), name='cas-proxy-callback'), ] diff --git a/apps/authentication/backends/cas/views.py b/apps/authentication/backends/cas/views.py new file mode 100644 index 000000000..f74e46e9c --- /dev/null +++ b/apps/authentication/backends/cas/views.py @@ -0,0 +1,15 @@ +from django_cas_ng.views import LoginView +from django.core.exceptions import PermissionDenied +from django.http import HttpResponseRedirect + +__all__ = ['LoginView'] + + +class CASLoginView(LoginView): + def get(self, request): + try: + return super().get(request) + except PermissionDenied: + return HttpResponseRedirect('/') + + From d405bae2058cc9641c2c55d2c03473b2316a8b49 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:46:17 +0800 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E5=A4=B1=E8=B4=A5=E5=90=8E=E9=94=99=E8=AF=AF=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=80=BB=E6=98=AF=20IP=20block=20=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#8755)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jiangjie.Bai --- apps/authentication/errors/failed.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/authentication/errors/failed.py b/apps/authentication/errors/failed.py index ec43d1b73..c85695fe5 100644 --- a/apps/authentication/errors/failed.py +++ b/apps/authentication/errors/failed.py @@ -56,7 +56,8 @@ class BlockGlobalIpLoginError(AuthFailedError): error = 'block_global_ip_login' def __init__(self, username, ip, **kwargs): - self.msg = const.block_ip_login_msg.format(settings.SECURITY_LOGIN_IP_LIMIT_TIME) + if not self.msg: + self.msg = const.block_ip_login_msg.format(settings.SECURITY_LOGIN_IP_LIMIT_TIME) LoginIpBlockUtil(ip).set_block_if_need() super().__init__(username=username, ip=ip, **kwargs) @@ -66,22 +67,21 @@ class CredentialError( BlockGlobalIpLoginError, AuthFailedError ): def __init__(self, error, username, ip, request): - super().__init__(error=error, username=username, ip=ip, request=request) util = LoginBlockUtil(username, ip) times_remainder = util.get_remainder_times() block_time = settings.SECURITY_LOGIN_LIMIT_TIME - if times_remainder < 1: self.msg = const.block_user_login_msg.format(settings.SECURITY_LOGIN_LIMIT_TIME) - return - - default_msg = const.invalid_login_msg.format( - times_try=times_remainder, block_time=block_time - ) - if error == const.reason_password_failed: - self.msg = default_msg else: - self.msg = const.reason_choices.get(error, default_msg) + default_msg = const.invalid_login_msg.format( + times_try=times_remainder, block_time=block_time + ) + if error == const.reason_password_failed: + self.msg = default_msg + else: + self.msg = const.reason_choices.get(error, default_msg) + # 先处理 msg 在 super,记录日志时原因才准确 + super().__init__(error=error, username=username, ip=ip, request=request) class MFAFailedError(AuthFailedNeedLogMixin, AuthFailedError):