From bae438706893ca68b664f91c3d5ee0e2ca5dde4b Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 5 Nov 2019 19:44:34 +0800 Subject: [PATCH 01/17] =?UTF-8?q?[Update]=20tree=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/templates/assets/_node_tree.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/assets/templates/assets/_node_tree.html b/apps/assets/templates/assets/_node_tree.html index c76d3d685..803c29c13 100644 --- a/apps/assets/templates/assets/_node_tree.html +++ b/apps/assets/templates/assets/_node_tree.html @@ -32,8 +32,7 @@ } - -
+
@@ -306,6 +305,7 @@ function defaultCallback(action) { $(document).ready(function () { + $('.treebox').css('height', window.innerHeight - 180); }) .on('click', '.btn-show-current-asset', function(){ hideRMenu(); @@ -322,4 +322,4 @@ $(document).ready(function () { location.reload(); }) - \ No newline at end of file + From eedaaddbf547c2f2de212f77ebe76f2e0bc160a1 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 6 Nov 2019 11:57:00 +0800 Subject: [PATCH 02/17] =?UTF-8?q?[Update]=20=E7=94=A8=E6=88=B7=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=94=AF=E6=8C=81=E4=BF=AE=E6=94=B9source?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/assets/_asset_list_modal.html | 2 +- apps/jumpserver/conf.py | 1 + apps/settings/api.py | 25 ++++++- apps/settings/serializers.py | 3 + apps/settings/urls/api_urls.py | 1 + apps/users/forms.py | 22 +++++- apps/users/serializers/__init__.py | 3 +- apps/users/serializers/group.py | 69 +++++++++++++++++++ apps/users/serializers/{v1.py => user.py} | 60 +--------------- apps/users/templates/users/_user.html | 1 + 10 files changed, 122 insertions(+), 65 deletions(-) create mode 100644 apps/users/serializers/group.py rename apps/users/serializers/{v1.py => user.py} (69%) diff --git a/apps/assets/templates/assets/_asset_list_modal.html b/apps/assets/templates/assets/_asset_list_modal.html index 8d8c3f0ba..4c6eb7199 100644 --- a/apps/assets/templates/assets/_asset_list_modal.html +++ b/apps/assets/templates/assets/_asset_list_modal.html @@ -25,7 +25,7 @@
-
+
diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 58ca170c6..b76dac415 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -395,6 +395,7 @@ defaults = { 'FLOWER_URL': "127.0.0.1:5555", 'DEFAULT_ORG_SHOW_ALL_USERS': True, 'PERIOD_TASK_ENABLED': True, + 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': True, } diff --git a/apps/settings/api.py b/apps/settings/api.py index 22a295b68..426cdf76d 100644 --- a/apps/settings/api.py +++ b/apps/settings/api.py @@ -12,11 +12,14 @@ from django.conf import settings from django.core.mail import send_mail from django.utils.translation import ugettext_lazy as _ -from .models import Setting -from .utils import LDAPUtil from common.permissions import IsOrgAdmin, IsSuperUser from common.utils import get_logger -from .serializers import MailTestSerializer, LDAPTestSerializer, LDAPUserSerializer +from .models import Setting +from .utils import LDAPUtil +from .serializers import ( + MailTestSerializer, LDAPTestSerializer, LDAPUserSerializer, + PublicSettingSerializer, +) logger = get_logger(__file__) @@ -245,3 +248,19 @@ class CommandStorageDeleteAPI(APIView): storage_name = str(request.data.get('name')) Setting.delete_storage('TERMINAL_COMMAND_STORAGE', storage_name) return Response({"msg": _('Delete succeed')}, status=200) + + +class PublicSettingApi(generics.RetrieveAPIView): + permission_classes = () + serializer_class = PublicSettingSerializer + + def get_object(self): + c = settings.CONFIG + instance = { + "data": { + "WINDOWS_SKIP_ALL_MANUAL_PASSWORD": c.WINDOWS_SKIP_ALL_MANUAL_PASSWORD + } + } + return instance + + diff --git a/apps/settings/serializers.py b/apps/settings/serializers.py index eb8a61679..f29b514d7 100644 --- a/apps/settings/serializers.py +++ b/apps/settings/serializers.py @@ -28,3 +28,6 @@ class LDAPUserSerializer(serializers.Serializer): email = serializers.CharField() existing = serializers.BooleanField(read_only=True) + +class PublicSettingSerializer(serializers.Serializer): + data = serializers.DictField(read_only=True) diff --git a/apps/settings/urls/api_urls.py b/apps/settings/urls/api_urls.py index bc2e4731f..026598206 100644 --- a/apps/settings/urls/api_urls.py +++ b/apps/settings/urls/api_urls.py @@ -15,4 +15,5 @@ urlpatterns = [ path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'), path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'), path('terminal/command-storage/delete/', api.CommandStorageDeleteAPI.as_view(), name='command-storage-delete'), + path('public/', api.PublicSettingApi.as_view(), name='public-setting'), ] diff --git a/apps/users/forms.py b/apps/users/forms.py index 98d7c9e09..649f66ab9 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -2,6 +2,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ +from django.conf import settings from common.utils import validate_ssh_public_key from orgs.mixins.forms import OrgModelForm @@ -21,6 +22,20 @@ class UserCheckOtpCodeForm(forms.Form): otp_code = forms.CharField(label=_('MFA code'), max_length=6) +def get_source_choices(): + choices_all = dict(User.SOURCE_CHOICES) + choices = [ + (User.SOURCE_LOCAL, choices_all[User.SOURCE_LOCAL]), + ] + if settings.AUTH_LDAP: + choices.append((User.SOURCE_LDAP, choices_all[User.SOURCE_LDAP])) + if settings.AUTH_OPENID: + choices.append((User.SOURCE_OPENID, choices_all[User.SOURCE_OPENID])) + if settings.AUTH_RADIUS: + choices.append((User.SOURCE_RADIUS, choices_all[User.SOURCE_RADIUS])) + return choices + + class UserCreateUpdateFormMixin(OrgModelForm): role_choices = ((i, n) for i, n in User.ROLE_CHOICES if i != User.ROLE_APP) password = forms.CharField( @@ -31,6 +46,10 @@ class UserCreateUpdateFormMixin(OrgModelForm): choices=role_choices, required=True, initial=User.ROLE_USER, label=_("Role") ) + source = forms.ChoiceField( + choices=get_source_choices, required=True, + initial=User.SOURCE_LOCAL, label=_("Source") + ) public_key = forms.CharField( label=_('ssh public key'), max_length=5000, required=False, widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), @@ -41,7 +60,8 @@ class UserCreateUpdateFormMixin(OrgModelForm): model = User fields = [ 'username', 'name', 'email', 'groups', 'wechat', - 'phone', 'role', 'date_expired', 'comment', 'otp_level' + 'source', 'phone', 'role', 'date_expired', + 'comment', 'otp_level' ] widgets = { 'otp_level': forms.RadioSelect(), diff --git a/apps/users/serializers/__init__.py b/apps/users/serializers/__init__.py index 94ef71f28..78a695e51 100644 --- a/apps/users/serializers/__init__.py +++ b/apps/users/serializers/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- # -from .v1 import * \ No newline at end of file +from .user import * +from .group import * diff --git a/apps/users/serializers/group.py b/apps/users/serializers/group.py new file mode 100644 index 000000000..d27ddc19a --- /dev/null +++ b/apps/users/serializers/group.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from common.fields import StringManyToManyField +from common.serializers import AdaptedBulkListSerializer +from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from ..models import User, UserGroup +from .. import utils + + +__all__ = [ + 'UserGroupSerializer', 'UserGroupListSerializer', + 'UserGroupUpdateMemberSerializer' +] + + +class UserGroupSerializer(BulkOrgResourceModelSerializer): + users = serializers.PrimaryKeyRelatedField( + required=False, many=True, queryset=User.objects, label=_('User') + ) + + class Meta: + model = UserGroup + list_serializer_class = AdaptedBulkListSerializer + fields = [ + 'id', 'name', 'users', 'comment', 'date_created', + 'created_by', + ] + extra_kwargs = { + 'created_by': {'label': _('Created by'), 'read_only': True} + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_fields_queryset() + + def set_fields_queryset(self): + users_field = self.fields['users'] + users_field.child_relation.queryset = utils.get_current_org_members() + + def validate_users(self, users): + for user in users: + if user.is_super_auditor: + msg = _('Auditors cannot be join in the user group') + raise serializers.ValidationError(msg) + return users + + +class UserGroupListSerializer(UserGroupSerializer): + users = StringManyToManyField(many=True, read_only=True) + + +class UserGroupUpdateMemberSerializer(serializers.ModelSerializer): + users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects) + + class Meta: + model = UserGroup + fields = ['id', 'users'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_fields_queryset() + + def set_fields_queryset(self): + users_field = self.fields['users'] + users_field.child_relation.queryset = utils.get_current_org_members() + diff --git a/apps/users/serializers/v1.py b/apps/users/serializers/user.py similarity index 69% rename from apps/users/serializers/v1.py rename to apps/users/serializers/user.py index 847afe885..57e2f43fa 100644 --- a/apps/users/serializers/v1.py +++ b/apps/users/serializers/user.py @@ -6,19 +6,14 @@ from rest_framework import serializers from common.utils import validate_ssh_public_key from common.mixins import BulkSerializerMixin -from common.fields import StringManyToManyField from common.serializers import AdaptedBulkListSerializer from common.permissions import CanUpdateDeleteUser -from orgs.mixins.serializers import BulkOrgResourceModelSerializer from ..models import User, UserGroup -from .. import utils __all__ = [ 'UserSerializer', 'UserPKUpdateSerializer', 'UserUpdateGroupSerializer', - 'UserGroupSerializer', 'UserGroupListSerializer', - 'UserGroupUpdateMemberSerializer', 'ChangeUserPasswordSerializer', - 'ResetOTPSerializer', + 'ChangeUserPasswordSerializer', 'ResetOTPSerializer', ] @@ -49,7 +44,6 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): 'is_valid': {'label': _('Is valid')}, 'is_expired': {'label': _('Is expired')}, 'avatar_url': {'label': _('Avatar url')}, - 'source': {'read_only': True}, 'created_by': {'read_only': True, 'allow_blank': True}, 'can_update': {'read_only': True}, 'can_delete': {'read_only': True}, @@ -127,58 +121,6 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer): fields = ['id', 'groups'] -class UserGroupSerializer(BulkOrgResourceModelSerializer): - users = serializers.PrimaryKeyRelatedField( - required=False, many=True, queryset=User.objects, label=_('User') - ) - - class Meta: - model = UserGroup - list_serializer_class = AdaptedBulkListSerializer - fields = [ - 'id', 'name', 'users', 'comment', 'date_created', - 'created_by', - ] - extra_kwargs = { - 'created_by': {'label': _('Created by'), 'read_only': True} - } - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.set_fields_queryset() - - def set_fields_queryset(self): - users_field = self.fields['users'] - users_field.child_relation.queryset = utils.get_current_org_members() - - def validate_users(self, users): - for user in users: - if user.is_super_auditor: - msg = _('Auditors cannot be join in the user group') - raise serializers.ValidationError(msg) - return users - - -class UserGroupListSerializer(UserGroupSerializer): - users = StringManyToManyField(many=True, read_only=True) - - -class UserGroupUpdateMemberSerializer(serializers.ModelSerializer): - users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects) - - class Meta: - model = UserGroup - fields = ['id', 'users'] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.set_fields_queryset() - - def set_fields_queryset(self): - users_field = self.fields['users'] - users_field.child_relation.queryset = utils.get_current_org_members() - - class ChangeUserPasswordSerializer(serializers.ModelSerializer): class Meta: diff --git a/apps/users/templates/users/_user.html b/apps/users/templates/users/_user.html index 192dbfb70..ea0f76854 100644 --- a/apps/users/templates/users/_user.html +++ b/apps/users/templates/users/_user.html @@ -21,6 +21,7 @@

{% trans 'Auth' %}

{% block password %}{% endblock %} {% bootstrap_field form.otp_level layout="horizontal" %} + {% bootstrap_field form.source layout="horizontal" %}

{% trans 'Security and Role' %}

From ed5a57042a2cafa5a8bb7b0e753181f05dc4c2d8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 6 Nov 2019 14:28:49 +0800 Subject: [PATCH 03/17] =?UTF-8?q?[Update]=20windows=E8=B7=B3=E8=BF=87?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=E8=BE=93=E5=85=A5=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index b76dac415..f7f35c215 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -395,7 +395,7 @@ defaults = { 'FLOWER_URL': "127.0.0.1:5555", 'DEFAULT_ORG_SHOW_ALL_USERS': True, 'PERIOD_TASK_ENABLED': True, - 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': True, + 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False, } From fb9e2ac9f633b695e240057847e3f86ebfcf2978 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 6 Nov 2019 17:18:39 +0800 Subject: [PATCH 04/17] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=92=8C=E7=94=A8=E6=88=B7=E7=BB=84=E5=85=B3=E7=B3=BB?= =?UTF-8?q?=E5=8F=91=E7=94=9F=E5=8F=98=E5=8C=96=E6=97=B6=EF=BC=8C=E5=88=B7?= =?UTF-8?q?=E6=96=B0=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/perms/signals_handler.py | 7 ------- apps/users/signals_handler.py | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/perms/signals_handler.py b/apps/perms/signals_handler.py index eecd101e8..4a7fe389c 100644 --- a/apps/perms/signals_handler.py +++ b/apps/perms/signals_handler.py @@ -11,13 +11,6 @@ from .utils.asset_permission import AssetPermissionUtilV2 logger = get_logger(__file__) -permission_m2m_senders = ( - AssetPermission.nodes.through, - AssetPermission.assets.through, - AssetPermission.users.through, - AssetPermission.user_groups.through, -) - @receiver([post_save, post_delete], sender=AssetPermission) @on_transaction_commit diff --git a/apps/users/signals_handler.py b/apps/users/signals_handler.py index 4c6afc663..a33d9eec9 100644 --- a/apps/users/signals_handler.py +++ b/apps/users/signals_handler.py @@ -2,11 +2,11 @@ # from django.dispatch import receiver -# from django.db.models.signals import post_save +from django.db.models.signals import post_save, m2m_changed from common.utils import get_logger from .signals import post_user_create -# from .models import User +from .models import User logger = get_logger(__file__) @@ -28,3 +28,14 @@ def on_user_create(sender, user=None, **kwargs): logger.info(" - Sending welcome mail ...".format(user.name)) if user.email: send_user_created_mail(user) + + +@receiver(m2m_changed, sender=User.groups.through) +def on_user_groups_change(sender, instance=None, action='', **kwargs): + """ + 资产节点发生变化时,刷新节点 + """ + if action.startswith('post'): + logger.debug("User group member change signal recv: {}".format(instance)) + from perms.utils import AssetPermissionUtilV2 + AssetPermissionUtilV2.expire_all_user_tree_cache() From f4136decde92a4ae288b7068be8b553b41e5ef54 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 11 Nov 2019 11:37:34 +0800 Subject: [PATCH 05/17] [Update] Stash --- apps/assets/models/node.py | 2 +- apps/assets/templates/assets/asset_list.html | 23 +++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index dd1f706f2..43ea83a22 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -67,7 +67,7 @@ class TreeMixin: @classmethod def refresh_node_assets(cls, t=None): - logger.debug("Refresh node tree assets") + logger.debug("Refresh node assets") key = cls.tree_assets_cache_key ttl = cls.tree_cache_time if not t: diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index be267cc4d..fcc17c87d 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -426,13 +426,15 @@ $(document).ready(function(){ function success(data) { url = setUrlParam(the_url, 'spm', data.spm); requestApi({ - url:url, - method:'DELETE', - success:refreshPage, - flash_message:false, + url: url, + method: 'DELETE', + success: function () { + var msg = "{% trans 'Asset Deleted.' %}"; + swal("{% trans 'Asset Delete' %}", msg, "success"); + refreshPage(); + }, + flash_message: false, }); - var msg = "{% trans 'Asset Deleted.' %}"; - swal("{% trans 'Asset Delete' %}", msg, "success"); } function fail() { var msg = "{% trans 'Asset Deleting failed.' %}"; @@ -440,10 +442,11 @@ $(document).ready(function(){ } requestApi({ url: "{% url 'api-common:resources-cache' %}", - method:'POST', - body:JSON.stringify(data), - success:success, - error:fail + method: 'POST', + body: JSON.stringify(data), + success: success, + error: fail, + flash_message: false }) }) } From 596e5a6dd1bfc172afe5173a42b00c2b0dccbbf3 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Nov 2019 16:41:32 +0800 Subject: [PATCH 06/17] =?UTF-8?q?[Update]=20=E9=87=8D=E6=9E=84=20LDAP/AD?= =?UTF-8?q?=20=E5=90=8C=E6=AD=A5=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=BC=93=E5=AD=98=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/signals_handlers.py | 2 +- apps/settings/api.py | 163 ++++++--- apps/settings/serializers.py | 1 + apps/settings/tasks/__init__.py | 4 + apps/settings/tasks/ldap.py | 17 + .../settings/_ldap_list_users_modal.html | 84 ++++- .../templates/settings/ldap_setting.html | 27 -- apps/settings/urls/api_urls.py | 3 +- apps/settings/utils.py | 219 ------------ apps/settings/utils/__init__.py | 4 + apps/settings/utils/ldap.py | 336 ++++++++++++++++++ apps/settings/views.py | 2 + apps/users/tasks.py | 24 +- 13 files changed, 569 insertions(+), 317 deletions(-) create mode 100644 apps/settings/tasks/__init__.py create mode 100644 apps/settings/tasks/ldap.py delete mode 100644 apps/settings/utils.py create mode 100644 apps/settings/utils/__init__.py create mode 100644 apps/settings/utils/ldap.py diff --git a/apps/authentication/signals_handlers.py b/apps/authentication/signals_handlers.py index c0b48c61d..b894e0651 100644 --- a/apps/authentication/signals_handlers.py +++ b/apps/authentication/signals_handlers.py @@ -47,7 +47,7 @@ def on_openid_login_success(sender, user=None, request=None, **kwargs): @receiver(populate_user) def on_ldap_create_user(sender, user, ldap_user, **kwargs): - if user and user.username != 'admin': + if user and user.username not in ['admin']: user.source = user.SOURCE_LDAP user.save() diff --git a/apps/settings/api.py b/apps/settings/api.py index 22a295b68..c226c9205 100644 --- a/apps/settings/api.py +++ b/apps/settings/api.py @@ -13,10 +13,16 @@ from django.core.mail import send_mail from django.utils.translation import ugettext_lazy as _ from .models import Setting -from .utils import LDAPUtil +from .utils import ( + LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil, + LDAP_USE_CACHE_FLAGS + +) +from .tasks import sync_ldap_user_task from common.permissions import IsOrgAdmin, IsSuperUser from common.utils import get_logger from .serializers import MailTestSerializer, LDAPTestSerializer, LDAPUserSerializer +from users.models import User logger = get_logger(__file__) @@ -67,65 +73,107 @@ class LDAPTestingAPI(APIView): success_message = _("Test ldap success") @staticmethod - def get_ldap_util(serializer): - host = serializer.validated_data["AUTH_LDAP_SERVER_URI"] + def get_ldap_config(serializer): + server_uri = serializer.validated_data["AUTH_LDAP_SERVER_URI"] bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"] password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"] use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False) search_ougroup = serializer.validated_data["AUTH_LDAP_SEARCH_OU"] search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"] attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"] - try: - attr_map = json.loads(attr_map) - except json.JSONDecodeError: - return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401) - - util = LDAPUtil( - use_settings_config=False, server_uri=host, bind_dn=bind_dn, - password=password, use_ssl=use_ssl, - search_ougroup=search_ougroup, search_filter=search_filter, - attr_map=attr_map - ) - return util + config = { + 'server_uri': server_uri, + 'bind_dn': bind_dn, + 'password': password, + 'use_ssl': use_ssl, + 'search_ougroup': search_ougroup, + 'search_filter': search_filter, + 'attr_map': json.loads(attr_map), + } + return config def post(self, request): serializer = self.serializer_class(data=request.data) if not serializer.is_valid(): return Response({"error": str(serializer.errors)}, status=401) - util = self.get_ldap_util(serializer) - + attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"] try: - users = util.search_user_items() + json.loads(attr_map) + except json.JSONDecodeError: + return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401) + + config = self.get_ldap_config(serializer) + util = LDAPServerUtil(config=config) + try: + users = util.search() except Exception as e: return Response({"error": str(e)}, status=401) - if len(users) > 0: - return Response({"msg": _("Match {} s users").format(len(users))}) - else: - return Response({"error": "Have user but attr mapping error"}, status=401) + return Response({"msg": _("Match {} s users").format(len(users))}) class LDAPUserListApi(generics.ListAPIView): permission_classes = (IsOrgAdmin,) serializer_class = LDAPUserSerializer + def get_queryset_from_cache(self): + search_value = self.request.query_params.get('search') + users = LDAPCacheUtil().search(search_value=search_value) + return users + + def get_queryset_from_server(self): + search_value = self.request.query_params.get('search') + users = LDAPServerUtil().search(search_value=search_value) + return users + def get_queryset(self): if hasattr(self, 'swagger_fake_view'): return [] - q = self.request.query_params.get('search') - try: - util = LDAPUtil() - extra_filter = util.construct_extra_filter(util.SEARCH_FIELD_ALL, q) - users = util.search_user_items(extra_filter) - except Exception as e: - users = [] - logger.error(e) - # 前端data_table会根据row.id对table.selected值进行操作 - for user in users: - user['id'] = user['username'] + cache_police = self.request.query_params.get('cache_police', True) + if cache_police in LDAP_USE_CACHE_FLAGS: + users = self.get_queryset_from_cache() + else: + users = self.get_queryset_from_server() return users + def list(self, request, *args, **kwargs): + cache_police = self.request.query_params.get('cache_police', True) + # 不是用缓存 + if cache_police not in LDAP_USE_CACHE_FLAGS: + return super().list(request, *args, **kwargs) + + queryset = self.get_queryset() + # 缓存有数据 + if queryset is not None: + return super().list(request, *args, **kwargs) + + sync_util = LDAPSyncUtil() + # 还没有同步任务 + if sync_util.task_no_start: + task = sync_ldap_user_task.delay() + data = {'msg': 'Cache no data, sync task {} started.'.format(task.id)} + return Response(data=data, status=409) + # 同步任务正在执行 + if sync_util.task_is_running: + data = {'msg': 'synchronization is running.'} + return Response(data=data, status=409) + # 同步任务执行结束 + if sync_util.task_is_over: + msg = sync_util.get_task_error_msg() + data = {'msg': 'Synchronization task report error: {}'.format(msg)} + return Response(data=data, status=400) + + return super().list(request, *args, **kwargs) + + @staticmethod + def processing_queryset(queryset): + db_username_list = User.objects.all().values_list('username', flat=True) + for q in queryset: + q['id'] = q['username'] + q['existing'] = q['username'] in db_username_list + return queryset + def sort_queryset(self, queryset): order_by = self.request.query_params.get('order') if not order_by: @@ -138,32 +186,41 @@ class LDAPUserListApi(generics.ListAPIView): queryset = sorted(queryset, key=lambda x: x[order_by], reverse=reverse) return queryset - def list(self, request, *args, **kwargs): - queryset = self.get_queryset() + def filter_queryset(self, queryset): + queryset = self.processing_queryset(queryset) queryset = self.sort_queryset(queryset) - page = self.paginate_queryset(queryset) - if page is not None: - return self.get_paginated_response(page) - return Response(queryset) + return queryset -class LDAPUserSyncAPI(APIView): +class LDAPUserImportAPI(APIView): permission_classes = (IsOrgAdmin,) - def post(self, request): - username_list = request.data.get('username_list', []) - - util = LDAPUtil() - try: - result = util.sync_users(username_list) - except Exception as e: - logger.error(e, exc_info=True) - return Response({'error': str(e)}, status=401) + def get_ldap_users(self): + username_list = self.request.data.get('username_list', []) + cache_police = self.request.query_params.get('cache_police', True) + if cache_police in LDAP_USE_CACHE_FLAGS: + users = LDAPCacheUtil().search(search_users=username_list) else: - msg = _("succeed: {} failed: {} total: {}").format( - result['succeed'], result['failed'], result['total'] - ) - return Response({'msg': msg}) + users = LDAPServerUtil().search(search_users=username_list) + return users + + def post(self, request): + users = self.get_ldap_users() + errors = LDAPImportUtil().perform_import(users) + if errors: + return Response({'Error': errors}, status=401) + return Response({'msg': 'Imported {} users successfully'.format(len(users))}) + + +class LDAPCacheRefreshAPI(generics.RetrieveAPIView): + + def retrieve(self, request, *args, **kwargs): + try: + LDAPSyncUtil().clear_cache() + except Exception as e: + logger.error(str(e)) + return Response(data={'msg': str(e)}, status=400) + return Response(data={'msg': 'success'}) class ReplayStorageCreateAPI(APIView): diff --git a/apps/settings/serializers.py b/apps/settings/serializers.py index eb8a61679..0e2e48fa5 100644 --- a/apps/settings/serializers.py +++ b/apps/settings/serializers.py @@ -25,6 +25,7 @@ class LDAPTestSerializer(serializers.Serializer): class LDAPUserSerializer(serializers.Serializer): id = serializers.CharField() username = serializers.CharField() + name = serializers.CharField() email = serializers.CharField() existing = serializers.BooleanField(read_only=True) diff --git a/apps/settings/tasks/__init__.py b/apps/settings/tasks/__init__.py new file mode 100644 index 000000000..87bc6198f --- /dev/null +++ b/apps/settings/tasks/__init__.py @@ -0,0 +1,4 @@ +# coding: utf-8 +# + +from .ldap import * diff --git a/apps/settings/tasks/ldap.py b/apps/settings/tasks/ldap.py new file mode 100644 index 000000000..60058e03e --- /dev/null +++ b/apps/settings/tasks/ldap.py @@ -0,0 +1,17 @@ +# coding: utf-8 +# + +from celery import shared_task + +from common.utils import get_logger +from ..utils import LDAPSyncUtil + +__all__ = ['sync_ldap_user_task'] + + +logger = get_logger(__file__) + + +@shared_task +def sync_ldap_user_task(): + LDAPSyncUtil().perform_sync() diff --git a/apps/settings/templates/settings/_ldap_list_users_modal.html b/apps/settings/templates/settings/_ldap_list_users_modal.html index dd839eb5b..cc63f72c5 100644 --- a/apps/settings/templates/settings/_ldap_list_users_modal.html +++ b/apps/settings/templates/settings/_ldap_list_users_modal.html @@ -23,6 +23,7 @@
+ @@ -43,8 +44,11 @@ diff --git a/apps/settings/templates/settings/ldap_setting.html b/apps/settings/templates/settings/ldap_setting.html index 694fb66f9..e1d7af7a6 100644 --- a/apps/settings/templates/settings/ldap_setting.html +++ b/apps/settings/templates/settings/ldap_setting.html @@ -109,33 +109,6 @@ $(document).ready(function () { error: error }); }) -.on("click","#btn_ldap_modal_confirm",function () { - var username_list = ldap_users_table.selected; - - if (username_list.length === 0){ - var msg = "{% trans 'User is not currently selected, please check the user you want to import'%}"; - toastr.error(msg); - return - } - - var the_url = "{% url "api-settings:ldap-user-sync" %}"; - - function error(message) { - toastr.error(message) - } - - function success(message) { - toastr.success(message.msg) - } - requestApi({ - url: the_url, - body: JSON.stringify({'username_list':username_list}), - method: "POST", - flash_message: false, - success: success, - error: error - }); - }) {% endblock %} diff --git a/apps/settings/urls/api_urls.py b/apps/settings/urls/api_urls.py index bc2e4731f..ee35be25d 100644 --- a/apps/settings/urls/api_urls.py +++ b/apps/settings/urls/api_urls.py @@ -10,7 +10,8 @@ urlpatterns = [ path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'), path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'), path('ldap/users/', api.LDAPUserListApi.as_view(), name='ldap-user-list'), - path('ldap/users/sync/', api.LDAPUserSyncAPI.as_view(), name='ldap-user-sync'), + path('ldap/users/import/', api.LDAPUserImportAPI.as_view(), name='ldap-user-import'), + path('ldap/cache/refresh/', api.LDAPCacheRefreshAPI.as_view(), name='ldap-cache-refresh'), path('terminal/replay-storage/create/', api.ReplayStorageCreateAPI.as_view(), name='replay-storage-create'), path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'), path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'), diff --git a/apps/settings/utils.py b/apps/settings/utils.py deleted file mode 100644 index 9ecd5d286..000000000 --- a/apps/settings/utils.py +++ /dev/null @@ -1,219 +0,0 @@ -# -*- coding: utf-8 -*- -# - -from ldap3 import Server, Connection -from django.utils.translation import ugettext_lazy as _ - -from users.models import User -from users.utils import construct_user_email -from common.utils import get_logger -from common.const import LDAP_AD_ACCOUNT_DISABLE - -from .models import settings - - -logger = get_logger(__file__) - - -class LDAPOUGroupException(Exception): - pass - - -class LDAPUtil: - _conn = None - - SEARCH_FIELD_ALL = 'all' - SEARCH_FIELD_USERNAME = 'username' - - def __init__(self, use_settings_config=True, server_uri=None, bind_dn=None, - password=None, use_ssl=None, search_ougroup=None, - search_filter=None, attr_map=None, auth_ldap=None): - # config - self.paged_size = settings.AUTH_LDAP_SEARCH_PAGED_SIZE - - if use_settings_config: - self._load_config_from_settings() - else: - self.server_uri = server_uri - self.bind_dn = bind_dn - self.password = password - self.use_ssl = use_ssl - self.search_ougroup = search_ougroup - self.search_filter = search_filter - self.attr_map = attr_map - self.auth_ldap = auth_ldap - - def _load_config_from_settings(self): - self.server_uri = settings.AUTH_LDAP_SERVER_URI - self.bind_dn = settings.AUTH_LDAP_BIND_DN - self.password = settings.AUTH_LDAP_BIND_PASSWORD - self.use_ssl = settings.AUTH_LDAP_START_TLS - self.search_ougroup = settings.AUTH_LDAP_SEARCH_OU - self.search_filter = settings.AUTH_LDAP_SEARCH_FILTER - self.attr_map = settings.AUTH_LDAP_USER_ATTR_MAP - self.auth_ldap = settings.AUTH_LDAP - - @property - def connection(self): - if self._conn is None: - server = Server(self.server_uri, use_ssl=self.use_ssl) - conn = Connection(server, self.bind_dn, self.password) - conn.bind() - self._conn = conn - return self._conn - - @staticmethod - def get_user_by_username(username): - try: - user = User.objects.get(username=username) - except Exception as e: - return None - else: - return user - - def _ldap_entry_to_user_item(self, entry): - user_item = {} - for attr, mapping in self.attr_map.items(): - if not hasattr(entry, mapping): - continue - value = getattr(entry, mapping).value or '' - if mapping.lower() == 'useraccountcontrol' and attr == 'is_active'\ - and value: - value = int(value) & LDAP_AD_ACCOUNT_DISABLE \ - != LDAP_AD_ACCOUNT_DISABLE - user_item[attr] = value - return user_item - - def _search_user_items_ou(self, search_ou, extra_filter=None, cookie=None): - search_filter = self.search_filter % {"user": "*"} - if extra_filter: - search_filter = '(&{}{})'.format(search_filter, extra_filter) - - ok = self.connection.search( - search_ou, search_filter, - attributes=list(self.attr_map.values()), - paged_size=self.paged_size, paged_cookie=cookie - ) - if not ok: - error = _("Search no entry matched in ou {}".format(search_ou)) - raise LDAPOUGroupException(error) - - user_items = [] - for entry in self.connection.entries: - user_item = self._ldap_entry_to_user_item(entry) - user = self.get_user_by_username(user_item['username']) - user_item['existing'] = bool(user) - if user_item in user_items: - continue - user_items.append(user_item) - return user_items - - def _cookie(self): - if self.paged_size is None: - cookie = None - else: - cookie = self.connection.result['controls']['1.2.840.113556.1.4.319']['value']['cookie'] - return cookie - - def search_user_items(self, extra_filter=None): - user_items = [] - logger.info("Search user items") - - for search_ou in str(self.search_ougroup).split("|"): - logger.info("Search user search ou: {}".format(search_ou)) - _user_items = self._search_user_items_ou(search_ou, extra_filter=extra_filter) - user_items.extend(_user_items) - while self._cookie(): - logger.info("Page Search user search ou: {}".format(search_ou)) - _user_items = self._search_user_items_ou(search_ou, extra_filter, self._cookie()) - user_items.extend(_user_items) - logger.info("Search user items end") - return user_items - - def construct_extra_filter(self, field, q): - if not q: - return None - extra_filter = '' - if field == self.SEARCH_FIELD_ALL: - for attr in self.attr_map.values(): - extra_filter += '({}={})'.format(attr, q) - extra_filter = '(|{})'.format(extra_filter) - return extra_filter - - if field == self.SEARCH_FIELD_USERNAME and isinstance(q, list): - attr = self.attr_map.get('username') - for username in q: - extra_filter += '({}={})'.format(attr, username) - extra_filter = '(|{})'.format(extra_filter) - return extra_filter - - def search_filter_user_items(self, username_list): - extra_filter = self.construct_extra_filter( - self.SEARCH_FIELD_USERNAME, username_list - ) - user_items = self.search_user_items(extra_filter) - return user_items - - @staticmethod - def save_user(user, user_item): - for field, value in user_item.items(): - if not hasattr(user, field): - continue - if isinstance(getattr(user, field), bool): - if isinstance(value, str): - value = value.lower() - value = value in ['true', 1, True] - setattr(user, field, value) - user.save() - - def update_user(self, user_item): - user = self.get_user_by_username(user_item['username']) - if user.source != User.SOURCE_LDAP: - msg = _('The user source is not LDAP') - return False, msg - try: - self.save_user(user, user_item) - except Exception as e: - logger.error(e, exc_info=True) - return False, str(e) - else: - return True, None - - def create_user(self, user_item): - user = User(source=User.SOURCE_LDAP) - try: - self.save_user(user, user_item) - except Exception as e: - logger.error(e, exc_info=True) - return False, str(e) - else: - return True, None - - @staticmethod - def construct_user_email(user_item): - username = user_item['username'] - email = user_item.get('email', '') - email = construct_user_email(username, email) - return email - - def create_or_update_users(self, user_items): - succeed = failed = 0 - for user_item in user_items: - exist = user_item.pop('existing', False) - user_item['email'] = self.construct_user_email(user_item) - if not exist: - ok, error = self.create_user(user_item) - else: - ok, error = self.update_user(user_item) - if not ok: - logger.info("Failed User: {}".format(user_item)) - failed += 1 - else: - succeed += 1 - result = {'total': len(user_items), 'succeed': succeed, 'failed': failed} - return result - - def sync_users(self, username_list=None): - user_items = self.search_filter_user_items(username_list) - result = self.create_or_update_users(user_items) - return result diff --git a/apps/settings/utils/__init__.py b/apps/settings/utils/__init__.py new file mode 100644 index 000000000..87bc6198f --- /dev/null +++ b/apps/settings/utils/__init__.py @@ -0,0 +1,4 @@ +# coding: utf-8 +# + +from .ldap import * diff --git a/apps/settings/utils/ldap.py b/apps/settings/utils/ldap.py new file mode 100644 index 000000000..ba3e5a838 --- /dev/null +++ b/apps/settings/utils/ldap.py @@ -0,0 +1,336 @@ +# coding: utf-8 +# + +from ldap3 import Server, Connection +from django.conf import settings +from django.core.cache import cache +from django.utils.translation import ugettext_lazy as _ + +from common.const import LDAP_AD_ACCOUNT_DISABLE +from common.utils import timeit, get_logger +from users.utils import construct_user_email +from users.models import User + +logger = get_logger(__file__) + +__all__ = [ + 'LDAPConfig', 'LDAPServerUtil', 'LDAPCacheUtil', 'LDAPImportUtil', + 'LDAPSyncUtil', 'LDAP_USE_CACHE_FLAGS' +] + +LDAP_USE_CACHE_FLAGS = [1, '1', 'true', 'True', True] + + +class LDAPOUGroupException(Exception): + pass + + +class LDAPConfig(object): + + def __init__(self, config=None): + self.server_uri = None + self.bind_dn = None + self.password = None + self.use_ssl = None + self.search_ougroup = None + self.search_filter = None + self.attr_map = None + if isinstance(config, dict): + self.load_from_config(config) + else: + self.load_from_settings() + + def load_from_config(self, config): + self.server_uri = config.get('server_uri') + self.bind_dn = config.get('bind_dn') + self.password = config.get('password') + self.use_ssl = config.get('use_ssl') + self.search_ougroup = config.get('search_ougroup') + self.search_filter = config.get('search_filter') + self.attr_map = config.get('attr_map') + + def load_from_settings(self): + self.server_uri = settings.AUTH_LDAP_SERVER_URI + self.bind_dn = settings.AUTH_LDAP_BIND_DN + self.password = settings.AUTH_LDAP_BIND_PASSWORD + self.use_ssl = settings.AUTH_LDAP_START_TLS + self.search_ougroup = settings.AUTH_LDAP_SEARCH_OU + self.search_filter = settings.AUTH_LDAP_SEARCH_FILTER + self.attr_map = settings.AUTH_LDAP_USER_ATTR_MAP + + +class LDAPServerUtil(object): + + def __init__(self, config=None): + if isinstance(config, dict): + self.config = LDAPConfig(config=config) + elif isinstance(config, LDAPConfig): + self.config = config + else: + self.config = LDAPConfig() + self._conn = None + self._paged_size = self.get_paged_size() + self.search_users = None + self.search_value = None + + @property + def connection(self): + if self._conn: + return self._conn + server = Server(self.config.server_uri, use_ssl=self.config.use_ssl) + conn = Connection(server, self.config.bind_dn, self.config.password) + conn.bind() + self._conn = conn + return self._conn + + @staticmethod + def get_paged_size(): + paged_size = settings.AUTH_LDAP_SEARCH_PAGED_SIZE + if isinstance(paged_size, int): + return paged_size + return None + + def paged_cookie(self): + if self._paged_size is None: + return None + cookie = self.connection.result['controls']['1.2.840.113556.1.4.319']['value']['cookie'] + return cookie + + def get_search_filter_extra(self): + extra = '' + if self.search_users: + mapping_username = self.config.attr_map.get('username') + for user in self.search_users: + extra += '({}={})'.format(mapping_username, user) + return '(|{})'.format(extra) + if self.search_value: + for attr in self.config.attr_map.values(): + extra += '({}={})'.format(attr, self.search_value) + return '(|{})'.format(extra) + return extra + + def get_search_filter(self): + search_filter = self.config.search_filter % {'user': '*'} + search_filter_extra = self.get_search_filter_extra() + if search_filter_extra: + search_filter = '(&{}{})'.format(search_filter, search_filter_extra) + return search_filter + + def search_user_entries_ou(self, search_ou, paged_cookie=None): + logger.info("Search user entries ou: {}, paged_cookie: {}". + format(search_ou, paged_cookie)) + search_filter = self.get_search_filter() + attributes = list(self.config.attr_map.values()) + ok = self.connection.search( + search_base=search_ou, search_filter=search_filter, + attributes=attributes, paged_size=self._paged_size, + paged_cookie=paged_cookie + ) + if not ok: + error = _("Search no entry matched in ou {}".format(search_ou)) + raise LDAPOUGroupException(error) + + @timeit + def search_user_entries(self): + logger.info("Search user entries") + user_entries = list() + search_ous = str(self.config.search_ougroup).split('|') + for search_ou in search_ous: + self.search_user_entries_ou(search_ou) + user_entries.extend(self.connection.entries) + while self.paged_cookie(): + self.search_user_entries_ou(search_ou, self.paged_cookie()) + user_entries.extend(self.connection.entries) + return user_entries + + def user_entry_to_dict(self, entry): + user = {} + attr_map = self.config.attr_map.items() + for attr, mapping in attr_map: + if not hasattr(entry, mapping): + continue + value = getattr(entry, mapping).value or '' + if attr == 'is_active' and mapping.lower() == 'useraccountcontrol' \ + and value: + value = int(value) & LDAP_AD_ACCOUNT_DISABLE != LDAP_AD_ACCOUNT_DISABLE + user[attr] = value + return user + + @timeit + def user_entries_to_dict(self, user_entries): + users = [] + for user_entry in user_entries: + user = self.user_entry_to_dict(user_entry) + users.append(user) + return users + + @timeit + def search(self, search_users=None, search_value=None): + logger.info("Search ldap users") + self.search_users = search_users + self.search_value = search_value + user_entries = self.search_user_entries() + users = self.user_entries_to_dict(user_entries) + return users + + +class LDAPCacheUtil(object): + CACHE_KEY_USERS = 'CACHE_KEY_LDAP_USERS' + + def __init__(self): + self.search_users = None + self.search_value = None + + def set_users(self, users): + logger.info('Set ldap users to cache, count: {}'.format(len(users))) + cache.set(self.CACHE_KEY_USERS, users, None) + + def get_users(self): + users = cache.get(self.CACHE_KEY_USERS) + logger.info('Get ldap users from cache, count: {}'.format(len(users))) + return users + + def delete_users(self): + logger.info('Delete ldap users from cache') + cache.delete(self.CACHE_KEY_USERS) + + def filter_users(self, users): + if self.search_users: + filter_users = [ + user for user in users + if user['username'] in self.search_users + ] + elif self.search_value: + filter_users = [ + user for user in users + if self.search_value in ','.join(user.values()) + ] + else: + filter_users = users + return filter_users + + def search(self, search_users=None, search_value=None): + self.search_users = search_users + self.search_value = search_value + users = self.get_users() + users = self.filter_users(users) + return users + + +class LDAPSyncUtil(object): + CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG = 'CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG' + + CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS = 'CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS' + TASK_STATUS_IS_RUNNING = 'RUNNING' + TASK_STATUS_IS_OVER = 'OVER' + + def __init__(self): + self.server_util = LDAPServerUtil() + self.cache_util = LDAPCacheUtil() + self.task_error_msg = None + + def clear_cache(self): + logger.info('Clear ldap sync cache') + self.delete_task_status() + self.delete_task_error_msg() + self.cache_util.delete_users() + + @property + def task_no_start(self): + status = self.get_task_status() + return status is None + + @property + def task_is_running(self): + status = self.get_task_status() + return status == self.TASK_STATUS_IS_RUNNING + + @property + def task_is_over(self): + status = self.get_task_status() + return status == self.TASK_STATUS_IS_OVER + + def set_task_status(self, status): + logger.info('Set task status: {}'.format(status)) + cache.set(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS, status, None) + + def get_task_status(self): + status = cache.get(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS) + logger.info('Get task status: {}'.format(status)) + return status + + def delete_task_status(self): + logger.info('Delete task status') + cache.delete(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS) + + def set_task_error_msg(self, error_msg): + logger.info('Set task error msg') + cache.set(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG, error_msg, None) + + def get_task_error_msg(self): + logger.info('Get task error msg') + error_msg = cache.get(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG) + return error_msg + + def delete_task_error_msg(self): + logger.info('Delete task error msg') + cache.delete(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG) + + def pre_sync(self): + self.set_task_status(self.TASK_STATUS_IS_RUNNING) + + def sync(self): + users = self.server_util.search() + self.cache_util.set_users(users) + + def post_sync(self): + self.set_task_status(self.TASK_STATUS_IS_OVER) + + def perform_sync(self): + logger.info('Start perform sync ldap users from server to cache') + self.pre_sync() + try: + self.sync() + except Exception as e: + error_msg = str(e) + logger.error(error_msg) + self.set_task_error_msg(error_msg) + self.post_sync() + logger.info('End perform sync ldap users from server to cache') + + +class LDAPImportUtil(object): + + def __init__(self): + pass + + @staticmethod + def get_user_email(user): + username = user['username'] + email = user['email'] + email = construct_user_email(username, email) + return email + + def update_or_create(self, user): + user['email'] = self.get_user_email(user) + if user['username'] not in ['admin']: + user['source'] = User.SOURCE_LDAP + obj, created = User.objects.update_or_create( + username=user['username'], defaults=user + ) + return obj, created + + def perform_import(self, users): + logger.info('Start perform import ldap users, count: {}'.format(len(users))) + errors = [] + for user in users: + try: + self.update_or_create(user) + except Exception as e: + errors.append({user['username']: str(e)}) + logger.error(e) + logger.info('End perform import ldap users') + return errors + + + diff --git a/apps/settings/views.py b/apps/settings/views.py index a9df717d7..2442f074e 100644 --- a/apps/settings/views.py +++ b/apps/settings/views.py @@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _ from common.permissions import PermissionsMixin, IsSuperUser from common import utils +from .utils import LDAPSyncUtil from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \ TerminalSettingForm, SecuritySettingForm, EmailContentSettingForm @@ -83,6 +84,7 @@ class LDAPSettingView(PermissionsMixin, TemplateView): form.save() msg = _("Update setting successfully") messages.success(request, msg) + LDAPSyncUtil().clear_cache() return redirect('settings:ldap-setting') else: context = self.get_context_data() diff --git a/apps/users/tasks.py b/apps/users/tasks.py index e0051e939..29355514d 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -11,7 +11,7 @@ from .models import User from .utils import ( send_password_expiration_reminder_mail, send_user_expiration_reminder_mail ) -from settings.utils import LDAPUtil +from settings.utils import LDAPServerUtil, LDAPImportUtil logger = get_logger(__file__) @@ -70,16 +70,21 @@ def check_user_expired_periodic(): @shared_task -def sync_ldap_user(): - logger.info("Start sync ldap user periodic task") - util = LDAPUtil() - result = util.sync_users() - logger.info("Result: {}".format(result)) +def import_ldap_user(): + logger.info("Start import ldap user task") + util_server = LDAPServerUtil() + util_import = LDAPImportUtil() + users = util_server.search() + errors = util_import.perform_import(users) + if errors: + logger.error("Imported LDAP users errors: {}".format(errors)) + else: + logger.info('Imported {} users successfully'.format(len(users))) @shared_task @after_app_ready_start -def sync_ldap_user_periodic(): +def import_ldap_user_periodic(): if not settings.AUTH_LDAP: return if not settings.AUTH_LDAP_SYNC_IS_PERIODIC: @@ -91,10 +96,9 @@ def sync_ldap_user_periodic(): else: interval = None crontab = settings.AUTH_LDAP_SYNC_CRONTAB - tasks = { - 'sync_ldap_user_periodic': { - 'task': sync_ldap_user.name, + 'import_ldap_user_periodic': { + 'task': import_ldap_user.name, 'interval': interval, 'crontab': crontab, 'enabled': True, From 2ea3ad4ca5d1b4d55d0a7cc1a276d60057980113 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Nov 2019 17:45:39 +0800 Subject: [PATCH 07/17] =?UTF-8?q?[Update]=20=E9=87=8D=E6=9E=84=20LDAP/AD?= =?UTF-8?q?=20=E5=90=8C=E6=AD=A5=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=BC=93=E5=AD=98=E6=9C=BA=E5=88=B6=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/api.py | 80 +++++++++++-------- .../settings/_ldap_list_users_modal.html | 8 +- apps/settings/utils/ldap.py | 5 +- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/apps/settings/api.py b/apps/settings/api.py index c226c9205..f5f6ddfb2 100644 --- a/apps/settings/api.py +++ b/apps/settings/api.py @@ -137,35 +137,6 @@ class LDAPUserListApi(generics.ListAPIView): users = self.get_queryset_from_server() return users - def list(self, request, *args, **kwargs): - cache_police = self.request.query_params.get('cache_police', True) - # 不是用缓存 - if cache_police not in LDAP_USE_CACHE_FLAGS: - return super().list(request, *args, **kwargs) - - queryset = self.get_queryset() - # 缓存有数据 - if queryset is not None: - return super().list(request, *args, **kwargs) - - sync_util = LDAPSyncUtil() - # 还没有同步任务 - if sync_util.task_no_start: - task = sync_ldap_user_task.delay() - data = {'msg': 'Cache no data, sync task {} started.'.format(task.id)} - return Response(data=data, status=409) - # 同步任务正在执行 - if sync_util.task_is_running: - data = {'msg': 'synchronization is running.'} - return Response(data=data, status=409) - # 同步任务执行结束 - if sync_util.task_is_over: - msg = sync_util.get_task_error_msg() - data = {'msg': 'Synchronization task report error: {}'.format(msg)} - return Response(data=data, status=400) - - return super().list(request, *args, **kwargs) - @staticmethod def processing_queryset(queryset): db_username_list = User.objects.all().values_list('username', flat=True) @@ -187,10 +158,46 @@ class LDAPUserListApi(generics.ListAPIView): return queryset def filter_queryset(self, queryset): + if queryset is None: + return queryset queryset = self.processing_queryset(queryset) queryset = self.sort_queryset(queryset) return queryset + def list(self, request, *args, **kwargs): + cache_police = self.request.query_params.get('cache_police', True) + # 不是用缓存 + if cache_police not in LDAP_USE_CACHE_FLAGS: + return super().list(request, *args, **kwargs) + + try: + queryset = self.get_queryset() + except Exception as e: + data = {'error': str(e)} + return Response(data=data, status=400) + + # 缓存有数据 + if queryset is not None: + return super().list(request, *args, **kwargs) + + sync_util = LDAPSyncUtil() + # 还没有同步任务 + if sync_util.task_no_start: + task = sync_ldap_user_task.delay() + data = {'msg': 'Cache no data, sync task {} started.'.format(task.id)} + return Response(data=data, status=409) + # 同步任务正在执行 + if sync_util.task_is_running: + data = {'msg': 'synchronization is running.'} + return Response(data=data, status=409) + # 同步任务执行结束 + if sync_util.task_is_over: + msg = sync_util.get_task_error_msg() + data = {'error': 'Synchronization task report error: {}'.format(msg)} + return Response(data=data, status=400) + + return super().list(request, *args, **kwargs) + class LDAPUserImportAPI(APIView): permission_classes = (IsOrgAdmin,) @@ -205,11 +212,20 @@ class LDAPUserImportAPI(APIView): return users def post(self, request): - users = self.get_ldap_users() + try: + users = self.get_ldap_users() + except Exception as e: + return Response({'error': str(e)}, status=401) + + if users is None: + return Response({'msg': 'Get ldap users is None'}, status=401) + errors = LDAPImportUtil().perform_import(users) if errors: - return Response({'Error': errors}, status=401) - return Response({'msg': 'Imported {} users successfully'.format(len(users))}) + return Response({'errors': errors}, status=401) + + count = users if users is None else len(users) + return Response({'msg': 'Imported {} users successfully'.format(count)}) class LDAPCacheRefreshAPI(generics.RetrieveAPIView): diff --git a/apps/settings/templates/settings/_ldap_list_users_modal.html b/apps/settings/templates/settings/_ldap_list_users_modal.html index cc63f72c5..1b2597924 100644 --- a/apps/settings/templates/settings/_ldap_list_users_modal.html +++ b/apps/settings/templates/settings/_ldap_list_users_modal.html @@ -37,6 +37,9 @@
+
+
{% trans 'Loading' %}...
+
@@ -48,7 +51,7 @@ var interval; function initLdapUsersTable() { if(ldap_users_table){ - ldap_users_table.ajax.reload(); + ldap_users_table.ajax.reload(null, false); return ldap_users_table } var options = { @@ -77,6 +80,7 @@ function initLdapUsersTable() { } function testRequestLdapUser(){ + $("#fake_datatable_wrapper_loading").css('display', 'block'); var the_url = "{% url 'api-settings:ldap-user-list' %}"; var error = function (data, status) { if (status === 409){ @@ -92,6 +96,7 @@ function testRequestLdapUser(){ console.log(data, status) }; var success = function() { + $("#fake_datatable_wrapper_loading").css('display', 'none'); initLdapUsersTable(); clearInterval(interval); interval = undefined @@ -145,6 +150,7 @@ $(document).ready(function(){ } function success(message) { toastr.success(message.msg); + ldap_users_table.selected = []; timingTestRequestLdapUser(); } requestApi({ diff --git a/apps/settings/utils/ldap.py b/apps/settings/utils/ldap.py index ba3e5a838..3df45bfae 100644 --- a/apps/settings/utils/ldap.py +++ b/apps/settings/utils/ldap.py @@ -187,7 +187,8 @@ class LDAPCacheUtil(object): def get_users(self): users = cache.get(self.CACHE_KEY_USERS) - logger.info('Get ldap users from cache, count: {}'.format(len(users))) + count = users if users is None else len(users) + logger.info('Get ldap users from cache, count: {}'.format(count)) return users def delete_users(self): @@ -195,6 +196,8 @@ class LDAPCacheUtil(object): cache.delete(self.CACHE_KEY_USERS) def filter_users(self, users): + if users is None: + return users if self.search_users: filter_users = [ user for user in users From 0a08ba3b9c4287311787075f8f332d4ce442121c Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Nov 2019 17:50:46 +0800 Subject: [PATCH 08/17] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | Bin 80725 -> 80616 bytes apps/locale/zh/LC_MESSAGES/django.po | 249 ++++++++++++++------------- 2 files changed, 126 insertions(+), 123 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index a6510ca5a41597f874d262fab0c92cdb24ef153b..ae4f19da812c095df7e12e22afc6c7383f957604 100644 GIT binary patch delta 20053 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 delta 20145 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 RpI(~(K`6a&`D1|={|}#}c4`0s diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index fdc37654f..3ad7782f2 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Jumpserver 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-10-25 10:52+0800\n" +"POT-Creation-Date: 2019-11-11 17:46+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -83,7 +83,7 @@ msgstr "运行参数" #: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_list.html:26 #: assets/templates/assets/label_list.html:16 -#: assets/templates/assets/system_user_list.html:51 audits/models.py:19 +#: assets/templates/assets/system_user_list.html:51 audits/models.py:20 #: audits/templates/audits/ftp_log_list.html:44 #: audits/templates/audits/ftp_log_list.html:74 #: perms/forms/asset_permission.py:84 perms/models/asset_permission.py:80 @@ -96,7 +96,7 @@ msgstr "运行参数" #: terminal/templates/terminal/session_list.html:28 #: terminal/templates/terminal/session_list.html:72 #: xpack/plugins/change_auth_plan/forms.py:73 -#: xpack/plugins/change_auth_plan/models.py:412 +#: xpack/plugins/change_auth_plan/models.py:419 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13 @@ -137,7 +137,7 @@ msgstr "资产" #: perms/templates/perms/remote_app_permission_remote_app.html:53 #: perms/templates/perms/remote_app_permission_user.html:53 #: settings/models.py:29 -#: settings/templates/settings/_ldap_list_users_modal.html:31 +#: settings/templates/settings/_ldap_list_users_modal.html:32 #: settings/templates/settings/command_storage_create.html:41 #: settings/templates/settings/replay_storage_create.html:44 #: settings/templates/settings/terminal_setting.html:83 @@ -152,7 +152,7 @@ msgstr "资产" #: users/templates/users/user_profile.html:51 #: users/templates/users/user_pubkey_update.html:57 #: xpack/plugins/change_auth_plan/forms.py:56 -#: xpack/plugins/change_auth_plan/models.py:63 +#: xpack/plugins/change_auth_plan/models.py:64 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12 #: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144 @@ -199,7 +199,7 @@ msgstr "参数" #: perms/templates/perms/remote_app_permission_detail.html:90 #: users/models/user.py:414 users/serializers/v1.py:143 #: users/templates/users/user_detail.html:111 -#: xpack/plugins/change_auth_plan/models.py:108 +#: xpack/plugins/change_auth_plan/models.py:109 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113 #: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179 #: xpack/plugins/gathered_user/models.py:46 @@ -262,7 +262,7 @@ msgstr "创建日期" #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:37 #: users/templates/users/user_profile.html:138 -#: xpack/plugins/change_auth_plan/models.py:104 +#: xpack/plugins/change_auth_plan/models.py:105 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19 #: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173 @@ -410,7 +410,7 @@ msgstr "详情" #: assets/templates/assets/label_list.html:39 #: assets/templates/assets/system_user_detail.html:26 #: assets/templates/assets/system_user_list.html:29 -#: assets/templates/assets/system_user_list.html:81 audits/models.py:33 +#: assets/templates/assets/system_user_list.html:81 audits/models.py:34 #: perms/templates/perms/asset_permission_detail.html:30 #: perms/templates/perms/asset_permission_list.html:178 #: perms/templates/perms/remote_app_permission_detail.html:30 @@ -454,7 +454,7 @@ msgstr "更新" #: assets/templates/assets/domain_list.html:55 #: assets/templates/assets/label_list.html:40 #: assets/templates/assets/system_user_detail.html:30 -#: assets/templates/assets/system_user_list.html:82 audits/models.py:34 +#: assets/templates/assets/system_user_list.html:82 audits/models.py:35 #: authentication/templates/authentication/_access_key_modal.html:65 #: ops/templates/ops/task_list.html:69 #: perms/templates/perms/asset_permission_detail.html:34 @@ -510,7 +510,7 @@ msgstr "创建远程应用" #: assets/templates/assets/domain_gateway_list.html:73 #: assets/templates/assets/domain_list.html:29 #: assets/templates/assets/label_list.html:17 -#: assets/templates/assets/system_user_list.html:56 audits/models.py:38 +#: assets/templates/assets/system_user_list.html:56 audits/models.py:39 #: audits/templates/audits/operate_log_list.html:47 #: audits/templates/audits/operate_log_list.html:73 #: authentication/templates/authentication/_access_key_modal.html:34 @@ -602,7 +602,7 @@ msgstr "端口" #: assets/templates/assets/asset_detail.html:196 #: assets/templates/assets/system_user_assets.html:83 #: perms/models/asset_permission.py:81 -#: xpack/plugins/change_auth_plan/models.py:74 +#: xpack/plugins/change_auth_plan/models.py:75 #: xpack/plugins/gathered_user/models.py:31 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:17 msgid "Nodes" @@ -634,7 +634,7 @@ msgid "Domain" msgstr "网域" #: assets/forms/asset.py:69 assets/forms/asset.py:103 assets/forms/asset.py:116 -#: assets/forms/asset.py:152 assets/models/node.py:421 +#: assets/forms/asset.py:152 assets/models/node.py:462 #: assets/serializers/system_user.py:36 #: assets/templates/assets/asset_create.html:42 #: perms/forms/asset_permission.py:87 perms/forms/asset_permission.py:94 @@ -696,21 +696,21 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" #: assets/templates/assets/admin_user_list.html:45 #: assets/templates/assets/domain_gateway_list.html:71 #: assets/templates/assets/system_user_detail.html:62 -#: assets/templates/assets/system_user_list.html:48 audits/models.py:80 +#: assets/templates/assets/system_user_list.html:48 audits/models.py:81 #: audits/templates/audits/login_log_list.html:57 authentication/forms.py:13 #: authentication/templates/authentication/login.html:65 #: authentication/templates/authentication/new_login.html:92 #: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:70 #: perms/templates/perms/asset_permission_user.html:55 #: perms/templates/perms/remote_app_permission_user.html:54 -#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:13 +#: settings/templates/settings/_ldap_list_users_modal.html:31 users/forms.py:13 #: users/models/user.py:371 users/templates/users/_select_user_modal.html:14 #: users/templates/users/user_detail.html:67 #: users/templates/users/user_list.html:36 #: users/templates/users/user_profile.html:47 #: xpack/plugins/change_auth_plan/forms.py:58 -#: xpack/plugins/change_auth_plan/models.py:65 -#: xpack/plugins/change_auth_plan/models.py:408 +#: xpack/plugins/change_auth_plan/models.py:66 +#: xpack/plugins/change_auth_plan/models.py:415 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12 @@ -737,8 +737,8 @@ msgstr "密码或密钥密码" #: users/templates/users/user_profile_update.html:41 #: users/templates/users/user_pubkey_update.html:41 #: users/templates/users/user_update.html:20 -#: xpack/plugins/change_auth_plan/models.py:95 -#: xpack/plugins/change_auth_plan/models.py:263 +#: xpack/plugins/change_auth_plan/models.py:96 +#: xpack/plugins/change_auth_plan/models.py:264 msgid "Password" msgstr "密码" @@ -931,13 +931,13 @@ msgstr "版本" msgid "AuthBook" msgstr "" -#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:99 -#: xpack/plugins/change_auth_plan/models.py:270 +#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:100 +#: xpack/plugins/change_auth_plan/models.py:271 msgid "SSH private key" msgstr "ssh密钥" -#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:102 -#: xpack/plugins/change_auth_plan/models.py:266 +#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:103 +#: xpack/plugins/change_auth_plan/models.py:267 msgid "SSH public key" msgstr "ssh公钥" @@ -1090,8 +1090,8 @@ msgstr "资产组" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 audits/models.py:17 audits/models.py:37 -#: audits/models.py:50 audits/templates/audits/ftp_log_list.html:36 +#: assets/models/label.py:15 audits/models.py:18 audits/models.py:38 +#: audits/models.py:51 audits/templates/audits/ftp_log_list.html:36 #: audits/templates/audits/ftp_log_list.html:73 #: audits/templates/audits/operate_log_list.html:39 #: audits/templates/audits/operate_log_list.html:72 @@ -1120,7 +1120,7 @@ msgstr "默认资产组" msgid "User" msgstr "用户" -#: assets/models/label.py:19 assets/models/node.py:412 +#: assets/models/label.py:19 assets/models/node.py:453 #: assets/templates/assets/label_list.html:15 settings/models.py:30 msgid "Value" msgstr "值" @@ -1129,23 +1129,23 @@ msgstr "值" msgid "Category" msgstr "分类" -#: assets/models/node.py:163 +#: assets/models/node.py:164 msgid "New node" msgstr "新节点" -#: assets/models/node.py:324 +#: assets/models/node.py:325 msgid "ungrouped" msgstr "未分组" -#: assets/models/node.py:326 +#: assets/models/node.py:327 msgid "empty" msgstr "空" -#: assets/models/node.py:328 +#: assets/models/node.py:329 msgid "favorite" msgstr "收藏夹" -#: assets/models/node.py:411 +#: assets/models/node.py:452 msgid "Key" msgstr "键" @@ -1176,7 +1176,7 @@ msgstr "手动登录" #: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73 #: assets/views/system_user.py:29 assets/views/system_user.py:46 #: assets/views/system_user.py:63 assets/views/system_user.py:79 -#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:70 +#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:71 msgid "Assets" msgstr "资产管理" @@ -1200,7 +1200,7 @@ msgid "Login mode" msgstr "登录模式" #: assets/models/user.py:166 assets/templates/assets/user_asset_list.html:79 -#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:52 +#: audits/models.py:21 audits/templates/audits/ftp_log_list.html:52 #: audits/templates/audits/ftp_log_list.html:75 #: perms/forms/asset_permission.py:90 perms/forms/remote_app_permission.py:43 #: perms/models/asset_permission.py:82 perms/models/remote_app_permission.py:16 @@ -1321,7 +1321,7 @@ msgstr "测试资产可连接性: {}" #: assets/tasks/asset_user_connectivity.py:27 #: assets/tasks/push_system_user.py:130 -#: xpack/plugins/change_auth_plan/models.py:521 +#: xpack/plugins/change_auth_plan/models.py:528 msgid "The asset {} system platform {} does not support run Ansible tasks" msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务" @@ -1439,6 +1439,7 @@ msgstr "资产列表" #: assets/templates/assets/_node_tree.html:40 #: ops/templates/ops/command_execution_create.html:70 #: ops/templates/ops/command_execution_create.html:127 +#: settings/templates/settings/_ldap_list_users_modal.html:41 #: users/templates/users/_granted_assets.html:7 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:66 msgid "Loading" @@ -1481,7 +1482,7 @@ msgstr "获取认证信息错误" #: assets/templates/assets/_user_asset_detail_modal.html:23 #: authentication/templates/authentication/_access_key_modal.html:142 #: authentication/templates/authentication/_mfa_confirm_modal.html:53 -#: settings/templates/settings/_ldap_list_users_modal.html:92 +#: settings/templates/settings/_ldap_list_users_modal.html:170 #: templates/_modal.html:22 msgid "Close" msgstr "关闭" @@ -1697,7 +1698,7 @@ msgstr "导出" #: assets/templates/assets/admin_user_list.html:21 #: assets/templates/assets/asset_list.html:73 #: assets/templates/assets/system_user_list.html:24 -#: settings/templates/settings/_ldap_list_users_modal.html:93 +#: settings/templates/settings/_ldap_list_users_modal.html:171 #: users/templates/users/user_group_list.html:15 #: users/templates/users/user_list.html:15 #: xpack/plugins/license/templates/license/license_detail.html:110 @@ -2180,7 +2181,7 @@ msgstr "资产管理" msgid "System user asset" msgstr "系统用户资产" -#: audits/models.py:18 audits/models.py:41 audits/models.py:52 +#: audits/models.py:19 audits/models.py:42 audits/models.py:53 #: audits/templates/audits/ftp_log_list.html:76 #: audits/templates/audits/operate_log_list.html:76 #: audits/templates/audits/password_change_log_list.html:58 @@ -2190,16 +2191,16 @@ msgstr "系统用户资产" msgid "Remote addr" msgstr "远端地址" -#: audits/models.py:21 audits/templates/audits/ftp_log_list.html:77 +#: audits/models.py:22 audits/templates/audits/ftp_log_list.html:77 msgid "Operate" msgstr "操作" -#: audits/models.py:22 audits/templates/audits/ftp_log_list.html:59 +#: audits/models.py:23 audits/templates/audits/ftp_log_list.html:59 #: audits/templates/audits/ftp_log_list.html:78 msgid "Filename" msgstr "文件名" -#: audits/models.py:23 audits/models.py:76 +#: audits/models.py:24 audits/models.py:77 #: audits/templates/audits/ftp_log_list.html:79 #: ops/templates/ops/command_execution_list.html:68 #: ops/templates/ops/task_list.html:15 @@ -2209,82 +2210,82 @@ msgstr "文件名" msgid "Success" msgstr "成功" -#: audits/models.py:32 +#: audits/models.py:33 #: authentication/templates/authentication/_access_key_modal.html:22 #: xpack/plugins/vault/templates/vault/vault.html:46 msgid "Create" msgstr "创建" -#: audits/models.py:39 audits/templates/audits/operate_log_list.html:55 +#: audits/models.py:40 audits/templates/audits/operate_log_list.html:55 #: audits/templates/audits/operate_log_list.html:74 msgid "Resource Type" msgstr "资源类型" -#: audits/models.py:40 audits/templates/audits/operate_log_list.html:75 +#: audits/models.py:41 audits/templates/audits/operate_log_list.html:75 msgid "Resource" msgstr "资源" -#: audits/models.py:51 audits/templates/audits/password_change_log_list.html:57 +#: audits/models.py:52 audits/templates/audits/password_change_log_list.html:57 msgid "Change by" msgstr "修改者" -#: audits/models.py:70 users/templates/users/user_detail.html:98 +#: audits/models.py:71 users/templates/users/user_detail.html:98 msgid "Disabled" msgstr "禁用" -#: audits/models.py:71 settings/models.py:33 +#: audits/models.py:72 settings/models.py:33 #: users/templates/users/user_detail.html:96 msgid "Enabled" msgstr "启用" -#: audits/models.py:72 +#: audits/models.py:73 msgid "-" msgstr "" -#: audits/models.py:77 xpack/plugins/cloud/models.py:264 +#: audits/models.py:78 xpack/plugins/cloud/models.py:264 #: xpack/plugins/cloud/models.py:287 msgid "Failed" msgstr "失败" -#: audits/models.py:81 +#: audits/models.py:82 msgid "Login type" msgstr "登录方式" -#: audits/models.py:82 +#: audits/models.py:83 msgid "Login ip" msgstr "登录IP" -#: audits/models.py:83 +#: audits/models.py:84 msgid "Login city" msgstr "登录城市" -#: audits/models.py:84 +#: audits/models.py:85 msgid "User agent" msgstr "Agent" -#: audits/models.py:85 audits/templates/audits/login_log_list.html:62 +#: audits/models.py:86 audits/templates/audits/login_log_list.html:62 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: users/forms.py:174 users/models/user.py:395 #: users/templates/users/first_login.html:45 msgid "MFA" msgstr "MFA" -#: audits/models.py:86 audits/templates/audits/login_log_list.html:63 -#: xpack/plugins/change_auth_plan/models.py:416 +#: audits/models.py:87 audits/templates/audits/login_log_list.html:63 +#: xpack/plugins/change_auth_plan/models.py:423 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15 #: xpack/plugins/cloud/models.py:278 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69 msgid "Reason" msgstr "原因" -#: audits/models.py:87 audits/templates/audits/login_log_list.html:64 +#: audits/models.py:88 audits/templates/audits/login_log_list.html:64 #: xpack/plugins/cloud/models.py:275 xpack/plugins/cloud/models.py:310 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65 msgid "Status" msgstr "状态" -#: audits/models.py:88 +#: audits/models.py:89 msgid "Date login" msgstr "登录日期" @@ -2296,8 +2297,8 @@ msgstr "登录日期" #: perms/templates/perms/asset_permission_detail.html:86 #: perms/templates/perms/remote_app_permission_detail.html:78 #: terminal/models.py:167 terminal/templates/terminal/session_list.html:34 -#: xpack/plugins/change_auth_plan/models.py:249 -#: xpack/plugins/change_auth_plan/models.py:419 +#: xpack/plugins/change_auth_plan/models.py:250 +#: xpack/plugins/change_auth_plan/models.py:426 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17 #: xpack/plugins/gathered_user/models.py:143 @@ -2826,49 +2827,49 @@ msgstr "Become" msgid "Create by" msgstr "创建者" -#: ops/models/adhoc.py:251 +#: ops/models/adhoc.py:252 msgid "{} Start task: {}" msgstr "{} 任务开始: {}" -#: ops/models/adhoc.py:263 +#: ops/models/adhoc.py:264 msgid "{} Task finish" msgstr "{} 任务结束" -#: ops/models/adhoc.py:355 +#: ops/models/adhoc.py:356 msgid "Start time" msgstr "开始时间" -#: ops/models/adhoc.py:356 +#: ops/models/adhoc.py:357 msgid "End time" msgstr "完成时间" -#: ops/models/adhoc.py:357 ops/templates/ops/adhoc_history.html:57 +#: ops/models/adhoc.py:358 ops/templates/ops/adhoc_history.html:57 #: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:17 -#: xpack/plugins/change_auth_plan/models.py:252 -#: xpack/plugins/change_auth_plan/models.py:422 +#: xpack/plugins/change_auth_plan/models.py:253 +#: xpack/plugins/change_auth_plan/models.py:429 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16 #: xpack/plugins/gathered_user/models.py:146 msgid "Time" msgstr "时间" -#: ops/models/adhoc.py:358 ops/templates/ops/adhoc_detail.html:106 +#: ops/models/adhoc.py:359 ops/templates/ops/adhoc_detail.html:106 #: ops/templates/ops/adhoc_history.html:55 #: ops/templates/ops/adhoc_history_detail.html:69 #: ops/templates/ops/task_detail.html:84 ops/templates/ops/task_history.html:61 msgid "Is finished" msgstr "是否完成" -#: ops/models/adhoc.py:359 ops/templates/ops/adhoc_history.html:56 +#: ops/models/adhoc.py:360 ops/templates/ops/adhoc_history.html:56 #: ops/templates/ops/task_history.html:62 msgid "Is success" msgstr "是否成功" -#: ops/models/adhoc.py:360 +#: ops/models/adhoc.py:361 msgid "Adhoc raw result" msgstr "结果" -#: ops/models/adhoc.py:361 +#: ops/models/adhoc.py:362 msgid "Adhoc result summary" msgstr "汇总" @@ -3395,33 +3396,29 @@ msgstr "远程应用授权用户列表" msgid "RemoteApp permission RemoteApp list" msgstr "远程应用授权远程应用列表" -#: settings/api.py:28 +#: settings/api.py:34 msgid "Test mail sent to {}, please check" msgstr "邮件已经发送{}, 请检查" -#: settings/api.py:67 +#: settings/api.py:73 msgid "Test ldap success" msgstr "连接LDAP成功" -#: settings/api.py:104 +#: settings/api.py:113 msgid "Match {} s users" msgstr "匹配 {} 个用户" -#: settings/api.py:163 -msgid "succeed: {} failed: {} total: {}" -msgstr "成功:{} 失败:{} 总数:{}" - -#: settings/api.py:185 settings/api.py:221 +#: settings/api.py:258 settings/api.py:294 msgid "" "Error: Account invalid (Please make sure the information such as Access key " "or Secret key is correct)" msgstr "错误:账户无效 (请确保 Access key 或 Secret key 等信息正确)" -#: settings/api.py:191 settings/api.py:227 +#: settings/api.py:264 settings/api.py:300 msgid "Create succeed" msgstr "创建成功" -#: settings/api.py:209 settings/api.py:247 +#: settings/api.py:282 settings/api.py:320 #: settings/templates/settings/terminal_setting.html:154 msgid "Delete succeed" msgstr "删除成功" @@ -3741,23 +3738,32 @@ msgstr "LDAP 用户列表" msgid "Please submit the LDAP configuration before import" msgstr "请先提交LDAP配置再进行导入" -#: settings/templates/settings/_ldap_list_users_modal.html:32 +#: settings/templates/settings/_ldap_list_users_modal.html:26 +msgid "Refresh cache" +msgstr "刷新缓存" + +#: settings/templates/settings/_ldap_list_users_modal.html:33 #: users/models/user.py:375 users/templates/users/user_detail.html:71 #: users/templates/users/user_profile.html:59 msgid "Email" msgstr "邮件" -#: settings/templates/settings/_ldap_list_users_modal.html:33 +#: settings/templates/settings/_ldap_list_users_modal.html:34 msgid "Existing" msgstr "已存在" +#: settings/templates/settings/_ldap_list_users_modal.html:143 +msgid "" +"User is not currently selected, please check the user you want to import" +msgstr "当前无勾选用户,请勾选你想要导入的用户" + #: settings/templates/settings/basic_setting.html:15 #: settings/templates/settings/email_content_setting.html:15 #: settings/templates/settings/email_setting.html:15 #: settings/templates/settings/ldap_setting.html:15 #: settings/templates/settings/security_setting.html:15 #: settings/templates/settings/terminal_setting.html:16 -#: settings/templates/settings/terminal_setting.html:49 settings/views.py:20 +#: settings/templates/settings/terminal_setting.html:49 settings/views.py:21 msgid "Basic setting" msgstr "基本设置" @@ -3766,7 +3772,7 @@ msgstr "基本设置" #: settings/templates/settings/email_setting.html:18 #: settings/templates/settings/ldap_setting.html:18 #: settings/templates/settings/security_setting.html:18 -#: settings/templates/settings/terminal_setting.html:20 settings/views.py:47 +#: settings/templates/settings/terminal_setting.html:20 settings/views.py:48 msgid "Email setting" msgstr "邮件设置" @@ -3775,7 +3781,7 @@ msgstr "邮件设置" #: settings/templates/settings/email_setting.html:21 #: settings/templates/settings/ldap_setting.html:21 #: settings/templates/settings/security_setting.html:21 -#: settings/templates/settings/terminal_setting.html:23 settings/views.py:186 +#: settings/templates/settings/terminal_setting.html:23 settings/views.py:188 msgid "Email content setting" msgstr "邮件内容设置" @@ -3784,7 +3790,7 @@ msgstr "邮件内容设置" #: settings/templates/settings/email_setting.html:24 #: settings/templates/settings/ldap_setting.html:24 #: settings/templates/settings/security_setting.html:24 -#: settings/templates/settings/terminal_setting.html:27 settings/views.py:74 +#: settings/templates/settings/terminal_setting.html:27 settings/views.py:75 msgid "LDAP setting" msgstr "LDAP设置" @@ -3793,7 +3799,7 @@ msgstr "LDAP设置" #: settings/templates/settings/email_setting.html:27 #: settings/templates/settings/ldap_setting.html:27 #: settings/templates/settings/security_setting.html:27 -#: settings/templates/settings/terminal_setting.html:31 settings/views.py:104 +#: settings/templates/settings/terminal_setting.html:31 settings/views.py:106 msgid "Terminal setting" msgstr "终端设置" @@ -3803,7 +3809,7 @@ msgstr "终端设置" #: settings/templates/settings/ldap_setting.html:30 #: settings/templates/settings/security_setting.html:30 #: settings/templates/settings/security_setting.html:45 -#: settings/templates/settings/terminal_setting.html:34 settings/views.py:159 +#: settings/templates/settings/terminal_setting.html:34 settings/views.py:161 msgid "Security setting" msgstr "安全设置" @@ -3827,11 +3833,6 @@ msgstr "创建用户设置" msgid "Bulk import" msgstr "一键导入" -#: settings/templates/settings/ldap_setting.html:116 -msgid "" -"User is not currently selected, please check the user you want to import" -msgstr "当前无勾选用户,请勾选你想要导入的用户" - #: settings/templates/settings/replay_storage_create.html:66 msgid "Bucket" msgstr "桶名称" @@ -3936,30 +3937,26 @@ msgstr "删除失败" msgid "Are you sure about deleting it?" msgstr "您确定删除吗?" -#: settings/utils.py:98 +#: settings/utils/ldap.py:130 msgid "Search no entry matched in ou {}" msgstr "在ou:{}中没有匹配条目" -#: settings/utils.py:172 -msgid "The user source is not LDAP" -msgstr "用户来源不是LDAP" - -#: settings/views.py:19 settings/views.py:46 settings/views.py:73 -#: settings/views.py:103 settings/views.py:131 settings/views.py:144 -#: settings/views.py:158 settings/views.py:185 templates/_nav.html:170 +#: settings/views.py:20 settings/views.py:47 settings/views.py:74 +#: settings/views.py:105 settings/views.py:133 settings/views.py:146 +#: settings/views.py:160 settings/views.py:187 templates/_nav.html:170 msgid "Settings" msgstr "系统设置" -#: settings/views.py:30 settings/views.py:57 settings/views.py:84 -#: settings/views.py:116 settings/views.py:169 settings/views.py:196 +#: settings/views.py:31 settings/views.py:58 settings/views.py:85 +#: settings/views.py:118 settings/views.py:171 settings/views.py:198 msgid "Update setting successfully" msgstr "更新设置成功" -#: settings/views.py:132 +#: settings/views.py:134 msgid "Create replay storage" msgstr "创建录像存储" -#: settings/views.py:145 +#: settings/views.py:147 msgid "Create command storage" msgstr "创建命令存储" @@ -4586,7 +4583,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户" msgid "Set password" msgstr "设置密码" -#: users/forms.py:132 xpack/plugins/change_auth_plan/models.py:88 +#: users/forms.py:132 xpack/plugins/change_auth_plan/models.py:89 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57 @@ -5522,8 +5519,8 @@ msgstr "" "具)
注意: 如果同时设置了定期执行和周期执行,优先使用定期执行" #: xpack/plugins/change_auth_plan/meta.py:9 -#: xpack/plugins/change_auth_plan/models.py:116 -#: xpack/plugins/change_auth_plan/models.py:256 +#: xpack/plugins/change_auth_plan/models.py:117 +#: xpack/plugins/change_auth_plan/models.py:257 #: xpack/plugins/change_auth_plan/views.py:33 #: xpack/plugins/change_auth_plan/views.py:50 #: xpack/plugins/change_auth_plan/views.py:74 @@ -5534,20 +5531,20 @@ msgstr "" msgid "Change auth plan" msgstr "改密计划" -#: xpack/plugins/change_auth_plan/models.py:57 +#: xpack/plugins/change_auth_plan/models.py:58 msgid "Custom password" msgstr "自定义密码" -#: xpack/plugins/change_auth_plan/models.py:58 +#: xpack/plugins/change_auth_plan/models.py:59 msgid "All assets use the same random password" msgstr "所有资产使用相同的随机密码" -#: xpack/plugins/change_auth_plan/models.py:59 +#: xpack/plugins/change_auth_plan/models.py:60 msgid "All assets use different random password" msgstr "所有资产使用不同的随机密码" -#: xpack/plugins/change_auth_plan/models.py:78 -#: xpack/plugins/change_auth_plan/models.py:147 +#: xpack/plugins/change_auth_plan/models.py:79 +#: xpack/plugins/change_auth_plan/models.py:148 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100 #: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91 @@ -5556,8 +5553,8 @@ msgstr "所有资产使用不同的随机密码" msgid "Cycle perform" msgstr "周期执行" -#: xpack/plugins/change_auth_plan/models.py:83 -#: xpack/plugins/change_auth_plan/models.py:145 +#: xpack/plugins/change_auth_plan/models.py:84 +#: xpack/plugins/change_auth_plan/models.py:146 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92 #: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83 @@ -5566,37 +5563,37 @@ msgstr "周期执行" msgid "Regularly perform" msgstr "定期执行" -#: xpack/plugins/change_auth_plan/models.py:92 +#: xpack/plugins/change_auth_plan/models.py:93 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74 msgid "Password rules" msgstr "密码规则" -#: xpack/plugins/change_auth_plan/models.py:212 +#: xpack/plugins/change_auth_plan/models.py:213 msgid "* For security, do not change {} user's password" msgstr "* 为了安全,禁止更改 {} 用户的密码" -#: xpack/plugins/change_auth_plan/models.py:216 +#: xpack/plugins/change_auth_plan/models.py:217 msgid "Assets is empty, please add the asset" msgstr "资产为空,请添加资产" -#: xpack/plugins/change_auth_plan/models.py:260 +#: xpack/plugins/change_auth_plan/models.py:261 msgid "Change auth plan snapshot" msgstr "改密计划快照" -#: xpack/plugins/change_auth_plan/models.py:275 -#: xpack/plugins/change_auth_plan/models.py:426 +#: xpack/plugins/change_auth_plan/models.py:276 +#: xpack/plugins/change_auth_plan/models.py:433 msgid "Change auth plan execution" msgstr "改密计划执行" -#: xpack/plugins/change_auth_plan/models.py:435 +#: xpack/plugins/change_auth_plan/models.py:442 msgid "Change auth plan execution subtask" msgstr "改密计划执行子任务" -#: xpack/plugins/change_auth_plan/models.py:453 +#: xpack/plugins/change_auth_plan/models.py:460 msgid "Authentication failed" msgstr "认证失败" -#: xpack/plugins/change_auth_plan/models.py:455 +#: xpack/plugins/change_auth_plan/models.py:462 msgid "Connection timeout" msgstr "连接超时" @@ -6206,6 +6203,12 @@ msgstr "密码匣子" msgid "vault create" msgstr "创建" +#~ msgid "succeed: {} failed: {} total: {}" +#~ msgstr "成功:{} 失败:{} 总数:{}" + +#~ msgid "The user source is not LDAP" +#~ msgstr "用户来源不是LDAP" + #~ msgid "Recipient" #~ msgstr "收件人" From 82077f4a0e247a79be0b95e5db3ff35c2dd6c4d7 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Nov 2019 18:05:32 +0800 Subject: [PATCH 09/17] =?UTF-8?q?[Update]=20=E9=87=8D=E6=9E=84=20LDAP/AD?= =?UTF-8?q?=20=E5=90=8C=E6=AD=A5=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=BC=93=E5=AD=98=E6=9C=BA=E5=88=B6=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/templates/settings/ldap_setting.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/settings/templates/settings/ldap_setting.html b/apps/settings/templates/settings/ldap_setting.html index e1d7af7a6..5da66405d 100644 --- a/apps/settings/templates/settings/ldap_setting.html +++ b/apps/settings/templates/settings/ldap_setting.html @@ -63,9 +63,8 @@
-{# #} - +
From e0d7a0e23933f9b86d68a720bccb09e7ada0e44b Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Nov 2019 18:10:21 +0800 Subject: [PATCH 10/17] =?UTF-8?q?[Update]=20=E9=87=8D=E6=9E=84=20LDAP/AD?= =?UTF-8?q?=20=E5=90=8C=E6=AD=A5=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=BC=93=E5=AD=98=E6=9C=BA=E5=88=B6=204?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/templates/settings/_ldap_list_users_modal.html | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/settings/templates/settings/_ldap_list_users_modal.html b/apps/settings/templates/settings/_ldap_list_users_modal.html index 1b2597924..8589b0066 100644 --- a/apps/settings/templates/settings/_ldap_list_users_modal.html +++ b/apps/settings/templates/settings/_ldap_list_users_modal.html @@ -89,6 +89,7 @@ function testRequestLdapUser(){ } if (status === 400){ toastr.error(data); + $("#fake_datatable_wrapper_loading").css('display', 'none'); clearInterval(interval); interval = undefined; return From fc58906bce535f9f3af6fa91539f9ac8b4d6c075 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Nov 2019 18:15:25 +0800 Subject: [PATCH 11/17] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | Bin 80616 -> 80819 bytes apps/locale/zh/LC_MESSAGES/django.po | 197 +++++++++++++-------------- apps/settings/api.py | 6 +- 3 files changed, 96 insertions(+), 107 deletions(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index ae4f19da812c095df7e12e22afc6c7383f957604..e002c1e2aab622b09e691c200aff0d5c6a8d983f 100644 GIT binary patch delta 23407 zcmYk@1(;UF+sE;hyQW^wm6m{LFpNoS8Xu&UyCPRc}9wyYYEk|8iv9*&avTxSp35XJzudJ@Gs* zqO!7{7y7a1HN!BRitTVHKE@`&p4Yvl=S?Es-^%kwVx88WcM(tG&q1E|T^r9!M!zBL zJnt0w&pUWtB)00vn4af*eW@fTF&yLLOiYOLFdUa-3fzbp@u+zpGY}{0+v@4#Sz>yG}&|Ju+XT1`6xu21ZFUibj}VauML*8oprL(D>>9E{2z$Ksfww_89{)Gcb+oBh{TwI`7SJ7ad7 zVI8-lu5=ITEx3x6@Pj_?Ss961_&U@M?KO{LIPqDGz$>VS^*-v>M)Y+T7UNS1JiVx` zDU3RyB9F$?k6s2x3kI{zf*#mlH?AcF1E_$g5R{mfL7 zP^pHxhs{tA(*V>nFcnkd5=@M{%@e2zE~B>cf#v@}y)AM3y9sik7G4*%uokEt?}$7j zzSotC?rC4tKwqMEWC?1eD^LsBZ24oDl=u|timqXLypLL7_yG6br$M#XLXF!5wSbnW zew{Hy@Bd&b>Npa03nrnqcDlI`bqiLb2HI~P#l^&@Q3LiL=yqf*MiIxNE@V0CZCH)k z*-e-Nk7HQi{imW14^R_4Lv7h>)Rl$~^1M`-8g;9RU^G@nZRIDZ3utS0HwT&{%}J<- zbQWryuh1_^Wi1s=d>eD(Bh-RY4R%+S5p`lg)QP1~&qPhs=Rrr*0w zpmri+h}+?$s2xr@g#Fh&&PqZP7DC;lQWjT6O;8)FU~klpY{e9K1e4>RsE6@ii{tSV zO1B`5nIEHxE23`I$EbO`4`u(A7(zl6;0T~9Do{lgvC=(A3(EE3*3S_aSv*s<5(DfL*2rN z;ch`un1(nDYA4F0#%*NzcBq~7eJUDo2ByS?sC&2twY5KDYWx+o6OU0l5jw&Rm;!Zv zR@4<1Lfyj3m<;QoChCA%@F3K>lbC_MNLo@ zHE=W30(zl#b|~r=jYjQIEb5le$Ao(SS5eUwZ$nMAFVNt5Cr|_bfm+~0i{GH0=ER(< zD^G^HH5o0=g&LBk^tcvl;LrFsMvr#mgpG0MN1_&xat!;gm8U163G$$>pgdN^TBxUZ z7OLNROpCkmL%e8l`mvlt`~hmg!%-JB4)u{f74^17% z#TnioT^euptWobC~N@Oz7Qq9)vny27KVTXP=6@DgUjzc3gR zO+x1Pl2XZp$;{$dlejr*YnNdY{0XaLhRNVm#S?chf95Ncr; z%vij8~8)u`ocrWVw+o%OU zMZL~1a5aWa<6>~jH1oPy_cuZT(P8h@(&sTi%+56hFhrq|N2xkLBbhsz_h5X%#6zCMlGZi>glbD`b2Dj+R8zwXJ-Pou~^rf*S7^)P-Kctauf5L4L?gcSQ+NCq$#RFpK4DqHakO)RnZhxQn&-!$|U< zq82a%wSYyaomyl0ZKw-4jC%bpB0K1NPp!j$sHZ(F)~z%*>PqvY-v64I6g!~4QVl{a zXgcP^d8iB6k5PCMbs^U=5);gFSDp^F(2AHu?|%a-x|i+DuBd_fptfiP>Yj~3y%n*j z1#B>nVlv|EsC)hb_0T1n?Iw;wEhro6^P?2%t!s)I^!^W21?Qk9+Jf4`L#VAkiyH6} zYDcc2uKX>kU&tKSo(#3MsZbv<1ySc0!ID@OwSd8>^Jk-xH^Tf74`(Q(wo zw^98bp!&Tzv^ zkb&fzg%`m5SPpewFVwAq;|MOU<^r(rlnfXy$Tmp3~tDr8Z0qTmIqxyHn6xa{b z;v@{y`@h^eYzQQH`ce1v0BV4fs0Cd?ZE55}x1cPjhchQOz_Q2{c{5P2^A^-I@+;~Y zx@6u(?dWq1{Qm!jiaLgT?Y1-#>I$->t}H+5R+K?)Z5`CscR?+1G-|+^sBz|^7Wy@6 zoDCLlxAtSGXXOm~+On%ubPpe(27Hd1Fl3SI7>&9$*-=+q3NvF}OoRO}6;8wSxDs_s ze?pCa88zWu%z-aa3(fQm``?^O&Tm|Yv1TmliWZ|5wgNTr_gD}QqXvA5x~HLw-Pi04 zn2xv>D&G}#??<6_auVtS=c3-G6^ng$!jB}hqRXg>Z(4j0_3%AG?Z|V~fnq>I!n87FHHDL4DL#H%IMUSJZfeQR7cU^^Zke=rWAKZ9WyP_yT6f z%hnKojq8vd^+{F@^(?ePJ*p- z(g!o*eAGgAVoSV%UGT$o?oTeOQLoh_)Idendmf*{UUeLd_fS6r+HY__BgWtk;^nCR zZNKNwuQ&+%>-|4RB{zxc8{I=O0IL(nBDdSSgxPSyCig+K4)qWo!F+fFbpcVEouyG1 z&;g6%R4j=HEq;f38;Wny&U63EQPDlGgrl%7KEu-(g@?Dgt-Od?iIZ;Q_d_gh4#B*{ zKVTuegSx`B+xaelL$Do&vkm>R7q-D0*qZshDnGcDFUKmxr?5Gu-06N6^u-dy%TNot zg33@84;tZFvIMC4ncp0%DBjQ!A$LuDWpoj4TrzJ7+$APE?PDh=;1hv3*sBsUY^5;?GUPtAh`Bc;pZ?D_R zq-H8CO+F)PYd^tm*cmnPIn=$pgF62qY6t#9?Lg>0cg5*Zw=yqkM~b3uMLkr1zXg>J zRJvepJc$MI6{=(2{ceI1s4c97g|Q*(VH}U@w*Yl-m!Yn3E9xOVg1XXcsD(bU_$|`k z_Yxi80ihuS>PkDKR@N7_pplpvS6llj)K>qFx}pcD2}2LM1*JxPB*dB~W{17wXKd7BZcEascdep7UeuDkiL-QdCZGA)3mbSw&I0zHrE7Sl9 zPP&1UVFYn%)Weq@^$Zk3^{<4rSy)3!{0-ZooqmM6;9jSBje~iMPLmx%;@Me#jZ(uo zzR%$k+`|Ctf8mwF-skxuNxs_!_ms!{?j|mXx|gL9co=ixDa?bfumI+`=<>}l9r0MqhKn&Z9>EyAVeRq$aQ!mlNb(i1GVVj|VCW_O z9*mKg5dA|`w3X+~tEhYY5OrnGEFXH=<&&fOr!{k$#mtIkJ=8cY&Gx7TbhGvWPTw0% zMIRK?tYHP}#LcJ$?8L-)(%P?>_fcE=(&EHdY++_j)Op1$u3~XRvt1zX^J6ejVY^Th zPc>(m3(Uo+D_DuzfsN*msD=J){)wH4pQ8G=_|yGepabS0o`g+tBewRbgkN=k8tHDX zHt(BxuDSQVJ8FT;Py?^Acq?Wn-fiBpe4^{_lP@{yEhvJeaJ1!jp|98N4=TE%zs)yh zf*UTM64ft@SpxO&)Iu$&x!K0-j2f?(IozCR&OzOpB{z60btM}~Xlr&^hg;@T%X>H7 zfH}=#n4bO>EpCf?hI*lH#RPMK<=0w#*!&%}zy~+ke^ve?p%sPPas#I{i=o;ZV|;9j zaj*+&;XN=54np<&6651ai`SdGEPoWWz|)q$;Zw;-;;|*7{&EY5!T99MnN?5|)Ha)2 zzLVM09ANFk&57n5YhQx8fK}KZ{k7I1^KCal0n`f1pgvmbVJNmhy>9Kzp{NB-NA1K{ z=5ot#K#j8-b!$#reAnVP$U=NC#T_?5YBP(O&n#hnh}w}_7B|Eq#4Rm96Sb4`to;Yn zEj?)Mzn~WU2Wnw=FpYjHytYL2UAK_jsC!xzi(zxr#52sf7)JaxYQUA2-(vADi;rS9 z+D~Iod|~aK?zsi@#|XXuiQucIb>XZeWxF3w=)K`peT#pTV~ zmTzvhv3zIrHSqvTjI$0iQTchO1+BF9J*X=`j+*FK^OEInUiwQ%@k-Q*J1st9{$cq?7QaRsNXDq&HK0!U@@66~&Zo-172`icPQ49LS9O_d^Ok$2D zmZ7d-ABNy@OpNC&f5YO3sDa;FKKyU@#UnW?pAi#dKC`S@2X*T{LG|-HSf%g#ggFJZ zW%JC{mfwTA(&HB2G@qF7P!mKvb`wOQ;uuVb*-;BC>hiuOTlI;indVZtb%Ja{m`o(LGsb9z%W2{tM@0#;0!Jou~ni zn5Qg%9yP%Yi~m9Wm<{^JJ%puE7uWzJu@x4;zKZq!FR{cH)C7N-56x$ol>BSdCtRXu zZlD6Ffh(du)9azmZ-hy(1?pMqZux#TjJ#rrKj^)K(guIxMsop{F@o?6HMEDm|@CQgLvm&VMB zT0nlYm{|t(kXEv|C2HaA%|4i&_|xa?zdq}iS;H~&H`G17Y4J1EM8PlIKw+o}QlRc> ze#?Jo*0ub{n2z>d7>!d=U)z_V#@p*t$wlQS)C!+s0`y+G4&kVLBAM);=OezM55wU7H6|KuUX3SRn0nBg#L{! zo`m|On}HhVYjd@^9ksAS7cmeg^7x01t zf9WWKdUk4=O;K-GThuuHQ41PoPQbu_|390GwsM6v>_bg>(&B5Vd;bVEP-u{g6Qc&q zfyx&}JzS+xx3G%Y1d|hYG>4+mP)#JiXkUs;?nuDh~As4H%PQP>%^6T{6h z);`JNFU*CgEnjMGGyP*!w4%%AL)3t8twX$c?%qYCZc#q7s@d8cf?C*2)I?uf{5@*C zJ*bb|L#Um2==8l8mI#g?6!@|jf!e~%r~$L1K0u0K;8!ine~FrCCF%lpq851EyorGe zK|Or&Lfm+1Q287IeLlOXXrN-Kt*>rzbF4w!1GSKC=0WoeW+Q*a;`pI%oG8?SvRPaV zb$)fT36>*nhw+);TVf5X%&n*`I$-e$)QT@+8oY*@;H{Y`%uSHq%x#uHombi7S{64m zTcfWIoh;E4wG#tSA4C&SD_?DHxAsG*1zbQa=#J&%C2;+tQMV$i#br?sYfaR7tx&h5 zR|4LDee#X7j>}OeZnF4i)Cs>^d>1w0b2BR3U15H+7;3>~%o=7>Yj1CHH`Ij=4(I*X z36rg1E^1{?7LP%VHyt&O{{@wg zsH{SLbjC>(Iw&=c3=jM!MUgfrcLV1iE1x~+QBlY@v5Re>YJkae~y8l{|l(-hsRpf z1Q$>b;SE}4IuaiZM$DNx^pGGk8s2=#?zv^mwBYc7f6{nys4C83q=M!h~iS^Us^VFo92 zR~muo=oe*iK@2=2W@WPhYQe26?rX7c&P-8aP3(+sE$)=Unupr4WfreT zoxjWC1LkSef-YP9*nDgH;VIpK(Wn8lTAUv>U}=jhVm#t{mTzLVMBU@|sD<@HUEy%l zg-k(>yVTm(p~l_m^u7I5^z;6xHJm`5@CzowE0%v~J~LmVCX5s9-i9O?mpB_LpBr`M zCCr+r1$|-;!U(5pUjxuIz)J}~tzeG*E88z>j zw7mb?@+%~?;wPv$D4h+6`bt&?GhiLm50RdzE1Qa%_$$=Jhb+E>Rfr?g2L=9=(+o=x z$D(f0anu5`WZ?auL#1E_*YFc+#W69?lBo6usD<1?eTM&s`lD3AjBY1dp+1sFVQKsV z`{6m%7nW+7+=APqF0empp7FjV7Fmaln1Y7=SPg&2%9uH``vTGh^>)la-I@icD_M-a za2+1R3|ZV(-^2pM!C8X>|6Z{$>f3Z{)DHPes1&7g7aLp{_h`uAsobTTY4ma_W0|T*WJk zx}tihD{5-CH~U!or>GC2aj4IY$(Emn`XHKP@i(XmSD<$62ONqAF&xY1)_lDGwWz2= zL$f97#7=<@{PJk|QI?;KdI%Sy9>QJb&!~l8LY;rt;y2cwAdkBxsZs64F+KBpHK^#s zE~tlSAnKu+V)+&3W^*6vX+LT46Z4%Jk=Kov%FKqEurTVOt77?9=qu6PI*hZ1xu}P5 zsl_|ZBd9C>)#6*`Q`8Q6`J4%`JYh1_Ep3bq@nh6N)?0pmKHh(II7&i0aN0UtMosh> zLoq174UD??$xyFjYWx&)V+mYkUPq0WwtzDyYP@1t3ag+NGO7UYzg9lWIxIBTqHe`* zi_f9@-LUwf`4&r(PgKxNRLyLRdMnzZJ}IYL{(ICy_MpZ;>09Ea`3yBtyh3h;iBaEl zGNG=hyya_SMdH?|9sCw`-gl_CVl%4$cFP|^^*d$x^QM2p8XlW3%y(w|!tTU`sFf!} zZCw@|g2hn}@dnhy1&g?a6h$qd9BSfPs1Krsm=(ujZoU7js5B?>E6%`zMT5Mv)qaN2TRy(O~YKHspVy+;xA}=eJcF zyV1A=6ERpG2KWG*&`Dn)c3=aFj)WLVziT#Ff!O5LvbR}96o;0gI0;`;^sf}^)3X&8 zBL5HN67{yU)gj-8I)B*nrV}TogwURjqGKnqe!}ZZcMj@FV!x`E(!WnFlyyy{*Faj5 z5m#V@?vxbNJKN;cw;=%0-K|9hkj z<`GIuqkgL9=LEg~1?ZTG`d{RWQtoq-eyG{J-eqz++B&@HG@K&0o|um)?;-VC_zUrP z+RhP2*vVJzoCV~9C~fpX_ZN-(2_AEjj(ya1RACWk$#tc^&)WW`{x$v6(MR7WpIQDU zxg*pYlaHl7)y7y&ZY1R)xoV8}31+2DKOA+GWqvOa9e$_$L_>x^nqOq;sADfB3;Ac1 zC*<^Feh7WPr3@gqiK1f*@rO*%8GBH4)S>)G`}IJ?qeng|@j=GfNts1nA5h*z$_f%C zY_L0Y%uKAGuxr$T;{a_F$$x2ml&eG^eQ(gwl6*(X2MW@FO=2X($=1dffg4W=Gu@dn}*v@CJW-apu$Tr1AWu4hyn&Ud%K zk&-x?b{(IQ*YTE;n0y@i+#y$(_|Mk-- zZPG2Ytt8gbo?J%GPaZp?c1k~rY-LJOMkqq|IIZ`Hk5DQQ52PHS==ho3ONx%M&MQhALU=?tg<#`6g`H}`YWwEN|67?MlEcXBUg{MX*NrsBmYRlx!=-f z1M-c^t4MnS3?`R}`Vjhz!~7j4J?~f~-*_2#4 ztcQG&_6AT6Q*`Wic)!v>CK)ss9cLG`dXn@7&f8O%jh<~(kXOMf#T$$r|vDfP*^~aMxO=)ebOhjM(9#EBj zamn|huA>6uRi)@SPv3ghzZh{(`kkIiPI*|xMCz4rJ|`Tbt|J#En3Hs@wA@M; z2L5yTX0&zT{EL>b@IUVSt)1OxaA^j}#q$)As(DVB-#?P4lj{n7^9v@BMk@^i$x8ozRhl ze)rPxFZCOEmhuhluk1v%&8O`I@d@(3S)RWWdF`lgqSTf3jMe`N<~zl)8>V#J^F-SUi*Zf0U#DGudj|lG%h8$(N!1YtHS3 zWr&MdTQWN*hIj()Q#675n#M;U$!kO(Q5JII`{Rt&m*P0eBPJ_ijhCpOvi9+qnG(e~ z`6x-P&or|YZLecDG)m(Sv1N?JresH3{0a5XDLN978%BK`^#zoY#Az5c2cD%oX6E+f zZ_;NLM5`b^UT)of2plA4}ah& zM&p;1y>!@%eJMI>VjRkG>%Y{cy&%k-H z4Vt9LTA5SRQ%*AF3`$Fy&S5OwHsNqitD_&i^%oFLp*4(_f2iv?L%lO4lzJ6>e;lS? zE8_a^6F8GNIi(n7E$4p``?yK=gyo4|S+C-nC$>b>h=he%k&c{<^ES4A(=>i3YFR10 z=wFf6?6lS*zC*0zPwHK85Ty@w9T)H|r8fPmGh_KcmLDqST)T=U^m)!WAL1$;Z0)yb ze@8t*Fh5IM(D;#^&<(2)e?aF)bm(u3ct9>LZ9fo4*d?qZr(-4g7q05Hz#jDZJ+{I} zY5Y|->fd;flAo58oVA!Tm@<#tB+j@;J_LUz9%iFzz+Z^FQgkGt&mLC|{Bw`>%Zqtw z+eV4D_RHj|=#xJuOHE7i3mP9#me8=pPJBPsdg5><`Gmy3l>Ee_ZPIbnzvsLa7Qa77 zKBt`ipL~4g>PFu`IA;uHlzxX?O(&lqHDxZ1I)0>Hz*W8Eob--fgurDyQm zl;!0A`k#rmS$#46VmLPoW6Y&bQc5G@48-ekhhDx9DB(2B!z7$|gNDQym)tr$NW2{D za?%@e87N;8>nK8f7xk<3iKO0+dO!S`w#L*STEFjTpG5s<)bTMcrT8c5tfM1dq(e>0 zC>o|=0@vgn!)LVrL@7=_4<5sJ6dkwGr~Jx!T`8$qsE&kK8DCSDQ`T_aQ~HI_XB71^ z`k$2>kho4l$47X8`2CT@22vbu^`vHTa%(9W8K^MlY_>7fcMA0wa=R>-hCW->z!7cd z_oH4bF8d#16RxB4sX!P08bYT$l;eDj$M?xHem#P8W7i} z?HHvF`I^=?nYa-3g*tEJFD=I9N#3Dh$W_VdF;=8plc3 zqj#^q{X4Xe88*UN`weKCo1OVhK09AF26N*(arT^Z+)@o*6axzN51R)!T$km C5@m+~ delta 23216 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
    u`qGMqi$i9 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\n" "Language-Team: Jumpserver team\n" @@ -197,7 +197,7 @@ msgstr "参数" #: orgs/models.py:16 perms/models/base.py:54 #: perms/templates/perms/asset_permission_detail.html:98 #: perms/templates/perms/remote_app_permission_detail.html:90 -#: users/models/user.py:414 users/serializers/v1.py:143 +#: users/models/user.py:414 users/serializers/group.py:32 #: users/templates/users/user_detail.html:111 #: xpack/plugins/change_auth_plan/models.py:109 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113 @@ -308,7 +308,7 @@ msgstr "远程应用" #: settings/templates/settings/security_setting.html:73 #: settings/templates/settings/terminal_setting.html:71 #: terminal/templates/terminal/terminal_update.html:45 -#: users/templates/users/_user.html:50 +#: users/templates/users/_user.html:51 #: users/templates/users/user_bulk_update.html:23 #: users/templates/users/user_detail.html:178 #: users/templates/users/user_group_create_update.html:31 @@ -352,7 +352,7 @@ msgstr "重置" #: terminal/templates/terminal/command_list.html:47 #: terminal/templates/terminal/session_list.html:52 #: terminal/templates/terminal/terminal_update.html:46 -#: users/templates/users/_user.html:51 +#: users/templates/users/_user.html:52 #: users/templates/users/forgot_password.html:42 #: users/templates/users/user_bulk_update.html:24 #: users/templates/users/user_list.html:57 @@ -703,7 +703,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" #: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:70 #: perms/templates/perms/asset_permission_user.html:55 #: perms/templates/perms/remote_app_permission_user.html:54 -#: settings/templates/settings/_ldap_list_users_modal.html:31 users/forms.py:13 +#: settings/templates/settings/_ldap_list_users_modal.html:31 users/forms.py:14 #: users/models/user.py:371 users/templates/users/_select_user_modal.html:14 #: users/templates/users/user_detail.html:67 #: users/templates/users/user_list.html:36 @@ -730,7 +730,7 @@ msgstr "密码或密钥密码" #: authentication/forms.py:15 #: authentication/templates/authentication/login.html:68 #: authentication/templates/authentication/new_login.html:95 -#: settings/forms.py:114 users/forms.py:15 users/forms.py:27 +#: settings/forms.py:114 users/forms.py:16 users/forms.py:42 #: users/templates/users/reset_password.html:53 #: users/templates/users/user_password_authentication.html:18 #: users/templates/users/user_password_update.html:44 @@ -1110,9 +1110,10 @@ msgstr "默认资产组" #: terminal/models.py:156 terminal/templates/terminal/command_list.html:29 #: terminal/templates/terminal/command_list.html:65 #: terminal/templates/terminal/session_list.html:27 -#: terminal/templates/terminal/session_list.html:71 users/forms.py:319 +#: terminal/templates/terminal/session_list.html:71 users/forms.py:339 #: users/models/user.py:127 users/models/user.py:143 users/models/user.py:500 -#: users/serializers/v1.py:132 users/templates/users/user_group_detail.html:78 +#: users/serializers/group.py:21 +#: users/templates/users/user_group_detail.html:78 #: users/templates/users/user_group_list.html:36 users/views/user.py:250 #: xpack/plugins/orgs/forms.py:28 #: xpack/plugins/orgs/templates/orgs/org_detail.html:113 @@ -1265,7 +1266,7 @@ msgstr "组织名称" msgid "Backend" msgstr "后端" -#: assets/serializers/asset_user.py:67 users/forms.py:262 +#: assets/serializers/asset_user.py:67 users/forms.py:282 #: users/models/user.py:403 users/templates/users/first_login.html:42 #: users/templates/users/user_password_update.html:49 #: users/templates/users/user_profile.html:69 @@ -1436,7 +1437,7 @@ msgid "Asset list" msgstr "资产列表" #: assets/templates/assets/_asset_list_modal.html:33 -#: assets/templates/assets/_node_tree.html:40 +#: assets/templates/assets/_node_tree.html:39 #: ops/templates/ops/command_execution_create.html:70 #: ops/templates/ops/command_execution_create.html:127 #: settings/templates/settings/_ldap_list_users_modal.html:41 @@ -1482,7 +1483,7 @@ msgstr "获取认证信息错误" #: assets/templates/assets/_user_asset_detail_modal.html:23 #: authentication/templates/authentication/_access_key_modal.html:142 #: authentication/templates/authentication/_mfa_confirm_modal.html:53 -#: settings/templates/settings/_ldap_list_users_modal.html:170 +#: settings/templates/settings/_ldap_list_users_modal.html:171 #: templates/_modal.html:22 msgid "Close" msgstr "关闭" @@ -1532,31 +1533,31 @@ msgstr "SSH端口" msgid "If use nat, set the ssh real port" msgstr "如果使用了nat端口映射,请设置为ssh真实监听的端口" -#: assets/templates/assets/_node_tree.html:50 +#: assets/templates/assets/_node_tree.html:49 msgid "Add node" msgstr "新建节点" -#: assets/templates/assets/_node_tree.html:51 +#: assets/templates/assets/_node_tree.html:50 msgid "Rename node" msgstr "重命名节点" -#: assets/templates/assets/_node_tree.html:52 +#: assets/templates/assets/_node_tree.html:51 msgid "Delete node" msgstr "删除节点" -#: assets/templates/assets/_node_tree.html:166 +#: assets/templates/assets/_node_tree.html:165 msgid "Create node failed" msgstr "创建节点失败" -#: assets/templates/assets/_node_tree.html:178 +#: assets/templates/assets/_node_tree.html:177 msgid "Have child node, cancel" msgstr "存在子节点,不能删除" -#: assets/templates/assets/_node_tree.html:180 +#: assets/templates/assets/_node_tree.html:179 msgid "Have assets, cancel" msgstr "存在资产,不能删除" -#: assets/templates/assets/_node_tree.html:255 +#: assets/templates/assets/_node_tree.html:254 msgid "Rename success" msgstr "重命名成功" @@ -1698,7 +1699,7 @@ msgstr "导出" #: assets/templates/assets/admin_user_list.html:21 #: assets/templates/assets/asset_list.html:73 #: assets/templates/assets/system_user_list.html:24 -#: settings/templates/settings/_ldap_list_users_modal.html:171 +#: settings/templates/settings/_ldap_list_users_modal.html:172 #: users/templates/users/user_group_list.html:15 #: users/templates/users/user_list.html:15 #: xpack/plugins/license/templates/license/license_detail.html:110 @@ -1883,16 +1884,16 @@ msgstr "删除选择资产" msgid "Cancel" msgstr "取消" -#: assets/templates/assets/asset_list.html:434 +#: assets/templates/assets/asset_list.html:432 msgid "Asset Deleted." msgstr "已被删除" -#: assets/templates/assets/asset_list.html:435 -#: assets/templates/assets/asset_list.html:439 +#: assets/templates/assets/asset_list.html:433 +#: assets/templates/assets/asset_list.html:441 msgid "Asset Delete" msgstr "删除" -#: assets/templates/assets/asset_list.html:438 +#: assets/templates/assets/asset_list.html:440 msgid "Asset Deleting failed." msgstr "删除失败" @@ -2265,7 +2266,7 @@ msgstr "Agent" #: audits/models.py:86 audits/templates/audits/login_log_list.html:62 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms.py:174 users/models/user.py:395 +#: users/forms.py:194 users/models/user.py:395 #: users/templates/users/first_login.html:45 msgid "MFA" msgstr "MFA" @@ -2488,7 +2489,7 @@ msgid "" "after {} minutes)" msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)" -#: authentication/forms.py:66 users/forms.py:21 +#: authentication/forms.py:66 users/forms.py:22 msgid "MFA code" msgstr "MFA 验证码" @@ -3136,7 +3137,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件" #: perms/templates/perms/asset_permission_list.html:71 #: perms/templates/perms/asset_permission_list.html:118 #: perms/templates/perms/remote_app_permission_list.html:16 -#: templates/_nav.html:21 users/forms.py:293 users/models/group.py:26 +#: templates/_nav.html:21 users/forms.py:313 users/models/group.py:26 #: users/models/user.py:379 users/templates/users/_select_user_modal.html:16 #: users/templates/users/user_detail.html:218 #: users/templates/users/user_list.html:38 @@ -3396,29 +3397,41 @@ msgstr "远程应用授权用户列表" msgid "RemoteApp permission RemoteApp list" msgstr "远程应用授权远程应用列表" -#: settings/api.py:34 +#: settings/api.py:37 msgid "Test mail sent to {}, please check" msgstr "邮件已经发送{}, 请检查" -#: settings/api.py:73 +#: settings/api.py:76 msgid "Test ldap success" msgstr "连接LDAP成功" -#: settings/api.py:113 +#: settings/api.py:107 +msgid "LDAP attr map not valid" +msgstr "LDAP 属性映射无效" + +#: settings/api.py:116 msgid "Match {} s users" msgstr "匹配 {} 个用户" -#: settings/api.py:258 settings/api.py:294 +#: settings/api.py:224 +msgid "Get ldap users is None" +msgstr "获取 LDAP 用户为 None" + +#: settings/api.py:231 +msgid "Imported {} users successfully" +msgstr "导入 {} 个用户成功" + +#: settings/api.py:262 settings/api.py:298 msgid "" "Error: Account invalid (Please make sure the information such as Access key " "or Secret key is correct)" msgstr "错误:账户无效 (请确保 Access key 或 Secret key 等信息正确)" -#: settings/api.py:264 settings/api.py:300 +#: settings/api.py:268 settings/api.py:304 msgid "Create succeed" msgstr "创建成功" -#: settings/api.py:282 settings/api.py:320 +#: settings/api.py:286 settings/api.py:324 #: settings/templates/settings/terminal_setting.html:154 msgid "Delete succeed" msgstr "删除成功" @@ -3752,7 +3765,7 @@ msgstr "邮件" msgid "Existing" msgstr "已存在" -#: settings/templates/settings/_ldap_list_users_modal.html:143 +#: settings/templates/settings/_ldap_list_users_modal.html:144 msgid "" "User is not currently selected, please check the user you want to import" msgstr "当前无勾选用户,请勾选你想要导入的用户" @@ -3829,7 +3842,7 @@ msgstr "文档类型" msgid "Create User setting" msgstr "创建用户设置" -#: settings/templates/settings/ldap_setting.html:68 +#: settings/templates/settings/ldap_setting.html:66 msgid "Bulk import" msgstr "一键导入" @@ -3973,8 +3986,8 @@ msgid "Commercial support" msgstr "商业支持" #: templates/_header_bar.html:70 templates/_nav.html:30 -#: templates/_nav_user.html:32 users/forms.py:153 -#: users/templates/users/_user.html:43 +#: templates/_nav_user.html:32 users/forms.py:173 +#: users/templates/users/_user.html:44 #: users/templates/users/first_login.html:39 #: users/templates/users/user_password_update.html:40 #: users/templates/users/user_profile.html:17 @@ -4538,7 +4551,7 @@ msgstr "你可以使用ssh客户端工具连接终端" msgid "Could not reset self otp, use profile reset instead" msgstr "不能再该页面重置MFA, 请去个人信息页面重置" -#: users/forms.py:32 users/models/user.py:383 +#: users/forms.py:47 users/models/user.py:383 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:87 #: users/templates/users/user_list.html:37 @@ -4546,44 +4559,51 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置" msgid "Role" msgstr "角色" -#: users/forms.py:35 users/forms.py:232 +#: users/forms.py:51 users/models/user.py:418 +#: users/templates/users/user_detail.html:103 +#: users/templates/users/user_list.html:39 +#: users/templates/users/user_profile.html:102 +msgid "Source" +msgstr "用户来源" + +#: users/forms.py:54 users/forms.py:252 #: users/templates/users/user_update.html:30 msgid "ssh public key" msgstr "ssh公钥" -#: users/forms.py:36 users/forms.py:233 +#: users/forms.py:55 users/forms.py:253 msgid "ssh-rsa AAAA..." msgstr "" -#: users/forms.py:37 +#: users/forms.py:56 msgid "Paste user id_rsa.pub here." msgstr "复制用户公钥到这里" -#: users/forms.py:51 users/templates/users/user_detail.html:226 +#: users/forms.py:71 users/templates/users/user_detail.html:226 msgid "Join user groups" msgstr "添加到用户组" -#: users/forms.py:86 users/forms.py:247 +#: users/forms.py:106 users/forms.py:267 msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms.py:90 users/forms.py:251 users/serializers/v1.py:116 +#: users/forms.py:110 users/forms.py:271 users/serializers/user.py:110 msgid "Not a valid ssh public key" msgstr "ssh密钥不合法" -#: users/forms.py:103 users/views/login.py:114 users/views/user.py:287 +#: users/forms.py:123 users/views/login.py:114 users/views/user.py:287 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/forms.py:124 +#: users/forms.py:144 msgid "Reset link will be generated and sent to the user" msgstr "生成重置密码链接,通过邮件发送给用户" -#: users/forms.py:125 +#: users/forms.py:145 msgid "Set password" msgstr "设置密码" -#: users/forms.py:132 xpack/plugins/change_auth_plan/models.py:89 +#: users/forms.py:152 xpack/plugins/change_auth_plan/models.py:89 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57 @@ -4591,7 +4611,7 @@ msgstr "设置密码" msgid "Password strategy" msgstr "密码策略" -#: users/forms.py:159 +#: users/forms.py:179 msgid "" "When enabled, you will enter the MFA binding process the next time you log " "in. you can also directly bind in \"personal information -> quick " @@ -4600,11 +4620,11 @@ msgstr "" "启用之后您将会在下次登录时进入MFA绑定流程;您也可以在(个人信息->快速修改->更" "改MFA设置)中直接绑定!" -#: users/forms.py:169 +#: users/forms.py:189 msgid "* Enable MFA authentication to make the account more secure." msgstr "* 启用MFA认证,使账号更加安全。" -#: users/forms.py:179 +#: users/forms.py:199 msgid "" "In order to protect you and your company, please keep your account, password " "and key sensitive information properly. (for example: setting complex " @@ -4613,41 +4633,41 @@ msgstr "" "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:" "设置复杂密码,启用MFA认证)" -#: users/forms.py:186 users/templates/users/first_login.html:48 +#: users/forms.py:206 users/templates/users/first_login.html:48 #: users/templates/users/first_login.html:110 #: users/templates/users/first_login.html:139 msgid "Finish" msgstr "完成" -#: users/forms.py:192 +#: users/forms.py:212 msgid "Old password" msgstr "原来密码" -#: users/forms.py:197 +#: users/forms.py:217 msgid "New password" msgstr "新密码" -#: users/forms.py:202 +#: users/forms.py:222 msgid "Confirm password" msgstr "确认密码" -#: users/forms.py:212 +#: users/forms.py:232 msgid "Old password error" msgstr "原来密码错误" -#: users/forms.py:220 +#: users/forms.py:240 msgid "Password does not match" msgstr "密码不一致" -#: users/forms.py:230 +#: users/forms.py:250 msgid "Automatically configure and download the SSH key" msgstr "自动配置并下载SSH密钥" -#: users/forms.py:234 +#: users/forms.py:254 msgid "Paste your id_rsa.pub here." msgstr "复制你的公钥到这里" -#: users/forms.py:268 users/forms.py:273 users/forms.py:323 +#: users/forms.py:288 users/forms.py:293 users/forms.py:343 #: xpack/plugins/orgs/forms.py:18 msgid "Select users" msgstr "选择用户" @@ -4690,12 +4710,6 @@ msgstr "头像" msgid "Wechat" msgstr "微信" -#: users/models/user.py:418 users/templates/users/user_detail.html:103 -#: users/templates/users/user_list.html:39 -#: users/templates/users/user_profile.html:102 -msgid "Source" -msgstr "用户来源" - #: users/models/user.py:422 msgid "Date password last updated" msgstr "最后更新密码日期" @@ -4704,46 +4718,46 @@ msgstr "最后更新密码日期" msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/serializers/v1.py:45 +#: users/serializers/group.py:46 +msgid "Auditors cannot be join in the user group" +msgstr "审计员不能被加入到用户组" + +#: users/serializers/user.py:40 msgid "Groups name" msgstr "用户组名" -#: users/serializers/v1.py:46 +#: users/serializers/user.py:41 msgid "Source name" msgstr "用户来源名" -#: users/serializers/v1.py:47 +#: users/serializers/user.py:42 msgid "Is first login" msgstr "首次登录" -#: users/serializers/v1.py:48 +#: users/serializers/user.py:43 msgid "Role name" msgstr "角色名" -#: users/serializers/v1.py:49 +#: users/serializers/user.py:44 msgid "Is valid" msgstr "账户是否有效" -#: users/serializers/v1.py:50 +#: users/serializers/user.py:45 msgid "Is expired" msgstr " 是否过期" -#: users/serializers/v1.py:51 +#: users/serializers/user.py:46 msgid "Avatar url" msgstr "头像路径" -#: users/serializers/v1.py:72 +#: users/serializers/user.py:66 msgid "Role limit to {}" msgstr "角色只能为 {}" -#: users/serializers/v1.py:84 +#: users/serializers/user.py:78 msgid "Password does not match security rules" msgstr "密码不满足安全规则" -#: users/serializers/v1.py:157 -msgid "Auditors cannot be join in the user group" -msgstr "审计员不能被加入到用户组" - #: users/serializers_v2/user.py:36 msgid "name not unique" msgstr "名称重复" @@ -4776,7 +4790,7 @@ msgstr "选择用户" msgid "Asset num" msgstr "资产数量" -#: users/templates/users/_user.html:26 +#: users/templates/users/_user.html:27 msgid "Security and Role" msgstr "角色安全" @@ -6203,12 +6217,6 @@ msgstr "密码匣子" msgid "vault create" msgstr "创建" -#~ msgid "succeed: {} failed: {} total: {}" -#~ msgstr "成功:{} 失败:{} 总数:{}" - -#~ msgid "The user source is not LDAP" -#~ msgstr "用户来源不是LDAP" - #~ msgid "Recipient" #~ msgstr "收件人" @@ -6334,25 +6342,6 @@ msgstr "创建" #~ msgid "Sync User" #~ msgstr "同步用户" -#~ msgid "Have user but attr mapping error" -#~ msgstr "有用户但attr映射错误" - -#~ msgid "" -#~ "Import {} users successfully; import {} users failed, the database " -#~ "already exists with the same name" -#~ msgstr "导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户" - -#~ msgid "" -#~ "Import {} users successfully; import {} users failed, the database " -#~ "already exists with the same name; import {}users failed, " -#~ "Because’TypeError' object has no attribute 'keys'" -#~ msgstr "" -#~ "导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户; 导入 " -#~ "{} 这些用户失败,因为对象没有属性'keys'" - -#~ msgid "Import {} users successfully" -#~ msgstr "导入 {} 个用户成功" - #~ msgid "" #~ "Import {} users successfully;import {} users failed, Because’TypeError' " #~ "object has no attribute 'keys'" diff --git a/apps/settings/api.py b/apps/settings/api.py index 7f349117b..f55b37749 100644 --- a/apps/settings/api.py +++ b/apps/settings/api.py @@ -104,7 +104,7 @@ class LDAPTestingAPI(APIView): try: json.loads(attr_map) except json.JSONDecodeError: - return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401) + return Response({"error": _("LDAP attr map not valid")}, status=401) config = self.get_ldap_config(serializer) util = LDAPServerUtil(config=config) @@ -221,14 +221,14 @@ class LDAPUserImportAPI(APIView): return Response({'error': str(e)}, status=401) if users is None: - return Response({'msg': 'Get ldap users is None'}, status=401) + return Response({'msg': _('Get ldap users is None')}, status=401) errors = LDAPImportUtil().perform_import(users) if errors: return Response({'errors': errors}, status=401) count = users if users is None else len(users) - return Response({'msg': 'Imported {} users successfully'.format(count)}) + return Response({'msg': _('Imported {} users successfully').format(count)}) class LDAPCacheRefreshAPI(generics.RetrieveAPIView): From 78a7bfbd30046a3aac14cf1ce392f7ceae807161 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Nov 2019 18:50:05 +0800 Subject: [PATCH 12/17] =?UTF-8?q?[Update]=20=E4=BF=AE=E5=A4=8D=E5=8F=96?= =?UTF-8?q?=E6=B6=88=20LDAP=20=E5=90=8C=E6=AD=A5=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E5=A4=B1=E8=B4=A5=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/settings/utils/ldap.py | 3 +-- apps/users/tasks.py | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/settings/utils/ldap.py b/apps/settings/utils/ldap.py index 3df45bfae..e5ad1c3e8 100644 --- a/apps/settings/utils/ldap.py +++ b/apps/settings/utils/ldap.py @@ -117,8 +117,6 @@ class LDAPServerUtil(object): return search_filter def search_user_entries_ou(self, search_ou, paged_cookie=None): - logger.info("Search user entries ou: {}, paged_cookie: {}". - format(search_ou, paged_cookie)) search_filter = self.get_search_filter() attributes = list(self.config.attr_map.values()) ok = self.connection.search( @@ -136,6 +134,7 @@ class LDAPServerUtil(object): user_entries = list() search_ous = str(self.config.search_ougroup).split('|') for search_ou in search_ous: + logger.info("Search user entries ou: {}".format(search_ou)) self.search_user_entries_ou(search_ou) user_entries.extend(self.connection.entries) while self.paged_cookie(): diff --git a/apps/users/tasks.py b/apps/users/tasks.py index 29355514d..45e1c40cd 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- # +import sys from celery import shared_task from django.conf import settings -from ops.celery.utils import create_or_update_celery_periodic_tasks +from ops.celery.utils import ( + create_or_update_celery_periodic_tasks, disable_celery_periodic_task +) from ops.celery.decorator import after_app_ready_start from common.utils import get_logger from .models import User @@ -88,6 +91,8 @@ def import_ldap_user_periodic(): if not settings.AUTH_LDAP: return if not settings.AUTH_LDAP_SYNC_IS_PERIODIC: + task_name = sys._getframe().f_code.co_name + disable_celery_periodic_task(task_name) return interval = settings.AUTH_LDAP_SYNC_INTERVAL From 4057064b7f4f06fee12eae33c74b799f24d5dc40 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Tue, 12 Nov 2019 19:00:53 +0800 Subject: [PATCH 13/17] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E7=B3=BB=E7=BB=9F=E7=94=A8=E6=88=B7-=E8=B5=84?= =?UTF-8?q?=E4=BA=A7-=E8=AE=A4=E8=AF=81=E4=BF=A1=E6=81=AFAPI=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20NeedMFAVerify=20=E6=9D=83=E9=99=90?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/system_user.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index 5bf38853d..db0751343 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -14,11 +14,12 @@ # limitations under the License. from django.shortcuts import get_object_or_404 +from django.conf import settings from rest_framework.response import Response from common.serializers import CeleryTaskSerializer from common.utils import get_logger -from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser +from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, NeedMFAVerify from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins import generics from ..models import SystemUser, Asset @@ -72,6 +73,11 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView): permission_classes = (IsOrgAdminOrAppUser,) serializer_class = serializers.SystemUserAuthSerializer + def get_permissions(self): + if settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA: + self.permission_classes = (IsOrgAdminOrAppUser, NeedMFAVerify) + return super().get_permissions() + def get_object(self): instance = super().get_object() aid = self.kwargs.get('aid') From e731c01cc403386b8fb7a7c750029d04ba6e0b88 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Wed, 13 Nov 2019 11:18:29 +0800 Subject: [PATCH 14/17] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=20sytem-use?= =?UTF-8?q?r-asset-auth-info=20API=20=E7=9A=84=E6=9D=83=E9=99=90=EF=BC=88?= =?UTF-8?q?=E5=8F=AA=E5=85=81=E8=AE=B8=20App=EF=BC=89=E8=AE=BF=E9=97=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/system_user.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index db0751343..f1213f0a3 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -19,7 +19,7 @@ from rest_framework.response import Response from common.serializers import CeleryTaskSerializer from common.utils import get_logger -from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, NeedMFAVerify +from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsAppUser from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins import generics from ..models import SystemUser, Asset @@ -70,14 +70,9 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView): Get system user with asset auth info """ model = SystemUser - permission_classes = (IsOrgAdminOrAppUser,) + permission_classes = (IsAppUser,) serializer_class = serializers.SystemUserAuthSerializer - def get_permissions(self): - if settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA: - self.permission_classes = (IsOrgAdminOrAppUser, NeedMFAVerify) - return super().get_permissions() - def get_object(self): instance = super().get_object() aid = self.kwargs.get('aid') From 46a2311e302bbb13eca29846c95838fbe2563013 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 13 Nov 2019 11:25:33 +0800 Subject: [PATCH 15/17] =?UTF-8?q?[Update]=20=E6=9A=82=E6=97=B6=E5=85=B3?= =?UTF-8?q?=E9=97=ADcelery=E5=A4=84=E7=90=86=E8=AF=B7=E6=B1=82=E6=95=B0?= =?UTF-8?q?=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index f6b81e877..fe4f2fc84 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -516,7 +516,7 @@ CELERY_TASK_EAGER_PROPAGATES = True CELERY_WORKER_REDIRECT_STDOUTS = True CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO" # CELERY_WORKER_HIJACK_ROOT_LOGGER = True -CELERY_WORKER_MAX_TASKS_PER_CHILD = 40 +# CELERY_WORKER_MAX_TASKS_PER_CHILD = 40 CELERY_TASK_SOFT_TIME_LIMIT = 3600 # Cache use redis From aa428b02995fd3903738e6cf116669ab51d6b53a Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 13 Nov 2019 16:40:34 +0800 Subject: [PATCH 16/17] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=BD=91=E5=85=B3=E6=8F=90=E7=A4=BA=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E5=8C=85=E5=90=AB=E7=89=B9=E6=AE=8A=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/domain.py | 2 +- apps/assets/models/domain.py | 5 +- .../templates/assets/domain_gateway_list.html | 2 +- apps/locale/zh/LC_MESSAGES/django.mo | Bin 80819 -> 80775 bytes apps/locale/zh/LC_MESSAGES/django.po | 98 +++++++++--------- 5 files changed, 57 insertions(+), 50 deletions(-) diff --git a/apps/assets/api/domain.py b/apps/assets/api/domain.py index 13be62315..e6f6c0550 100644 --- a/apps/assets/api/domain.py +++ b/apps/assets/api/domain.py @@ -44,4 +44,4 @@ class GatewayTestConnectionApi(SingleObjectMixin, APIView): if ok: return Response("ok") else: - return Response({"failed": e}, status=404) + return Response({"error": e}, status=400) diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py index 2051181fe..99e677a07 100644 --- a/apps/assets/models/domain.py +++ b/apps/assets/models/domain.py @@ -3,9 +3,9 @@ import uuid import random +import re import paramiko - from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -63,6 +63,9 @@ class Gateway(AssetUser): def test_connective(self, local_port=None): if local_port is None: local_port = self.port + if not re.match(r'\w+$', self.password): + return False, _("Password should not contain special characters") + client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) proxy = paramiko.SSHClient() diff --git a/apps/assets/templates/assets/domain_gateway_list.html b/apps/assets/templates/assets/domain_gateway_list.html index ade46acc9..79636a2bc 100644 --- a/apps/assets/templates/assets/domain_gateway_list.html +++ b/apps/assets/templates/assets/domain_gateway_list.html @@ -139,7 +139,7 @@ $(document).ready(function(){ method: "POST", body: JSON.stringify({'port': parseInt(data.port)}), success_message: "{% trans 'Can be connected' %}", - fail_message: "{% trans 'The connection fails' %}" + {#fail_message: "{% trans 'The connection fails' %}"#} }) }); diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index e002c1e2aab622b09e691c200aff0d5c6a8d983f..233d13246a019e16a6731f51c2b8631aecb551a3 100644 GIT binary patch delta 19824 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 delta 19878 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\n" "Language-Team: Jumpserver team\n" @@ -96,7 +96,7 @@ msgstr "运行参数" #: terminal/templates/terminal/session_list.html:28 #: terminal/templates/terminal/session_list.html:72 #: xpack/plugins/change_auth_plan/forms.py:73 -#: xpack/plugins/change_auth_plan/models.py:419 +#: xpack/plugins/change_auth_plan/models.py:412 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13 @@ -152,7 +152,7 @@ msgstr "资产" #: users/templates/users/user_profile.html:51 #: users/templates/users/user_pubkey_update.html:57 #: xpack/plugins/change_auth_plan/forms.py:56 -#: xpack/plugins/change_auth_plan/models.py:64 +#: xpack/plugins/change_auth_plan/models.py:63 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12 #: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144 @@ -199,7 +199,7 @@ msgstr "参数" #: perms/templates/perms/remote_app_permission_detail.html:90 #: users/models/user.py:414 users/serializers/group.py:32 #: users/templates/users/user_detail.html:111 -#: xpack/plugins/change_auth_plan/models.py:109 +#: xpack/plugins/change_auth_plan/models.py:108 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113 #: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179 #: xpack/plugins/gathered_user/models.py:46 @@ -262,7 +262,7 @@ msgstr "创建日期" #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:37 #: users/templates/users/user_profile.html:138 -#: xpack/plugins/change_auth_plan/models.py:105 +#: xpack/plugins/change_auth_plan/models.py:104 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19 #: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173 @@ -602,7 +602,7 @@ msgstr "端口" #: assets/templates/assets/asset_detail.html:196 #: assets/templates/assets/system_user_assets.html:83 #: perms/models/asset_permission.py:81 -#: xpack/plugins/change_auth_plan/models.py:75 +#: xpack/plugins/change_auth_plan/models.py:74 #: xpack/plugins/gathered_user/models.py:31 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:17 msgid "Nodes" @@ -679,9 +679,9 @@ msgstr "选择资产" msgid "Content should not be contain: {}" msgstr "内容不能包含: {}" -#: assets/forms/domain.py:55 +#: assets/forms/domain.py:55 assets/models/domain.py:67 msgid "Password should not contain special characters" -msgstr "不能包含特殊字符" +msgstr "密码不能包含特殊字符" #: assets/forms/domain.py:74 msgid "SSH gateway support proxy SSH,RDP,VNC" @@ -709,8 +709,8 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" #: users/templates/users/user_list.html:36 #: users/templates/users/user_profile.html:47 #: xpack/plugins/change_auth_plan/forms.py:58 -#: xpack/plugins/change_auth_plan/models.py:66 -#: xpack/plugins/change_auth_plan/models.py:415 +#: xpack/plugins/change_auth_plan/models.py:65 +#: xpack/plugins/change_auth_plan/models.py:408 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12 @@ -737,8 +737,8 @@ msgstr "密码或密钥密码" #: users/templates/users/user_profile_update.html:41 #: users/templates/users/user_pubkey_update.html:41 #: users/templates/users/user_update.html:20 -#: xpack/plugins/change_auth_plan/models.py:96 -#: xpack/plugins/change_auth_plan/models.py:264 +#: xpack/plugins/change_auth_plan/models.py:95 +#: xpack/plugins/change_auth_plan/models.py:263 msgid "Password" msgstr "密码" @@ -931,13 +931,13 @@ msgstr "版本" msgid "AuthBook" msgstr "" -#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:100 -#: xpack/plugins/change_auth_plan/models.py:271 +#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:99 +#: xpack/plugins/change_auth_plan/models.py:270 msgid "SSH private key" msgstr "ssh密钥" -#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:103 -#: xpack/plugins/change_auth_plan/models.py:267 +#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:102 +#: xpack/plugins/change_auth_plan/models.py:266 msgid "SSH public key" msgstr "ssh公钥" @@ -1177,7 +1177,7 @@ msgstr "手动登录" #: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73 #: assets/views/system_user.py:29 assets/views/system_user.py:46 #: assets/views/system_user.py:63 assets/views/system_user.py:79 -#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:71 +#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:70 msgid "Assets" msgstr "资产管理" @@ -1322,7 +1322,7 @@ msgstr "测试资产可连接性: {}" #: assets/tasks/asset_user_connectivity.py:27 #: assets/tasks/push_system_user.py:130 -#: xpack/plugins/change_auth_plan/models.py:528 +#: xpack/plugins/change_auth_plan/models.py:521 msgid "The asset {} system platform {} does not support run Ansible tasks" msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务" @@ -1985,10 +1985,6 @@ msgstr "测试连接" msgid "Can be connected" msgstr "可连接" -#: assets/templates/assets/domain_gateway_list.html:142 -msgid "The connection fails" -msgstr "连接失败" - #: assets/templates/assets/domain_list.html:9 msgid "" "The domain function is added to address the fact that some environments " @@ -2272,7 +2268,7 @@ msgid "MFA" msgstr "MFA" #: audits/models.py:87 audits/templates/audits/login_log_list.html:63 -#: xpack/plugins/change_auth_plan/models.py:423 +#: xpack/plugins/change_auth_plan/models.py:416 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15 #: xpack/plugins/cloud/models.py:278 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69 @@ -2298,8 +2294,8 @@ msgstr "登录日期" #: perms/templates/perms/asset_permission_detail.html:86 #: perms/templates/perms/remote_app_permission_detail.html:78 #: terminal/models.py:167 terminal/templates/terminal/session_list.html:34 -#: xpack/plugins/change_auth_plan/models.py:250 -#: xpack/plugins/change_auth_plan/models.py:426 +#: xpack/plugins/change_auth_plan/models.py:249 +#: xpack/plugins/change_auth_plan/models.py:419 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17 #: xpack/plugins/gathered_user/models.py:143 @@ -2846,8 +2842,8 @@ msgstr "完成时间" #: ops/models/adhoc.py:358 ops/templates/ops/adhoc_history.html:57 #: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:17 -#: xpack/plugins/change_auth_plan/models.py:253 -#: xpack/plugins/change_auth_plan/models.py:429 +#: xpack/plugins/change_auth_plan/models.py:252 +#: xpack/plugins/change_auth_plan/models.py:422 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16 #: xpack/plugins/gathered_user/models.py:146 @@ -3950,7 +3946,7 @@ msgstr "删除失败" msgid "Are you sure about deleting it?" msgstr "您确定删除吗?" -#: settings/utils/ldap.py:130 +#: settings/utils/ldap.py:128 msgid "Search no entry matched in ou {}" msgstr "在ou:{}中没有匹配条目" @@ -4603,7 +4599,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户" msgid "Set password" msgstr "设置密码" -#: users/forms.py:152 xpack/plugins/change_auth_plan/models.py:89 +#: users/forms.py:152 xpack/plugins/change_auth_plan/models.py:88 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57 @@ -5533,8 +5529,8 @@ msgstr "" "具)
    注意: 如果同时设置了定期执行和周期执行,优先使用定期执行" #: xpack/plugins/change_auth_plan/meta.py:9 -#: xpack/plugins/change_auth_plan/models.py:117 -#: xpack/plugins/change_auth_plan/models.py:257 +#: xpack/plugins/change_auth_plan/models.py:116 +#: xpack/plugins/change_auth_plan/models.py:256 #: xpack/plugins/change_auth_plan/views.py:33 #: xpack/plugins/change_auth_plan/views.py:50 #: xpack/plugins/change_auth_plan/views.py:74 @@ -5545,20 +5541,20 @@ msgstr "" msgid "Change auth plan" msgstr "改密计划" -#: xpack/plugins/change_auth_plan/models.py:58 +#: xpack/plugins/change_auth_plan/models.py:57 msgid "Custom password" msgstr "自定义密码" -#: xpack/plugins/change_auth_plan/models.py:59 +#: xpack/plugins/change_auth_plan/models.py:58 msgid "All assets use the same random password" msgstr "所有资产使用相同的随机密码" -#: xpack/plugins/change_auth_plan/models.py:60 +#: xpack/plugins/change_auth_plan/models.py:59 msgid "All assets use different random password" msgstr "所有资产使用不同的随机密码" -#: xpack/plugins/change_auth_plan/models.py:79 -#: xpack/plugins/change_auth_plan/models.py:148 +#: xpack/plugins/change_auth_plan/models.py:78 +#: xpack/plugins/change_auth_plan/models.py:147 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100 #: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91 @@ -5567,8 +5563,8 @@ msgstr "所有资产使用不同的随机密码" msgid "Cycle perform" msgstr "周期执行" -#: xpack/plugins/change_auth_plan/models.py:84 -#: xpack/plugins/change_auth_plan/models.py:146 +#: xpack/plugins/change_auth_plan/models.py:83 +#: xpack/plugins/change_auth_plan/models.py:145 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92 #: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83 @@ -5577,37 +5573,37 @@ msgstr "周期执行" msgid "Regularly perform" msgstr "定期执行" -#: xpack/plugins/change_auth_plan/models.py:93 +#: xpack/plugins/change_auth_plan/models.py:92 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74 msgid "Password rules" msgstr "密码规则" -#: xpack/plugins/change_auth_plan/models.py:213 +#: xpack/plugins/change_auth_plan/models.py:212 msgid "* For security, do not change {} user's password" msgstr "* 为了安全,禁止更改 {} 用户的密码" -#: xpack/plugins/change_auth_plan/models.py:217 +#: xpack/plugins/change_auth_plan/models.py:216 msgid "Assets is empty, please add the asset" msgstr "资产为空,请添加资产" -#: xpack/plugins/change_auth_plan/models.py:261 +#: xpack/plugins/change_auth_plan/models.py:260 msgid "Change auth plan snapshot" msgstr "改密计划快照" -#: xpack/plugins/change_auth_plan/models.py:276 -#: xpack/plugins/change_auth_plan/models.py:433 +#: xpack/plugins/change_auth_plan/models.py:275 +#: xpack/plugins/change_auth_plan/models.py:426 msgid "Change auth plan execution" msgstr "改密计划执行" -#: xpack/plugins/change_auth_plan/models.py:442 +#: xpack/plugins/change_auth_plan/models.py:435 msgid "Change auth plan execution subtask" msgstr "改密计划执行子任务" -#: xpack/plugins/change_auth_plan/models.py:460 +#: xpack/plugins/change_auth_plan/models.py:453 msgid "Authentication failed" msgstr "认证失败" -#: xpack/plugins/change_auth_plan/models.py:462 +#: xpack/plugins/change_auth_plan/models.py:455 msgid "Connection timeout" msgstr "连接超时" @@ -6217,6 +6213,14 @@ msgstr "密码匣子" msgid "vault create" msgstr "创建" +#, fuzzy +#~| msgid "Password should not contain special characters" +#~ msgid "Password has special char" +#~ msgstr "不能包含特殊字符" + +#~ msgid "The connection fails" +#~ msgstr "连接失败" + #~ msgid "Recipient" #~ msgstr "收件人" From 6bfb026e47f5c9329c6e25c4688c3c89ae8c34ea Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Wed, 13 Nov 2019 16:45:38 +0800 Subject: [PATCH 17/17] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=20LDAP=20=E5=90=8C=E6=AD=A5=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=9C=A8=E4=BB=BB=E5=8A=A1=E5=A4=96=E9=83=A8=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?running=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/api.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/settings/api.py b/apps/settings/api.py index f55b37749..1fce9a44d 100644 --- a/apps/settings/api.py +++ b/apps/settings/api.py @@ -186,6 +186,8 @@ class LDAPUserListApi(generics.ListAPIView): sync_util = LDAPSyncUtil() # 还没有同步任务 if sync_util.task_no_start: + # 任务外部设置 task running 状态 + sync_util.set_task_status(sync_util.TASK_STATUS_IS_RUNNING) task = sync_ldap_user_task.delay() data = {'msg': 'Cache no data, sync task {} started.'.format(task.id)} return Response(data=data, status=409)