diff --git a/apps/applications/templates/applications/remote_app_list.html b/apps/applications/templates/applications/remote_app_list.html index 3dbe4f8eb..a308bf911 100644 --- a/apps/applications/templates/applications/remote_app_list.html +++ b/apps/applications/templates/applications/remote_app_list.html @@ -1,10 +1,8 @@ {% extends '_base_list.html' %} {% load i18n static %} {% block help_message %} -
{% trans 'Before using this feature, make sure that the application loader has been uploaded to the application server and successfully published as a RemoteApp application' %} {% trans 'Download application loader' %} -
{% endblock %} {% block table_search %}{% endblock %} {% block table_container %} @@ -84,4 +82,4 @@ $(document).ready(function(){ }, 3000); }); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/apps/assets/migrations/0043_auto_20191114_1111.py b/apps/assets/migrations/0043_auto_20191114_1111.py new file mode 100644 index 000000000..a07dee6bb --- /dev/null +++ b/apps/assets/migrations/0043_auto_20191114_1111.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.5 on 2019-11-14 03:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0042_favoriteasset'), + ] + + operations = [ + migrations.AddField( + model_name='gathereduser', + name='date_last_login', + field=models.DateTimeField(null=True, verbose_name='Date last login'), + ), + migrations.AddField( + model_name='gathereduser', + name='ip_last_login', + field=models.CharField(default='', max_length=39, verbose_name='IP last login'), + ), + ] diff --git a/apps/assets/models/gathered_user.py b/apps/assets/models/gathered_user.py index 282f9293a..d00021c56 100644 --- a/apps/assets/models/gathered_user.py +++ b/apps/assets/models/gathered_user.py @@ -12,13 +12,12 @@ __all__ = ['GatheredUser'] class GatheredUser(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_("Asset")) - username = models.CharField(max_length=32, blank=True, db_index=True, - verbose_name=_('Username')) + username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username')) present = models.BooleanField(default=True, verbose_name=_("Present")) - date_created = models.DateTimeField(auto_now_add=True, - verbose_name=_("Date created")) - date_updated = models.DateTimeField(auto_now=True, - verbose_name=_("Date updated")) + date_last_login = models.DateTimeField(null=True, verbose_name=_("Date last login")) + ip_last_login = models.CharField(max_length=39, default='', verbose_name=_("IP last login")) + date_created = models.DateTimeField(auto_now_add=True, verbose_name=_("Date created")) + date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated")) @property def hostname(self): diff --git a/apps/assets/serializers/gathered_user.py b/apps/assets/serializers/gathered_user.py index 956c19c6b..c055e25bd 100644 --- a/apps/assets/serializers/gathered_user.py +++ b/apps/assets/serializers/gathered_user.py @@ -12,6 +12,7 @@ class GatheredUserSerializer(OrgResourceModelSerializerMixin): model = GatheredUser fields = [ 'id', 'asset', 'hostname', 'ip', 'username', + 'date_last_login', 'ip_last_login', 'present', 'date_created', 'date_updated' ] read_only_fields = fields diff --git a/apps/assets/tasks/const.py b/apps/assets/tasks/const.py index 61a9580ed..5b7db13cd 100644 --- a/apps/assets/tasks/const.py +++ b/apps/assets/tasks/const.py @@ -94,6 +94,13 @@ GATHER_ASSET_USERS_TASKS = [ "args": "database=passwd" }, }, + { + "name": "get last login", + "action": { + "module": "shell", + "args": "users=$(getent passwd | grep -v 'nologin' | grep -v 'shudown' | awk -F: '{ print $1 }');for i in $users;do last -F $i -1 | head -1 | grep -v '^$' | awk '{ print $1\"@\"$3\"@\"$5,$6,$7,$8 }';done" + } + } ] GATHER_ASSET_USERS_TASKS_WINDOWS = [ diff --git a/apps/assets/tasks/gather_asset_users.py b/apps/assets/tasks/gather_asset_users.py index efeecc25e..7dfe0fb01 100644 --- a/apps/assets/tasks/gather_asset_users.py +++ b/apps/assets/tasks/gather_asset_users.py @@ -2,9 +2,10 @@ import re from collections import defaultdict -from celery import shared_task +from celery import shared_task from django.utils.translation import ugettext as _ +from django.utils import timezone from orgs.utils import tmp_to_org from common.utils import get_logger @@ -19,19 +20,25 @@ ignore_login_shell = re.compile(r'nologin$|sync$|shutdown$|halt$') def parse_linux_result_to_users(result): - task_result = {} - for task_name, raw in result.items(): - res = raw.get('ansible_facts', {}).get('getent_passwd') - if res: - task_result = res - break - if not task_result or not isinstance(task_result, dict): - return [] - users = [] - for username, attr in task_result.items(): + users = defaultdict(dict) + users_result = result.get('gather host users', {})\ + .get('ansible_facts', {})\ + .get('getent_passwd') + if not isinstance(users_result, dict): + users_result = {} + for username, attr in users_result.items(): if ignore_login_shell.search(attr[-1]): continue - users.append(username) + users[username] = {} + last_login_result = result.get('get last login', {}).get('stdout_lines', []) + for line in last_login_result: + data = line.split('@') + if len(data) != 3: + continue + username, ip, dt = data + dt += ' +0800' + date = timezone.datetime.strptime(dt, '%b %d %H:%M:%S %Y %z') + users[username] = {"ip": ip, "date": date} return users @@ -45,7 +52,7 @@ def parse_windows_result_to_users(result): if not task_result: return [] - users = [] + users = {} for i in range(4): task_result.pop(0) @@ -55,7 +62,7 @@ def parse_windows_result_to_users(result): for line in task_result: user = space.split(line) if user[0]: - users.append(user[0]) + users[user[0]] = {} return users @@ -82,8 +89,12 @@ def add_asset_users(assets, results): with tmp_to_org(asset.org_id): GatheredUser.objects.filter(asset=asset, present=True)\ .update(present=False) - for username in users: + for username, data in users.items(): defaults = {'asset': asset, 'username': username, 'present': True} + if data.get("ip"): + defaults["ip_last_login"] = data["ip"] + if data.get("date"): + defaults["date_last_login"] = data["date"] GatheredUser.objects.update_or_create( defaults=defaults, asset=asset, username=username, ) diff --git a/apps/assets/templates/assets/_asset_group_bulk_update_modal.html b/apps/assets/templates/assets/_asset_group_bulk_update_modal.html index 61ac04fa6..7df6c4ede 100644 --- a/apps/assets/templates/assets/_asset_group_bulk_update_modal.html +++ b/apps/assets/templates/assets/_asset_group_bulk_update_modal.html @@ -31,11 +31,11 @@
- +
{% endblock %} -{% block modal_confirm_id %}btn_asset_group_bulk_update{% endblock %} \ No newline at end of file +{% block modal_confirm_id %}btn_asset_group_bulk_update{% endblock %} diff --git a/apps/assets/templates/assets/_asset_list_modal.html b/apps/assets/templates/assets/_asset_list_modal.html index 4c6eb7199..7c3e03f17 100644 --- a/apps/assets/templates/assets/_asset_list_modal.html +++ b/apps/assets/templates/assets/_asset_list_modal.html @@ -135,7 +135,8 @@ function initAssetModalTable() { ], lengthMenu: [[10, 25, 50], [10, 25, 50]], pageLength: 10, - select_style: assetModalOption.selectStyle + select_style: assetModalOption.selectStyle, + paging_numbers_length: 3 }; assetModalTable = jumpserver.initServerSideDataTable(options); if (assetModalOption.onModalTableDone) { diff --git a/apps/assets/templates/assets/_node_tree.html b/apps/assets/templates/assets/_node_tree.html index 803c29c13..fc7f4ae06 100644 --- a/apps/assets/templates/assets/_node_tree.html +++ b/apps/assets/templates/assets/_node_tree.html @@ -303,9 +303,24 @@ function defaultCallback(action) { return logging } +function toggle() { + if (show === 0) { + $("#split-left").hide(500, function () { + $("#split-right").attr("class", "col-lg-12"); + $("#toggle-icon").attr("class", "fa fa-angle-right fa-x"); + show = 1; + }); + } else { + $("#split-right").attr("class", "col-lg-9"); + $("#toggle-icon").attr("class", "fa fa-angle-left fa-x"); + $("#split-left").show(500); + show = 0; + } +} + $(document).ready(function () { - $('.treebox').css('height', window.innerHeight - 180); + $('.treebox').css('height', window.innerHeight - 60); }) .on('click', '.btn-show-current-asset', function(){ hideRMenu(); @@ -320,6 +335,9 @@ $(document).ready(function () { $('#show_current_asset').css('display', 'inline-block'); setCookie('show_current_asset', ''); location.reload(); +}).on('click', '.tree-toggle-btn', function (e) { + e.preventDefault(); + toggle(); }) diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html index 7a4b2a2dc..8063d6de6 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -1,10 +1,8 @@ {% extends '_base_list.html' %} {% load i18n static %} {% block help_message %} -
{% trans 'Admin users are asset (charged server) on the root, or have NOPASSWD: ALL sudo permissions users, '%} {% trans 'Jumpserver users of the system using the user to `push system user`, `get assets hardware information`, etc. '%} -
{% endblock %} {% block table_search %}
diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index fcc17c87d..d52532848 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -3,16 +3,16 @@ {% load i18n %} {% block help_message %} -
+{#
#} +{# #} {# 左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,右侧是属于该节点下的资产#} {% trans 'The left side is the asset tree, right click to create, delete, and change the tree node, authorization asset is also organized as a node, and the right side is the asset under that node' %} -
+{#
#} {% endblock %} {% block custom_head_css_js %} - -{# #} - +{# #} +{# #} @@ -69,30 +76,34 @@
-
+
{% csrf_token %} + {% if form.non_field_errors %}
- {% if block_login %} -

{% trans 'Log in frequently and try again later' %}

-

{{ form.errors.password.as_text }}

- {% elif password_expired %} -

{% trans 'The user password has expired' %}

- {% elif form.errors %} - {% if 'captcha' in form.errors %} -

{% trans 'Captcha invalid' %}

- {% else %} -

{{ form.non_field_errors.as_text }}

- {% endif %} -

{{ form.errors.password.as_text }}

- {% endif %} +

{{ form.non_field_errors.as_text }}

+ {% elif form.errors.captcha %} +

{% trans 'Captcha invalid' %}

+ {% else %} +
+ {% endif %}
+ {% if form.errors.username %} +
+

{{ form.errors.username.as_text }}

+
+ {% endif %}
+ {% if form.errors.password %} +
+

{{ form.errors.password.as_text }}

+
+ {% endif %}
{{ form.captcha }} @@ -116,4 +127,4 @@
- \ No newline at end of file + diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py index 68dd8eeaa..da59711c4 100644 --- a/apps/authentication/urls/api_urls.py +++ b/apps/authentication/urls/api_urls.py @@ -1,29 +1,25 @@ # coding:utf-8 # - -from __future__ import absolute_import - from django.urls import path from rest_framework.routers import DefaultRouter from .. import api +app_name = 'authentication' router = DefaultRouter() router.register('access-keys', api.AccessKeyViewSet, 'access-key') -app_name = 'authentication' - - urlpatterns = [ # path('token/', api.UserToken.as_view(), name='user-token'), - path('auth/', api.UserAuthApi.as_view(), name='user-auth'), + path('auth/', api.TokenCreateApi.as_view(), name='user-auth'), path('tokens/', api.TokenCreateApi.as_view(), name='auth-token'), path('mfa/challenge/', api.MFAChallengeApi.as_view(), name='mfa-challenge'), path('connection-token/', api.UserConnectionTokenApi.as_view(), name='connection-token'), - path('otp/auth/', api.UserOtpAuthApi.as_view(), name='user-otp-auth'), path('otp/verify/', api.UserOtpVerifyApi.as_view(), name='user-otp-verify'), + path('login-confirm-ticket/status/', api.TicketStatusApi.as_view(), name='login-confirm-ticket-status'), + path('login-confirm-settings//', api.LoginConfirmSettingUpdateApi.as_view(), name='login-confirm-setting-update') ] urlpatterns += router.urls diff --git a/apps/authentication/urls/view_urls.py b/apps/authentication/urls/view_urls.py index 8602daca5..64d01ae34 100644 --- a/apps/authentication/urls/view_urls.py +++ b/apps/authentication/urls/view_urls.py @@ -16,5 +16,7 @@ urlpatterns = [ # login path('login/', views.UserLoginView.as_view(), name='login'), path('login/otp/', views.UserLoginOtpView.as_view(), name='login-otp'), + path('login/wait-confirm/', views.UserLoginWaitConfirmView.as_view(), name='login-wait-confirm'), + path('login/guard/', views.UserLoginGuardView.as_view(), name='login-guard'), path('logout/', views.UserLogoutView.as_view(), name='logout'), ] diff --git a/apps/authentication/utils.py b/apps/authentication/utils.py index 70c7e52fa..eb1649885 100644 --- a/apps/authentication/utils.py +++ b/apps/authentication/utils.py @@ -3,22 +3,11 @@ from django.utils.translation import ugettext as _ from django.contrib.auth import authenticate -from common.utils import get_ip_city, get_object_or_none, validate_ip +from common.utils import ( + get_ip_city, get_object_or_none, validate_ip +) from users.models import User -from . import const - - -def write_login_log(*args, **kwargs): - from audits.models import UserLoginLog - default_city = _("Unknown") - ip = kwargs.get('ip') or '' - if not (ip and validate_ip(ip)): - ip = ip[:15] - city = default_city - else: - city = get_ip_city(ip) or default_city - kwargs.update({'ip': ip, 'city': city}) - UserLoginLog.objects.create(**kwargs) +from . import errors def check_user_valid(**kwargs): @@ -26,6 +15,7 @@ def check_user_valid(**kwargs): public_key = kwargs.pop('public_key', None) email = kwargs.pop('email', None) username = kwargs.pop('username', None) + request = kwargs.get('request') if username: user = get_object_or_none(User, username=username) @@ -35,21 +25,17 @@ def check_user_valid(**kwargs): user = None if user is None: - return None, const.user_not_exist - elif not user.is_valid: - return None, const.user_invalid + return None, errors.reason_user_not_exist + elif user.is_expired: + return None, errors.reason_user_inactive + elif not user.is_active: + return None, errors.reason_user_inactive elif user.password_has_expired: - return None, const.password_expired + return None, errors.reason_password_expired - if password and authenticate(username=username, password=password): - return user, '' - - if public_key and user.public_key: - public_key_saved = user.public_key.split() - if len(public_key_saved) == 1: - if public_key == public_key_saved[0]: - return user, '' - elif len(public_key_saved) > 1: - if public_key == public_key_saved[1]: - return user, '' - return None, const.password_failed + if password or public_key: + user = authenticate(request, username=username, + password=password, public_key=public_key) + if user: + return user, '' + return None, errors.reason_password_failed diff --git a/apps/authentication/views/__init__.py b/apps/authentication/views/__init__.py index 5e7732adc..5a1a40f7a 100644 --- a/apps/authentication/views/__init__.py +++ b/apps/authentication/views/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- # - from .login import * +from .mfa import * diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 2a7098ae7..e24314717 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import os +import datetime from django.core.cache import cache from django.contrib.auth import login as auth_login, logout as auth_logout from django.http import HttpResponse @@ -12,36 +13,32 @@ from django.utils.translation import ugettext as _ from django.views.decorators.cache import never_cache from django.views.decorators.csrf import csrf_protect from django.views.decorators.debug import sensitive_post_parameters -from django.views.generic.base import TemplateView +from django.views.generic.base import TemplateView, RedirectView from django.views.generic.edit import FormView from django.conf import settings +from django.urls import reverse_lazy -from common.utils import get_request_ip -from users.models import User -from audits.models import UserLoginLog as LoginLog +from common.utils import get_request_ip, get_object_or_none from users.utils import ( - check_otp_code, is_block_login, clean_failed_count, get_user_or_tmp_user, - set_tmp_user_to_cache, increase_login_failed_count, - redirect_user_first_login_or_index, + redirect_user_first_login_or_index ) -from ..signals import post_auth_success, post_auth_failed -from .. import forms -from .. import const +from .. import forms, mixins, errors __all__ = [ - 'UserLoginView', 'UserLoginOtpView', 'UserLogoutView', + 'UserLoginView', 'UserLogoutView', + 'UserLoginGuardView', 'UserLoginWaitConfirmView', ] @method_decorator(sensitive_post_parameters(), name='dispatch') @method_decorator(csrf_protect, name='dispatch') @method_decorator(never_cache, name='dispatch') -class UserLoginView(FormView): +class UserLoginView(mixins.AuthMixin, FormView): form_class = forms.UserLoginForm form_class_captcha = forms.UserLoginCaptchaForm - redirect_field_name = 'next' key_prefix_captcha = "_LOGIN_INVALID_{}" + redirect_field_name = 'next' def get_template_names(self): template_name = 'authentication/login.html' @@ -52,7 +49,7 @@ class UserLoginView(FormView): if not License.has_valid_license(): return template_name - template_name = 'authentication/new_login.html' + template_name = 'authentication/xpack_login.html' return template_name def get(self, request, *args, **kwargs): @@ -68,48 +65,27 @@ class UserLoginView(FormView): request.session.set_test_cookie() return super().get(request, *args, **kwargs) - def post(self, request, *args, **kwargs): - # limit login authentication - ip = get_request_ip(request) - username = self.request.POST.get('username') - if is_block_login(username, ip): - return self.render_to_response(self.get_context_data(block_login=True)) - return super().post(request, *args, **kwargs) - def form_valid(self, form): if not self.request.session.test_cookie_worked(): return HttpResponse(_("Please enable cookies and try again.")) - user = form.get_user() - # user password expired - if user.password_has_expired: - reason = const.password_expired - self.send_auth_signal(success=False, username=user.username, reason=reason) - return self.render_to_response(self.get_context_data(password_expired=True)) + try: + self.check_user_auth() + except errors.AuthFailedError as e: + form.add_error(None, e.msg) + ip = self.get_request_ip() + cache.set(self.key_prefix_captcha.format(ip), 1, 3600) + new_form = self.form_class_captcha(data=form.data) + new_form._errors = form.errors + context = self.get_context_data(form=new_form) + return self.render_to_response(context) + return self.redirect_to_guard_view() - set_tmp_user_to_cache(self.request, user) - username = form.cleaned_data.get('username') - ip = get_request_ip(self.request) - # 登陆成功,清除缓存计数 - clean_failed_count(username, ip) - return redirect(self.get_success_url()) - - def form_invalid(self, form): - # write login failed log - username = form.cleaned_data.get('username') - exist = User.objects.filter(username=username).first() - reason = const.password_failed if exist else const.user_not_exist - # limit user login failed count - ip = get_request_ip(self.request) - increase_login_failed_count(username, ip) - form.add_limit_login_error(username, ip) - # show captcha - cache.set(self.key_prefix_captcha.format(ip), 1, 3600) - self.send_auth_signal(success=False, username=username, reason=reason) - - old_form = form - form = self.form_class_captcha(data=form.data) - form._errors = old_form.errors - return super().form_invalid(form) + def redirect_to_guard_view(self): + guard_url = reverse('authentication:login-guard') + args = self.request.META.get('QUERY_STRING', '') + if args: + guard_url = "%s?%s" % (guard_url, args) + return redirect(guard_url) def get_form_class(self): ip = get_request_ip(self.request) @@ -118,21 +94,6 @@ class UserLoginView(FormView): else: return self.form_class - def get_success_url(self): - user = get_user_or_tmp_user(self.request) - - if user.otp_enabled and user.otp_secret_key: - # 1,2,mfa_setting & T - return reverse('authentication:login-otp') - elif user.otp_enabled and not user.otp_secret_key: - # 1,2,mfa_setting & F - return reverse('users:user-otp-enable-authentication') - elif not user.otp_enabled: - # 0 & T,F - auth_login(self.request, user) - self.send_auth_signal(success=True, user=user) - return redirect_user_first_login_or_index(self.request, self.redirect_field_name) - def get_context_data(self, **kwargs): context = { 'demo_mode': os.environ.get("DEMO_MODE"), @@ -141,51 +102,70 @@ class UserLoginView(FormView): kwargs.update(context) return super().get_context_data(**kwargs) - def send_auth_signal(self, success=True, user=None, username='', reason=''): - if success: - post_auth_success.send(sender=self.__class__, user=user, request=self.request) - else: - post_auth_failed.send( - sender=self.__class__, username=username, - request=self.request, reason=reason - ) - -class UserLoginOtpView(FormView): - template_name = 'authentication/login_otp.html' - form_class = forms.UserCheckOtpCodeForm +class UserLoginGuardView(mixins.AuthMixin, RedirectView): redirect_field_name = 'next' + login_url = reverse_lazy('authentication:login') + login_otp_url = reverse_lazy('authentication:login-otp') + login_confirm_url = reverse_lazy('authentication:login-wait-confirm') - def form_valid(self, form): - user = get_user_or_tmp_user(self.request) - otp_code = form.cleaned_data.get('otp_code') - otp_secret_key = user.otp_secret_key + def format_redirect_url(self, url): + args = self.request.META.get('QUERY_STRING', '') + if args and self.query_string: + url = "%s?%s" % (url, args) + return url - if check_otp_code(otp_secret_key, otp_code): + def get_redirect_url(self, *args, **kwargs): + try: + user = self.check_user_auth_if_need() + self.check_user_mfa_if_need(user) + self.check_user_login_confirm_if_need(user) + except errors.CredentialError: + return self.format_redirect_url(self.login_url) + except errors.MFARequiredError: + return self.format_redirect_url(self.login_otp_url) + except errors.LoginConfirmBaseError: + return self.format_redirect_url(self.login_confirm_url) + else: auth_login(self.request, user) self.send_auth_signal(success=True, user=user) - return redirect(self.get_success_url()) - else: - self.send_auth_signal( - success=False, username=user.username, - reason=const.mfa_failed + self.clear_auth_mark() + # 启用但是没有设置otp, 排除radius + if user.mfa_enabled_but_not_set(): + # 1,2,mfa_setting & F + return reverse('users:user-otp-enable-authentication') + url = redirect_user_first_login_or_index( + self.request, self.redirect_field_name ) - form.add_error( - 'otp_code', _('MFA code invalid, or ntp sync server time') - ) - return super().form_invalid(form) + return url - def get_success_url(self): - return redirect_user_first_login_or_index(self.request, self.redirect_field_name) - def send_auth_signal(self, success=True, user=None, username='', reason=''): - if success: - post_auth_success.send(sender=self.__class__, user=user, request=self.request) +class UserLoginWaitConfirmView(TemplateView): + template_name = 'authentication/login_wait_confirm.html' + + def get_context_data(self, **kwargs): + from tickets.models import Ticket + ticket_id = self.request.session.get("auth_ticket_id") + if not ticket_id: + ticket = None else: - post_auth_failed.send( - sender=self.__class__, username=username, - request=self.request, reason=reason - ) + ticket = get_object_or_none(Ticket, pk=ticket_id) + context = super().get_context_data(**kwargs) + if ticket: + timestamp_created = datetime.datetime.timestamp(ticket.date_created) + ticket_detail_url = reverse('tickets:ticket-detail', kwargs={'pk': ticket_id}) + msg = _("""Wait for {} confirm, You also can copy link to her/him
+ Don't close this page""").format(ticket.assignees_display) + else: + timestamp_created = 0 + ticket_detail_url = '' + msg = _("No ticket found") + context.update({ + "msg": msg, + "timestamp": timestamp_created, + "ticket_detail_url": ticket_detail_url + }) + return context @method_decorator(never_cache, name='dispatch') diff --git a/apps/authentication/views/mfa.py b/apps/authentication/views/mfa.py new file mode 100644 index 000000000..57d6751da --- /dev/null +++ b/apps/authentication/views/mfa.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# + +from __future__ import unicode_literals +from django.views.generic.edit import FormView +from .. import forms, errors, mixins +from .utils import redirect_to_guard_view + +__all__ = ['UserLoginOtpView'] + + +class UserLoginOtpView(mixins.AuthMixin, FormView): + template_name = 'authentication/login_otp.html' + form_class = forms.UserCheckOtpCodeForm + redirect_field_name = 'next' + + def form_valid(self, form): + otp_code = form.cleaned_data.get('otp_code') + try: + self.check_user_mfa(otp_code) + return redirect_to_guard_view() + except errors.MFAFailedError as e: + form.add_error('otp_code', e.msg) + return super().form_invalid(form) + diff --git a/apps/authentication/views/utils.py b/apps/authentication/views/utils.py new file mode 100644 index 000000000..182d7390b --- /dev/null +++ b/apps/authentication/views/utils.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# +from django.shortcuts import reverse, redirect + + +def redirect_to_guard_view(): + continue_url = reverse('authentication:login-guard') + return redirect(continue_url) diff --git a/apps/common/mixins/api.py b/apps/common/mixins/api.py index 6b1e8a893..1328b4c96 100644 --- a/apps/common/mixins/api.py +++ b/apps/common/mixins/api.py @@ -25,6 +25,15 @@ class IDSpmFilterMixin: return backends +class SerializerMixin: + def get_serializer_class(self): + if self.request.method.lower() == 'get' and\ + self.request.query_params.get('draw') \ + and hasattr(self, 'serializer_display_class'): + return self.serializer_display_class + return super().get_serializer_class() + + class ExtraFilterFieldsMixin: default_added_filters = [CustomFilter, IDSpmFilter] filter_backends = api_settings.DEFAULT_FILTER_BACKENDS @@ -44,5 +53,5 @@ class ExtraFilterFieldsMixin: return queryset -class CommonApiMixin(ExtraFilterFieldsMixin): +class CommonApiMixin(SerializerMixin, ExtraFilterFieldsMixin): pass diff --git a/apps/common/mixins/models.py b/apps/common/mixins/models.py index df3d899e0..e373a6b08 100644 --- a/apps/common/mixins/models.py +++ b/apps/common/mixins/models.py @@ -53,3 +53,15 @@ class CommonModelMixin(models.Model): class Meta: abstract = True + + +class DebugQueryManager(models.Manager): + def get_queryset(self): + import traceback + lines = traceback.format_stack() + print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>") + for line in lines[-10:-1]: + print(line) + print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<") + queryset = super().get_queryset() + return queryset diff --git a/apps/common/utils/common.py b/apps/common/utils/common.py index 2f4ce784c..29f3b471c 100644 --- a/apps/common/utils/common.py +++ b/apps/common/utils/common.py @@ -153,6 +153,14 @@ def get_request_ip(request): return login_ip +def get_request_ip_or_data(request): + ip = '' + if hasattr(request, 'data'): + ip = request.data.get('remote_addr', '') + ip = ip or get_request_ip(request) + return ip + + def validate_ip(ip): try: ipaddress.ip_address(ip) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index f7f35c215..9a8a1c5f9 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -375,6 +375,7 @@ defaults = { 'RADIUS_SERVER': 'localhost', 'RADIUS_PORT': 1812, 'RADIUS_SECRET': '', + 'RADIUS_ENCRYPT_PASSWORD': True, 'AUTH_LDAP_SEARCH_PAGED_SIZE': 1000, 'AUTH_LDAP_SYNC_IS_PERIODIC': False, 'AUTH_LDAP_SYNC_INTERVAL': None, @@ -394,8 +395,11 @@ defaults = { 'WINDOWS_SSH_DEFAULT_SHELL': 'cmd', 'FLOWER_URL': "127.0.0.1:5555", 'DEFAULT_ORG_SHOW_ALL_USERS': True, - 'PERIOD_TASK_ENABLED': True, + 'PERIOD_TASK_ENABLE': True, + 'FORCE_SCRIPT_NAME': '', + 'LOGIN_CONFIRM_ENABLE': False, 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False, + 'OTP_IN_RADIUS': False, } diff --git a/apps/jumpserver/const.py b/apps/jumpserver/const.py index b932d6bb0..48293bdac 100644 --- a/apps/jumpserver/const.py +++ b/apps/jumpserver/const.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- # -VERSION = '1.5.4' +VERSION = '1.5.5' diff --git a/apps/jumpserver/context_processor.py b/apps/jumpserver/context_processor.py index 0bd5186dd..e940f06f6 100644 --- a/apps/jumpserver/context_processor.py +++ b/apps/jumpserver/context_processor.py @@ -18,7 +18,9 @@ def jumpserver_processor(request): 'COPYRIGHT': 'FIT2CLOUD 飞致云' + ' © 2014-2019', 'SECURITY_COMMAND_EXECUTION': settings.SECURITY_COMMAND_EXECUTION, 'SECURITY_MFA_VERIFY_TTL': settings.SECURITY_MFA_VERIFY_TTL, + 'FORCE_SCRIPT_NAME': settings.FORCE_SCRIPT_NAME, 'SECURITY_VIEW_AUTH_NEED_MFA': settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA, + 'LOGIN_CONFIRM_ENABLE': settings.CONFIG.LOGIN_CONFIRM_ENABLE, } return context diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index fe4f2fc84..5c5a741b3 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -71,6 +71,7 @@ INSTALLED_APPS = [ 'audits.apps.AuditsConfig', 'authentication.apps.AuthenticationConfig', # authentication 'applications.apps.ApplicationsConfig', + 'tickets.apps.TicketsConfig', 'rest_framework', 'rest_framework_swagger', 'drf_yasg', @@ -331,7 +332,7 @@ LOCALE_PATHS = [ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = '{}/static/'.format(CONFIG.FORCE_SCRIPT_NAME) STATIC_ROOT = os.path.join(PROJECT_DIR, "data", "static") STATIC_DIR = os.path.join(BASE_DIR, "static") @@ -410,6 +411,7 @@ REST_FRAMEWORK = { AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.ModelBackend', + 'authentication.backends.pubkey.PublicKeyAuthBackend', ] # Custom User Auth model @@ -655,3 +657,4 @@ CHANNEL_LAYERS = { # Enable internal period task PERIOD_TASK_ENABLED = CONFIG.PERIOD_TASK_ENABLED +FORCE_SCRIPT_NAME = CONFIG.FORCE_SCRIPT_NAME diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index 82773a51a..4d5e96671 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -13,22 +13,23 @@ from .celery_flower import celery_flower_view from .swagger import get_swagger_view api_v1 = [ - path('users/', include('users.urls.api_urls', namespace='api-users')), - path('assets/', include('assets.urls.api_urls', namespace='api-assets')), - path('perms/', include('perms.urls.api_urls', namespace='api-perms')), - path('terminal/', include('terminal.urls.api_urls', namespace='api-terminal')), - path('ops/', include('ops.urls.api_urls', namespace='api-ops')), - path('audits/', include('audits.urls.api_urls', namespace='api-audits')), - path('orgs/', include('orgs.urls.api_urls', namespace='api-orgs')), - path('settings/', include('settings.urls.api_urls', namespace='api-settings')), - path('authentication/', include('authentication.urls.api_urls', namespace='api-auth')), - path('common/', include('common.urls.api_urls', namespace='api-common')), - path('applications/', include('applications.urls.api_urls', namespace='api-applications')), + path('users/', include('users.urls.api_urls', namespace='api-users')), + path('assets/', include('assets.urls.api_urls', namespace='api-assets')), + path('perms/', include('perms.urls.api_urls', namespace='api-perms')), + path('terminal/', include('terminal.urls.api_urls', namespace='api-terminal')), + path('ops/', include('ops.urls.api_urls', namespace='api-ops')), + path('audits/', include('audits.urls.api_urls', namespace='api-audits')), + path('orgs/', include('orgs.urls.api_urls', namespace='api-orgs')), + path('settings/', include('settings.urls.api_urls', namespace='api-settings')), + path('authentication/', include('authentication.urls.api_urls', namespace='api-auth')), + path('common/', include('common.urls.api_urls', namespace='api-common')), + path('applications/', include('applications.urls.api_urls', namespace='api-applications')), + path('tickets/', include('tickets.urls.api_urls', namespace='api-tickets')), ] api_v2 = [ - path('terminal/', include('terminal.urls.api_urls_v2', namespace='api-terminal-v2')), - path('users/', include('users.urls.api_urls_v2', namespace='api-users-v2')), + path('terminal/', include('terminal.urls.api_urls_v2', namespace='api-terminal-v2')), + path('users/', include('users.urls.api_urls_v2', namespace='api-users-v2')), ] @@ -42,6 +43,7 @@ app_view_patterns = [ path('orgs/', include('orgs.urls.views_urls', namespace='orgs')), path('auth/', include('authentication.urls.view_urls'), name='auth'), path('applications/', include('applications.urls.views_urls', namespace='applications')), + path('tickets/', include('tickets.urls.views_urls', namespace='tickets')), re_path(r'flower/(?P.*)', celery_flower_view, name='flower-view'), ] @@ -65,7 +67,8 @@ urlpatterns = [ path('api/v2/', include(api_v2)), re_path('api/(?P\w+)/(?Pv\d)/.*', views.redirect_format_api), path('api/health/', views.HealthCheckView.as_view(), name="health"), - path('luna/', views.LunaView.as_view(), name='luna-view'), + re_path('luna/.*', views.LunaView.as_view(), name='luna-view'), + re_path('koko/.*', views.KokoView.as_view(), name='koko-view'), re_path('ws/.*', views.WsView.as_view(), name='ws-view'), path('i18n//', views.I18NView.as_view(), name='i18n-switch'), path('settings/', include('settings.urls.view_urls', namespace='settings')), diff --git a/apps/jumpserver/views.py b/apps/jumpserver/views.py index f8c8c1f02..1c5d2167a 100644 --- a/apps/jumpserver/views.py +++ b/apps/jumpserver/views.py @@ -234,3 +234,10 @@ class WsView(APIView): .format(self.ws_port)) return JsonResponse({"msg": msg}) + +class KokoView(View): + def get(self, request): + msg = _( + "
Koko is a separately deployed program, you need to deploy Koko, configure nginx for url distribution,
" + "
If you see this page, prove that you are not accessing the nginx listening port. Good luck.
") + return HttpResponse(msg) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 233d13246..1c086dd7f 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 95f544397..f09593c4f 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-11-13 16:38+0800\n" +"POT-Creation-Date: 2019-11-20 12:02+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -73,31 +73,31 @@ msgstr "运行参数" #: applications/forms/remote_app.py:100 applications/models/remote_app.py:23 #: applications/templates/applications/remote_app_detail.html:57 -#: applications/templates/applications/remote_app_list.html:22 +#: applications/templates/applications/remote_app_list.html:20 #: applications/templates/applications/user_remote_app_list.html:18 #: assets/forms/domain.py:15 assets/forms/label.py:13 #: assets/models/asset.py:295 assets/models/authbook.py:24 #: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:32 #: assets/serializers/asset_user.py:82 assets/serializers/system_user.py:37 -#: assets/templates/assets/admin_user_list.html:46 +#: assets/templates/assets/admin_user_list.html:44 #: assets/templates/assets/domain_detail.html:60 -#: assets/templates/assets/domain_list.html:26 +#: assets/templates/assets/domain_list.html:22 #: assets/templates/assets/label_list.html:16 -#: assets/templates/assets/system_user_list.html:51 audits/models.py:20 +#: assets/templates/assets/system_user_list.html:49 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 #: perms/templates/perms/asset_permission_create_update.html:45 #: perms/templates/perms/asset_permission_list.html:52 -#: perms/templates/perms/asset_permission_list.html:121 +#: perms/templates/perms/asset_permission_list.html:112 #: terminal/backends/command/models.py:13 terminal/models.py:157 #: terminal/templates/terminal/command_list.html:30 #: terminal/templates/terminal/command_list.html:66 #: 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/templates/change_auth_plan/plan_create_update.html:46 +#: xpack/plugins/change_auth_plan/models.py:419 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:44 #: 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 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14 @@ -110,27 +110,27 @@ msgstr "资产" #: applications/models/remote_app.py:21 #: applications/templates/applications/remote_app_detail.html:53 -#: applications/templates/applications/remote_app_list.html:20 +#: applications/templates/applications/remote_app_list.html:18 #: applications/templates/applications/user_remote_app_list.html:16 #: assets/forms/asset.py:21 assets/forms/domain.py:77 assets/forms/user.py:75 #: assets/forms/user.py:95 assets/models/base.py:28 assets/models/cluster.py:18 #: assets/models/cmd_filter.py:21 assets/models/domain.py:20 #: assets/models/group.py:20 assets/models/label.py:18 #: assets/templates/assets/admin_user_detail.html:56 -#: assets/templates/assets/admin_user_list.html:44 +#: assets/templates/assets/admin_user_list.html:42 #: assets/templates/assets/cmd_filter_detail.html:61 -#: assets/templates/assets/cmd_filter_list.html:24 +#: assets/templates/assets/cmd_filter_list.html:22 #: assets/templates/assets/domain_detail.html:56 #: assets/templates/assets/domain_gateway_list.html:67 -#: assets/templates/assets/domain_list.html:25 +#: assets/templates/assets/domain_list.html:21 #: assets/templates/assets/label_list.html:14 #: assets/templates/assets/system_user_detail.html:58 -#: assets/templates/assets/system_user_list.html:47 ops/models/adhoc.py:37 +#: assets/templates/assets/system_user_list.html:45 ops/models/adhoc.py:37 #: ops/templates/ops/task_detail.html:60 ops/templates/ops/task_list.html:11 #: orgs/models.py:12 perms/models/base.py:48 #: perms/templates/perms/asset_permission_detail.html:62 #: perms/templates/perms/asset_permission_list.html:49 -#: perms/templates/perms/asset_permission_list.html:68 +#: perms/templates/perms/asset_permission_list.html:208 #: perms/templates/perms/asset_permission_user.html:54 #: perms/templates/perms/remote_app_permission_detail.html:62 #: perms/templates/perms/remote_app_permission_list.html:14 @@ -143,8 +143,9 @@ msgstr "资产" #: settings/templates/settings/terminal_setting.html:83 #: settings/templates/settings/terminal_setting.html:105 terminal/models.py:23 #: terminal/models.py:260 terminal/templates/terminal/terminal_detail.html:43 -#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14 -#: users/models/user.py:373 users/templates/users/_select_user_modal.html:13 +#: terminal/templates/terminal/terminal_list.html:29 users/forms.py:162 +#: users/models/group.py:14 users/models/user.py:429 +#: users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_detail.html:63 #: users/templates/users/user_group_detail.html:55 #: users/templates/users/user_group_list.html:35 @@ -152,7 +153,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 @@ -169,7 +170,7 @@ msgstr "名称" #: applications/models/remote_app.py:28 #: applications/templates/applications/remote_app_detail.html:61 -#: applications/templates/applications/remote_app_list.html:21 +#: applications/templates/applications/remote_app_list.html:19 #: applications/templates/applications/user_remote_app_list.html:17 msgid "App type" msgstr "应用类型" @@ -197,9 +198,9 @@ 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/group.py:32 +#: users/models/user.py:470 users/serializers/group.py:32 #: 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 @@ -222,7 +223,8 @@ msgstr "创建者" #: orgs/models.py:17 perms/models/base.py:55 #: perms/templates/perms/asset_permission_detail.html:94 #: perms/templates/perms/remote_app_permission_detail.html:86 -#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17 +#: terminal/templates/terminal/terminal_detail.html:59 +#: tickets/templates/tickets/ticket_detail.html:52 users/models/group.py:17 #: users/templates/users/user_group_detail.html:63 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:105 #: xpack/plugins/cloud/models.py:83 xpack/plugins/cloud/models.py:182 @@ -236,33 +238,34 @@ msgstr "创建日期" # msgstr "创建日期" #: applications/models/remote_app.py:45 #: applications/templates/applications/remote_app_detail.html:77 -#: applications/templates/applications/remote_app_list.html:23 +#: applications/templates/applications/remote_app_list.html:21 #: applications/templates/applications/user_remote_app_list.html:19 #: assets/models/asset.py:176 assets/models/base.py:33 #: assets/models/cluster.py:29 assets/models/cmd_filter.py:23 #: assets/models/cmd_filter.py:56 assets/models/domain.py:21 #: assets/models/domain.py:53 assets/models/group.py:23 #: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:72 -#: assets/templates/assets/admin_user_list.html:50 +#: assets/templates/assets/admin_user_list.html:48 #: assets/templates/assets/asset_detail.html:130 #: assets/templates/assets/cmd_filter_detail.html:65 -#: assets/templates/assets/cmd_filter_list.html:27 +#: assets/templates/assets/cmd_filter_list.html:25 #: assets/templates/assets/cmd_filter_rule_list.html:62 #: assets/templates/assets/domain_detail.html:76 #: assets/templates/assets/domain_gateway_list.html:72 -#: assets/templates/assets/domain_list.html:28 +#: assets/templates/assets/domain_list.html:24 #: assets/templates/assets/system_user_detail.html:104 -#: assets/templates/assets/system_user_list.html:55 ops/models/adhoc.py:43 +#: assets/templates/assets/system_user_list.html:53 ops/models/adhoc.py:43 #: orgs/models.py:18 perms/models/base.py:56 #: perms/templates/perms/asset_permission_detail.html:102 #: perms/templates/perms/remote_app_permission_detail.html:94 #: settings/models.py:34 terminal/models.py:33 -#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15 -#: users/models/user.py:406 users/templates/users/user_detail.html:129 +#: terminal/templates/terminal/terminal_detail.html:63 +#: tickets/templates/tickets/ticket_detail.html:104 users/models/group.py:15 +#: users/models/user.py:462 users/templates/users/user_detail.html:129 #: 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 @@ -310,14 +313,14 @@ msgstr "远程应用" #: terminal/templates/terminal/terminal_update.html:45 #: 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_detail.html:180 #: users/templates/users/user_group_create_update.html:31 #: users/templates/users/user_password_update.html:75 #: users/templates/users/user_profile.html:209 #: users/templates/users/user_profile_update.html:67 #: users/templates/users/user_pubkey_update.html:74 #: users/templates/users/user_pubkey_update.html:80 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:71 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:69 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:33 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:53 #: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:44 @@ -359,7 +362,7 @@ msgstr "重置" #: users/templates/users/user_password_update.html:76 #: users/templates/users/user_profile_update.html:68 #: users/templates/users/user_pubkey_update.html:81 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:72 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:70 #: xpack/plugins/interface/templates/interface/interface.html:74 #: xpack/plugins/vault/templates/vault/vault_create.html:46 msgid "Submit" @@ -392,27 +395,27 @@ msgid "Detail" msgstr "详情" #: applications/templates/applications/remote_app_detail.html:21 -#: applications/templates/applications/remote_app_list.html:54 +#: applications/templates/applications/remote_app_list.html:52 #: assets/templates/assets/_asset_user_list.html:75 #: assets/templates/assets/admin_user_detail.html:24 -#: assets/templates/assets/admin_user_list.html:26 -#: assets/templates/assets/admin_user_list.html:74 +#: assets/templates/assets/admin_user_list.html:24 +#: assets/templates/assets/admin_user_list.html:72 #: assets/templates/assets/asset_detail.html:26 #: assets/templates/assets/asset_list.html:78 #: assets/templates/assets/asset_list.html:167 #: assets/templates/assets/cmd_filter_detail.html:29 -#: assets/templates/assets/cmd_filter_list.html:58 +#: assets/templates/assets/cmd_filter_list.html:56 #: assets/templates/assets/cmd_filter_rule_list.html:86 #: assets/templates/assets/domain_detail.html:24 #: assets/templates/assets/domain_detail.html:103 #: assets/templates/assets/domain_gateway_list.html:97 -#: assets/templates/assets/domain_list.html:54 +#: assets/templates/assets/domain_list.html:50 #: 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:34 +#: assets/templates/assets/system_user_list.html:27 +#: assets/templates/assets/system_user_list.html:79 audits/models.py:34 #: perms/templates/perms/asset_permission_detail.html:30 -#: perms/templates/perms/asset_permission_list.html:178 +#: perms/templates/perms/asset_permission_list.html:169 #: perms/templates/perms/remote_app_permission_detail.html:30 #: perms/templates/perms/remote_app_permission_list.html:64 #: terminal/templates/terminal/terminal_detail.html:16 @@ -440,25 +443,25 @@ msgid "Update" msgstr "更新" #: applications/templates/applications/remote_app_detail.html:25 -#: applications/templates/applications/remote_app_list.html:55 +#: applications/templates/applications/remote_app_list.html:53 #: assets/templates/assets/admin_user_detail.html:28 -#: assets/templates/assets/admin_user_list.html:75 +#: assets/templates/assets/admin_user_list.html:73 #: assets/templates/assets/asset_detail.html:30 #: assets/templates/assets/asset_list.html:168 #: assets/templates/assets/cmd_filter_detail.html:33 -#: assets/templates/assets/cmd_filter_list.html:59 +#: assets/templates/assets/cmd_filter_list.html:57 #: assets/templates/assets/cmd_filter_rule_list.html:87 #: assets/templates/assets/domain_detail.html:28 #: assets/templates/assets/domain_detail.html:104 #: assets/templates/assets/domain_gateway_list.html:98 -#: assets/templates/assets/domain_list.html:55 +#: assets/templates/assets/domain_list.html:51 #: 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:35 +#: assets/templates/assets/system_user_list.html:80 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 -#: perms/templates/perms/asset_permission_list.html:179 +#: perms/templates/perms/asset_permission_list.html:170 #: perms/templates/perms/remote_app_permission_detail.html:34 #: perms/templates/perms/remote_app_permission_list.html:65 #: settings/templates/settings/terminal_setting.html:93 @@ -481,7 +484,7 @@ msgstr "更新" msgid "Delete" msgstr "删除" -#: applications/templates/applications/remote_app_list.html:5 +#: applications/templates/applications/remote_app_list.html:4 msgid "" "Before using this feature, make sure that the application loader has been " "uploaded to the application server and successfully published as a RemoteApp " @@ -490,27 +493,27 @@ msgstr "" "使用此功能前,请确保已将应用加载器上传到应用服务器并成功发布为一个 RemoteApp " "应用" -#: applications/templates/applications/remote_app_list.html:6 +#: applications/templates/applications/remote_app_list.html:5 msgid "Download application loader" msgstr "下载应用加载器" -#: applications/templates/applications/remote_app_list.html:12 +#: applications/templates/applications/remote_app_list.html:10 #: applications/views/remote_app.py:48 msgid "Create RemoteApp" msgstr "创建远程应用" -#: applications/templates/applications/remote_app_list.html:24 +#: applications/templates/applications/remote_app_list.html:22 #: applications/templates/applications/user_remote_app_list.html:20 #: assets/models/cmd_filter.py:55 #: assets/templates/assets/_asset_user_list.html:25 -#: assets/templates/assets/admin_user_list.html:51 +#: assets/templates/assets/admin_user_list.html:49 #: assets/templates/assets/asset_list.html:100 -#: assets/templates/assets/cmd_filter_list.html:28 +#: assets/templates/assets/cmd_filter_list.html:26 #: assets/templates/assets/cmd_filter_rule_list.html:63 #: assets/templates/assets/domain_gateway_list.html:73 -#: assets/templates/assets/domain_list.html:29 +#: assets/templates/assets/domain_list.html:25 #: assets/templates/assets/label_list.html:17 -#: assets/templates/assets/system_user_list.html:56 audits/models.py:39 +#: assets/templates/assets/system_user_list.html:54 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 @@ -519,12 +522,13 @@ msgstr "创建远程应用" #: perms/forms/asset_permission.py:21 #: perms/templates/perms/asset_permission_create_update.html:50 #: perms/templates/perms/asset_permission_list.html:56 -#: perms/templates/perms/asset_permission_list.html:130 +#: perms/templates/perms/asset_permission_list.html:121 #: perms/templates/perms/remote_app_permission_list.html:20 #: settings/templates/settings/terminal_setting.html:85 #: settings/templates/settings/terminal_setting.html:107 #: terminal/templates/terminal/session_list.html:36 #: terminal/templates/terminal/terminal_list.html:36 +#: tickets/templates/tickets/ticket_list.html:105 #: users/templates/users/_granted_assets.html:34 #: users/templates/users/user_group_list.html:38 #: users/templates/users/user_list.html:41 @@ -602,7 +606,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" @@ -639,8 +643,8 @@ msgstr "网域" #: assets/templates/assets/asset_create.html:42 #: perms/forms/asset_permission.py:87 perms/forms/asset_permission.py:94 #: perms/templates/perms/asset_permission_list.html:53 -#: perms/templates/perms/asset_permission_list.html:74 -#: perms/templates/perms/asset_permission_list.html:124 +#: perms/templates/perms/asset_permission_list.html:115 +#: perms/templates/perms/asset_permission_list.html:214 #: xpack/plugins/change_auth_plan/forms.py:74 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:55 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:15 @@ -681,41 +685,42 @@ msgstr "内容不能包含: {}" #: 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" msgstr "SSH网关,支持代理SSH,RDP和VNC" #: assets/forms/domain.py:78 assets/forms/user.py:76 assets/forms/user.py:96 -#: assets/models/base.py:29 assets/models/gathered_user.py:16 +#: assets/models/base.py:29 assets/models/gathered_user.py:15 #: assets/templates/assets/_asset_user_auth_update_modal.html:15 #: assets/templates/assets/_asset_user_auth_view_modal.html:21 #: assets/templates/assets/_asset_user_list.html:21 #: assets/templates/assets/admin_user_detail.html:60 -#: assets/templates/assets/admin_user_list.html:45 +#: assets/templates/assets/admin_user_list.html:43 #: 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:81 +#: assets/templates/assets/system_user_list.html:46 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 +#: authentication/templates/authentication/login.html:58 +#: authentication/templates/authentication/xpack_login.html:93 +#: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:210 #: 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:14 -#: users/models/user.py:371 users/templates/users/_select_user_modal.html:14 +#: users/forms.py:161 users/models/user.py:427 +#: 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 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:13 -#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:74 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:64 msgid "Username" msgstr "用户名" @@ -728,8 +733,8 @@ msgstr "密码或密钥密码" #: assets/templates/assets/_asset_user_auth_update_modal.html:21 #: assets/templates/assets/_asset_user_auth_view_modal.html:27 #: authentication/forms.py:15 -#: authentication/templates/authentication/login.html:68 -#: authentication/templates/authentication/new_login.html:95 +#: authentication/templates/authentication/login.html:66 +#: authentication/templates/authentication/xpack_login.html:101 #: 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 @@ -737,14 +742,14 @@ 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 "密码" #: assets/forms/user.py:30 assets/serializers/asset_user.py:71 #: assets/templates/assets/_asset_user_auth_update_modal.html:27 -#: users/models/user.py:400 +#: users/models/user.py:456 msgid "Private key" msgstr "ssh私钥" @@ -793,15 +798,16 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" #: assets/templates/assets/domain_gateway_list.html:68 #: assets/templates/assets/user_asset_list.html:76 #: audits/templates/audits/login_log_list.html:60 -#: perms/templates/perms/asset_permission_asset.html:58 settings/forms.py:144 +#: perms/templates/perms/asset_permission_asset.html:58 +#: perms/templates/perms/asset_permission_list.html:212 settings/forms.py:144 #: users/templates/users/_granted_assets.html:31 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:54 -#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:73 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:63 msgid "IP" msgstr "IP" #: assets/models/asset.py:136 assets/serializers/asset_user.py:27 -#: assets/serializers/gathered_user.py:19 +#: assets/serializers/gathered_user.py:20 #: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/_asset_user_auth_update_modal.html:9 #: assets/templates/assets/_asset_user_auth_view_modal.html:15 @@ -810,10 +816,10 @@ msgstr "IP" #: assets/templates/assets/asset_list.html:96 #: assets/templates/assets/user_asset_list.html:75 #: perms/templates/perms/asset_permission_asset.html:57 -#: perms/templates/perms/asset_permission_list.html:73 settings/forms.py:143 +#: perms/templates/perms/asset_permission_list.html:213 settings/forms.py:143 #: users/templates/users/_granted_assets.html:30 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:53 -#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:72 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:62 msgid "Hostname" msgstr "主机名" @@ -821,7 +827,7 @@ msgstr "主机名" #: assets/models/user.py:113 assets/templates/assets/asset_detail.html:70 #: assets/templates/assets/domain_gateway_list.html:70 #: assets/templates/assets/system_user_detail.html:70 -#: assets/templates/assets/system_user_list.html:49 +#: assets/templates/assets/system_user_list.html:47 #: terminal/templates/terminal/session_list.html:31 #: terminal/templates/terminal/session_list.html:75 msgid "Protocol" @@ -842,6 +848,7 @@ msgstr "系统平台" #: assets/models/asset.py:146 assets/models/authbook.py:27 #: assets/models/cmd_filter.py:22 assets/models/domain.py:54 #: assets/models/label.py:22 assets/templates/assets/asset_detail.html:110 +#: authentication/models.py:45 msgid "Is active" msgstr "激活" @@ -931,21 +938,21 @@ 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公钥" -#: assets/models/base.py:35 assets/models/gathered_user.py:21 +#: assets/models/base.py:35 assets/models/gathered_user.py:20 #: assets/templates/assets/cmd_filter_detail.html:73 common/mixins/models.py:52 #: ops/models/adhoc.py:46 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109 -#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:76 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:68 msgid "Date updated" msgstr "更新日期" @@ -957,7 +964,7 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 users/models/user.py:392 +#: assets/models/cluster.py:22 users/models/user.py:448 #: users/templates/users/user_detail.html:76 msgid "Phone" msgstr "手机" @@ -983,7 +990,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:14 -#: users/models/user.py:512 +#: users/models/user.py:568 msgid "System" msgstr "系统" @@ -1011,7 +1018,7 @@ msgstr "BGP全网通" msgid "Regex" msgstr "正则表达式" -#: assets/models/cmd_filter.py:40 ops/models/command.py:21 +#: assets/models/cmd_filter.py:40 ops/models/command.py:22 #: ops/templates/ops/command_execution_list.html:64 terminal/models.py:163 #: terminal/templates/terminal/command_list.html:28 #: terminal/templates/terminal/command_list.html:68 @@ -1040,6 +1047,8 @@ msgstr "过滤器" #: settings/templates/settings/replay_storage_create.html:31 #: settings/templates/settings/terminal_setting.html:84 #: settings/templates/settings/terminal_setting.html:106 +#: tickets/models/ticket.py:43 tickets/templates/tickets/ticket_detail.html:33 +#: tickets/templates/tickets/ticket_list.html:35 msgid "Type" msgstr "类型" @@ -1069,16 +1078,26 @@ msgstr "命令过滤规则" #: assets/models/domain.py:61 assets/templates/assets/domain_detail.html:21 #: assets/templates/assets/domain_detail.html:64 #: assets/templates/assets/domain_gateway_list.html:26 -#: assets/templates/assets/domain_list.html:27 +#: assets/templates/assets/domain_list.html:23 msgid "Gateway" msgstr "网关" -#: assets/models/gathered_user.py:17 -#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:75 +#: assets/models/gathered_user.py:16 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:67 msgid "Present" msgstr "存在" -#: assets/models/gathered_user.py:32 +#: assets/models/gathered_user.py:17 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:65 +msgid "Date last login" +msgstr "最后登录日期" + +#: assets/models/gathered_user.py:18 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:66 +msgid "IP last login" +msgstr "最后登录IP" + +#: assets/models/gathered_user.py:31 msgid "GatherUser" msgstr "收集用户" @@ -1097,21 +1116,24 @@ msgstr "默认资产组" #: audits/templates/audits/operate_log_list.html:72 #: audits/templates/audits/password_change_log_list.html:39 #: audits/templates/audits/password_change_log_list.html:56 -#: ops/templates/ops/command_execution_list.html:38 +#: authentication/models.py:43 ops/templates/ops/command_execution_list.html:38 #: ops/templates/ops/command_execution_list.html:63 #: perms/forms/asset_permission.py:78 perms/forms/remote_app_permission.py:34 #: perms/models/base.py:49 #: perms/templates/perms/asset_permission_create_update.html:41 #: perms/templates/perms/asset_permission_list.html:50 -#: perms/templates/perms/asset_permission_list.html:115 +#: perms/templates/perms/asset_permission_list.html:106 #: perms/templates/perms/remote_app_permission_create_update.html:43 #: perms/templates/perms/remote_app_permission_list.html:15 #: templates/index.html:87 terminal/backends/command/models.py:12 #: 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:339 -#: users/models/user.py:127 users/models/user.py:143 users/models/user.py:500 +#: terminal/templates/terminal/session_list.html:71 tickets/models/ticket.py:33 +#: tickets/models/ticket.py:128 tickets/templates/tickets/ticket_detail.html:32 +#: tickets/templates/tickets/ticket_list.html:34 +#: tickets/templates/tickets/ticket_list.html:100 users/forms.py:339 +#: users/models/user.py:148 users/models/user.py:164 users/models/user.py:556 #: 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 @@ -1177,7 +1199,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 "资产管理" @@ -1196,7 +1218,7 @@ msgid "Shell" msgstr "Shell" #: assets/models/user.py:117 assets/templates/assets/system_user_detail.html:66 -#: assets/templates/assets/system_user_list.html:50 +#: assets/templates/assets/system_user_list.html:48 msgid "Login mode" msgstr "登录模式" @@ -1207,8 +1229,8 @@ msgstr "登录模式" #: perms/models/asset_permission.py:82 perms/models/remote_app_permission.py:16 #: perms/templates/perms/asset_permission_detail.html:140 #: perms/templates/perms/asset_permission_list.html:54 -#: perms/templates/perms/asset_permission_list.html:75 -#: perms/templates/perms/asset_permission_list.html:127 +#: perms/templates/perms/asset_permission_list.html:118 +#: perms/templates/perms/asset_permission_list.html:215 #: perms/templates/perms/remote_app_permission_detail.html:131 #: perms/templates/perms/remote_app_permission_list.html:18 #: templates/_nav.html:45 terminal/backends/command/models.py:14 @@ -1235,8 +1257,8 @@ msgstr "不可达" msgid "Reachable" msgstr "可连接" -#: assets/models/utils.py:45 assets/tasks/const.py:86 -#: authentication/utils.py:13 xpack/plugins/license/models.py:78 +#: assets/models/utils.py:45 assets/tasks/const.py:86 audits/utils.py:30 +#: xpack/plugins/license/models.py:78 msgid "Unknown" msgstr "未知" @@ -1267,7 +1289,7 @@ msgid "Backend" msgstr "后端" #: assets/serializers/asset_user.py:67 users/forms.py:282 -#: users/models/user.py:403 users/templates/users/first_login.html:42 +#: users/models/user.py:459 users/templates/users/first_login.html:42 #: users/templates/users/user_password_update.html:49 #: users/templates/users/user_profile.html:69 #: users/templates/users/user_profile_update.html:46 @@ -1322,7 +1344,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 任务" @@ -1342,7 +1364,7 @@ msgstr "更新资产硬件信息" msgid "Update asset hardware info: {}" msgstr "更新资产硬件信息: {}" -#: assets/tasks/gather_asset_users.py:96 +#: assets/tasks/gather_asset_users.py:107 msgid "Gather assets users" msgstr "收集资产上的用户" @@ -1415,7 +1437,7 @@ msgstr "选择资产" #: assets/templates/assets/_asset_group_bulk_update_modal.html:21 #: assets/templates/assets/cmd_filter_detail.html:89 -#: assets/templates/assets/cmd_filter_list.html:26 +#: assets/templates/assets/cmd_filter_list.html:24 msgid "System users" msgstr "系统用户" @@ -1461,8 +1483,8 @@ msgstr "请输入密码" #: assets/templates/assets/_asset_user_auth_update_modal.html:68 #: assets/templates/assets/asset_detail.html:302 -#: users/templates/users/user_detail.html:313 -#: users/templates/users/user_detail.html:340 +#: users/templates/users/user_detail.html:367 +#: users/templates/users/user_detail.html:394 #: xpack/plugins/interface/views.py:35 msgid "Update successfully!" msgstr "更新成功" @@ -1472,6 +1494,7 @@ msgid "Asset user auth" msgstr "资产用户信息" #: assets/templates/assets/_asset_user_auth_view_modal.html:54 +#: authentication/templates/authentication/login_wait_confirm.html:114 msgid "Copy success" msgstr "复制成功" @@ -1484,7 +1507,8 @@ msgstr "获取认证信息错误" #: authentication/templates/authentication/_access_key_modal.html:142 #: authentication/templates/authentication/_mfa_confirm_modal.html:53 #: settings/templates/settings/_ldap_list_users_modal.html:171 -#: templates/_modal.html:22 +#: templates/_modal.html:22 tickets/models/ticket.py:68 +#: tickets/templates/tickets/ticket_detail.html:103 msgid "Close" msgstr "关闭" @@ -1494,6 +1518,7 @@ msgstr "关闭" #: ops/templates/ops/task_adhoc.html:63 #: terminal/templates/terminal/command_list.html:33 #: terminal/templates/terminal/session_detail.html:50 +#: tickets/templates/tickets/ticket_list.html:37 msgid "Datetime" msgstr "日期" @@ -1566,7 +1591,7 @@ msgstr "重命名成功" #: assets/templates/assets/gateway_create_update.html:37 #: perms/templates/perms/asset_permission_create_update.html:38 #: perms/templates/perms/remote_app_permission_create_update.html:39 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:43 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:41 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:27 #: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:27 msgid "Basic" @@ -1589,7 +1614,7 @@ msgstr "自动生成密钥" #: perms/templates/perms/asset_permission_create_update.html:53 #: perms/templates/perms/remote_app_permission_create_update.html:53 #: terminal/templates/terminal/terminal_update.html:40 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:67 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:65 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:48 #: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:39 msgid "Other" @@ -1649,18 +1674,19 @@ msgstr "选择节点" #: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/asset_detail.html:202 -#: assets/templates/assets/asset_list.html:423 +#: assets/templates/assets/asset_list.html:427 #: assets/templates/assets/cmd_filter_detail.html:106 #: assets/templates/assets/system_user_assets.html:97 #: assets/templates/assets/system_user_detail.html:182 -#: assets/templates/assets/system_user_list.html:135 +#: assets/templates/assets/system_user_list.html:133 #: authentication/templates/authentication/_mfa_confirm_modal.html:20 #: settings/templates/settings/terminal_setting.html:168 #: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:112 -#: users/templates/users/user_detail.html:394 -#: users/templates/users/user_detail.html:420 -#: users/templates/users/user_detail.html:443 -#: users/templates/users/user_detail.html:488 +#: users/templates/users/user_detail.html:272 +#: users/templates/users/user_detail.html:448 +#: users/templates/users/user_detail.html:474 +#: users/templates/users/user_detail.html:497 +#: users/templates/users/user_detail.html:542 #: users/templates/users/user_group_create_update.html:32 #: users/templates/users/user_group_list.html:120 #: users/templates/users/user_list.html:256 @@ -1672,57 +1698,57 @@ msgstr "选择节点" msgid "Confirm" msgstr "确认" -#: assets/templates/assets/admin_user_list.html:5 +#: assets/templates/assets/admin_user_list.html:4 msgid "" "Admin users are asset (charged server) on the root, or have NOPASSWD: ALL " "sudo permissions users, " msgstr "" "管理用户是资产(被控服务器)上的 root,或拥有 NOPASSWD: ALL sudo 权限的用户," -#: assets/templates/assets/admin_user_list.html:6 +#: assets/templates/assets/admin_user_list.html:5 msgid "" "Jumpserver users of the system using the user to `push system user`, `get " "assets hardware information`, etc. " msgstr "Jumpserver 使用该用户来 `推送系统用户`、`获取资产硬件信息` 等。" -#: assets/templates/assets/admin_user_list.html:16 +#: assets/templates/assets/admin_user_list.html:14 #: assets/templates/assets/asset_list.html:68 -#: assets/templates/assets/system_user_list.html:19 +#: assets/templates/assets/system_user_list.html:17 #: audits/templates/audits/login_log_list.html:91 #: users/templates/users/user_group_list.html:10 #: users/templates/users/user_list.html:10 -#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:59 -#: xpack/plugins/vault/templates/vault/vault.html:55 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:49 +#: xpack/plugins/vault/templates/vault/vault.html:47 msgid "Export" msgstr "导出" -#: assets/templates/assets/admin_user_list.html:21 +#: assets/templates/assets/admin_user_list.html:19 #: assets/templates/assets/asset_list.html:73 -#: assets/templates/assets/system_user_list.html:24 +#: assets/templates/assets/system_user_list.html:22 #: 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 -#: xpack/plugins/vault/templates/vault/vault.html:60 +#: xpack/plugins/vault/templates/vault/vault.html:52 msgid "Import" msgstr "导入" -#: assets/templates/assets/admin_user_list.html:36 +#: assets/templates/assets/admin_user_list.html:34 #: assets/views/admin_user.py:50 msgid "Create admin user" msgstr "创建管理用户" -#: assets/templates/assets/admin_user_list.html:125 -#: assets/templates/assets/admin_user_list.html:156 +#: assets/templates/assets/admin_user_list.html:123 +#: assets/templates/assets/admin_user_list.html:154 #: assets/templates/assets/asset_list.html:304 #: assets/templates/assets/asset_list.html:341 -#: assets/templates/assets/system_user_list.html:188 -#: assets/templates/assets/system_user_list.html:219 +#: assets/templates/assets/system_user_list.html:186 +#: assets/templates/assets/system_user_list.html:217 #: users/templates/users/user_group_list.html:164 #: users/templates/users/user_group_list.html:195 #: users/templates/users/user_list.html:165 #: users/templates/users/user_list.html:197 -#: xpack/plugins/vault/templates/vault/vault.html:224 +#: xpack/plugins/vault/templates/vault/vault.html:200 msgid "Please select file" msgstr "选择文件" @@ -1770,7 +1796,7 @@ msgstr "硬盘" msgid "Date joined" msgstr "创建日期" -#: assets/templates/assets/asset_detail.html:148 authentication/models.py:15 +#: assets/templates/assets/asset_detail.html:148 authentication/models.py:19 #: authentication/templates/authentication/_access_key_modal.html:32 #: perms/models/base.py:51 #: perms/templates/perms/asset_permission_create_update.html:55 @@ -1789,10 +1815,11 @@ msgid "Refresh hardware" msgstr "更新硬件信息" #: assets/templates/assets/asset_detail.html:168 +#: authentication/templates/authentication/login_wait_confirm.html:42 msgid "Refresh" msgstr "刷新" -#: assets/templates/assets/asset_list.html:8 +#: assets/templates/assets/asset_list.html:9 msgid "" "The left side is the asset tree, right click to create, delete, and change " "the tree node, authorization asset is also organized as a node, and the " @@ -1857,43 +1884,43 @@ msgstr "仅显示当前节点资产" msgid "Displays all child node assets" msgstr "显示所有子节点资产" -#: assets/templates/assets/asset_list.html:417 -#: assets/templates/assets/system_user_list.html:129 -#: users/templates/users/user_detail.html:388 -#: users/templates/users/user_detail.html:414 -#: users/templates/users/user_detail.html:482 +#: assets/templates/assets/asset_list.html:421 +#: assets/templates/assets/system_user_list.html:127 +#: users/templates/users/user_detail.html:442 +#: users/templates/users/user_detail.html:468 +#: users/templates/users/user_detail.html:536 #: users/templates/users/user_group_list.html:114 #: users/templates/users/user_list.html:250 #: xpack/plugins/interface/templates/interface/interface.html:97 msgid "Are you sure?" msgstr "你确认吗?" -#: assets/templates/assets/asset_list.html:418 +#: assets/templates/assets/asset_list.html:422 msgid "This will delete the selected assets !!!" msgstr "删除选择资产" -#: assets/templates/assets/asset_list.html:421 -#: assets/templates/assets/system_user_list.html:133 +#: assets/templates/assets/asset_list.html:425 +#: assets/templates/assets/system_user_list.html:131 #: settings/templates/settings/terminal_setting.html:166 -#: users/templates/users/user_detail.html:392 -#: users/templates/users/user_detail.html:418 -#: users/templates/users/user_detail.html:486 +#: users/templates/users/user_detail.html:446 +#: users/templates/users/user_detail.html:472 +#: users/templates/users/user_detail.html:540 #: users/templates/users/user_group_list.html:118 #: users/templates/users/user_list.html:254 #: xpack/plugins/interface/templates/interface/interface.html:101 msgid "Cancel" msgstr "取消" -#: assets/templates/assets/asset_list.html:432 +#: assets/templates/assets/asset_list.html:436 msgid "Asset Deleted." msgstr "已被删除" -#: assets/templates/assets/asset_list.html:433 -#: assets/templates/assets/asset_list.html:441 +#: assets/templates/assets/asset_list.html:437 +#: assets/templates/assets/asset_list.html:445 msgid "Asset Delete" msgstr "删除" -#: assets/templates/assets/asset_list.html:440 +#: assets/templates/assets/asset_list.html:444 msgid "Asset Deleting failed." msgstr "删除失败" @@ -1902,7 +1929,7 @@ msgid "Configuration" msgstr "配置" #: assets/templates/assets/cmd_filter_detail.html:25 -#: assets/templates/assets/cmd_filter_list.html:25 +#: assets/templates/assets/cmd_filter_list.html:23 #: assets/templates/assets/cmd_filter_rule_list.html:23 msgid "Rules" msgstr "规则" @@ -1911,33 +1938,33 @@ msgstr "规则" msgid "Binding to system user" msgstr "绑定到系统用户" -#: assets/templates/assets/cmd_filter_list.html:6 +#: assets/templates/assets/cmd_filter_list.html:5 msgid "" "System user bound some command filter, each command filter has some rules," msgstr "系统用户可以绑定一些命令过滤器,一个过滤器可以定义一些规则" -#: assets/templates/assets/cmd_filter_list.html:7 +#: assets/templates/assets/cmd_filter_list.html:6 msgid "When user login asset with this system user, then run a command," msgstr "当用户使用这个系统用户登录资产,然后执行一个命令" -#: assets/templates/assets/cmd_filter_list.html:8 +#: assets/templates/assets/cmd_filter_list.html:7 msgid "The command will be filter by rules, higher priority rule run first," msgstr "这个命令需要被绑定过滤器的所有规则匹配,高优先级先被匹配," -#: assets/templates/assets/cmd_filter_list.html:9 +#: assets/templates/assets/cmd_filter_list.html:8 msgid "" "When a rule matched, if rule action is allow, then allow command execute," msgstr "当一个规则匹配到了,如果规则的动作是允许,这个命令会被放行," -#: assets/templates/assets/cmd_filter_list.html:10 +#: assets/templates/assets/cmd_filter_list.html:9 msgid "else if action is deny, then command with be deny," msgstr "如果规则的动作是禁止,命令将会被禁止执行," -#: assets/templates/assets/cmd_filter_list.html:11 +#: assets/templates/assets/cmd_filter_list.html:10 msgid "else match next rule, if none matched, allowed" msgstr "否则就匹配下一个规则,如果最后没有匹配到规则,则允许执行" -#: assets/templates/assets/cmd_filter_list.html:16 +#: assets/templates/assets/cmd_filter_list.html:14 #: assets/views/cmd_filter.py:49 msgid "Create command filter" msgstr "创建命令过滤器" @@ -1985,7 +2012,7 @@ msgstr "测试连接" msgid "Can be connected" msgstr "可连接" -#: assets/templates/assets/domain_list.html:9 +#: assets/templates/assets/domain_list.html:6 msgid "" "The domain function is added to address the fact that some environments " "(such as the hybrid cloud) cannot be connected directly by jumping on the " @@ -1994,11 +2021,11 @@ msgstr "" "网域功能是为了解决部分环境(如:混合云)无法直接连接而新增的功能,原理是通过" "网关服务器进行跳转登录。" -#: assets/templates/assets/domain_list.html:11 +#: assets/templates/assets/domain_list.html:8 msgid "JMS => Domain gateway => Target assets" msgstr "JMS => 网域网关 => 目标资产" -#: assets/templates/assets/domain_list.html:17 assets/views/domain.py:48 +#: assets/templates/assets/domain_list.html:13 assets/views/domain.py:48 msgid "Create domain" msgstr "创建网域" @@ -2036,7 +2063,7 @@ msgstr "Uid" msgid "Binding command filters" msgstr "绑定命令过滤器" -#: assets/templates/assets/system_user_list.html:6 +#: assets/templates/assets/system_user_list.html:5 msgid "" "System user is Jumpserver jump login assets used by the users, can be " "understood as the user login assets, such as web, sa, the dba (` ssh " @@ -2047,7 +2074,7 @@ msgstr "" "web,sa,dba(`ssh web@some-host`),而不是使用某个用户的用户名跳转登录服务器" "(`ssh xiaoming@some-host`);" -#: assets/templates/assets/system_user_list.html:7 +#: assets/templates/assets/system_user_list.html:6 msgid "" "In simple terms, users log into Jumpserver using their own username, and " "Jumpserver uses system users to log into assets. " @@ -2055,7 +2082,7 @@ msgstr "" "简单来说是用户使用自己的用户名登录 Jumpserver,Jumpserver 使用系统用户登录资" "产。" -#: assets/templates/assets/system_user_list.html:8 +#: assets/templates/assets/system_user_list.html:7 msgid "" "When system users are created, if you choose auto push Jumpserver to use " "Ansible push system users into the asset, if the asset (Switch) does not " @@ -2064,25 +2091,25 @@ msgstr "" "系统用户创建时,如果选择了自动推送,Jumpserver 会使用 Ansible 自动推送系统用" "户到资产中,如果资产(交换机)不支持 Ansible,请手动填写账号密码。" -#: assets/templates/assets/system_user_list.html:39 +#: assets/templates/assets/system_user_list.html:37 #: assets/views/system_user.py:47 msgid "Create system user" msgstr "创建系统用户" -#: assets/templates/assets/system_user_list.html:130 +#: assets/templates/assets/system_user_list.html:128 msgid "This will delete the selected System Users !!!" msgstr "删除选择系统用户" -#: assets/templates/assets/system_user_list.html:139 +#: assets/templates/assets/system_user_list.html:137 msgid "System Users Deleted." msgstr "已被删除" -#: assets/templates/assets/system_user_list.html:140 -#: assets/templates/assets/system_user_list.html:145 +#: assets/templates/assets/system_user_list.html:138 +#: assets/templates/assets/system_user_list.html:143 msgid "System Users Delete" msgstr "删除系统用户" -#: assets/templates/assets/system_user_list.html:144 +#: assets/templates/assets/system_user_list.html:142 msgid "System Users Deleting failed." msgstr "系统用户删除失败" @@ -2201,7 +2228,7 @@ msgstr "文件名" #: audits/templates/audits/ftp_log_list.html:79 #: ops/templates/ops/command_execution_list.html:68 #: ops/templates/ops/task_list.html:15 -#: users/templates/users/user_detail.html:464 +#: users/templates/users/user_detail.html:518 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:14 #: xpack/plugins/cloud/api.py:61 msgid "Success" @@ -2209,7 +2236,7 @@ msgstr "成功" #: audits/models.py:33 #: authentication/templates/authentication/_access_key_modal.html:22 -#: xpack/plugins/vault/templates/vault/vault.html:46 +#: xpack/plugins/vault/templates/vault/vault.html:38 msgid "Create" msgstr "创建" @@ -2262,13 +2289,13 @@ 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:194 users/models/user.py:395 +#: users/forms.py:194 users/models/user.py:451 #: users/templates/users/first_login.html:45 msgid "MFA" msgstr "MFA" #: audits/models.py:87 audits/templates/audits/login_log_list.html:63 -#: xpack/plugins/change_auth_plan/models.py:416 +#: 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 @@ -2276,6 +2303,9 @@ msgid "Reason" msgstr "原因" #: audits/models.py:88 audits/templates/audits/login_log_list.html:64 +#: tickets/templates/tickets/ticket_detail.html:34 +#: tickets/templates/tickets/ticket_list.html:36 +#: tickets/templates/tickets/ticket_list.html:101 #: 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 @@ -2294,11 +2324,11 @@ 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 +#: xpack/plugins/gathered_user/models.py:140 msgid "Date start" msgstr "开始日期" @@ -2314,7 +2344,7 @@ msgstr "选择用户" #: audits/templates/audits/password_change_log_list.html:48 #: ops/templates/ops/command_execution_list.html:46 #: ops/templates/ops/command_execution_list.html:51 -#: templates/_base_list.html:41 +#: templates/_base_list.html:41 templates/_user_profile.html:23 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:52 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:48 msgid "Search" @@ -2335,7 +2365,7 @@ msgstr "ID" msgid "UA" msgstr "Agent" -#: audits/templates/audits/login_log_list.html:61 +#: audits/templates/audits/login_log_list.html:61 authentication/models.py:63 msgid "City" msgstr "城市" @@ -2346,23 +2376,23 @@ msgid "Date" msgstr "日期" #: audits/views.py:86 audits/views.py:130 audits/views.py:167 -#: audits/views.py:212 audits/views.py:244 templates/_nav.html:129 +#: audits/views.py:212 audits/views.py:244 templates/_nav.html:137 msgid "Audits" msgstr "日志审计" -#: audits/views.py:87 templates/_nav.html:133 +#: audits/views.py:87 templates/_nav.html:141 msgid "FTP log" msgstr "FTP日志" -#: audits/views.py:131 templates/_nav.html:134 +#: audits/views.py:131 templates/_nav.html:142 msgid "Operate log" msgstr "操作日志" -#: audits/views.py:168 templates/_nav.html:135 +#: audits/views.py:168 templates/_nav.html:143 msgid "Password change log" msgstr "改密日志" -#: audits/views.py:213 templates/_nav.html:132 +#: audits/views.py:213 templates/_nav.html:140 msgid "Login log" msgstr "登录日志" @@ -2370,28 +2400,6 @@ msgstr "登录日志" msgid "Command execution log" msgstr "命令执行" -#: authentication/api/auth.py:61 authentication/api/token.py:45 -#: authentication/templates/authentication/login.html:52 -#: authentication/templates/authentication/new_login.html:77 -msgid "Log in frequently and try again later" -msgstr "登录频繁, 稍后重试" - -#: authentication/api/auth.py:86 -msgid "Please carry seed value and conduct MFA secondary certification" -msgstr "请携带seed值, 进行MFA二次认证" - -#: authentication/api/auth.py:176 -msgid "Please verify the user name and password first" -msgstr "请先进行用户名和密码验证" - -#: authentication/api/auth.py:181 -msgid "MFA certification failed" -msgstr "MFA认证失败" - -#: authentication/api/token.py:80 -msgid "MFA required" -msgstr "" - #: authentication/backends/api.py:53 msgid "Invalid signature header. No credentials provided." msgstr "" @@ -2443,56 +2451,87 @@ msgstr "" msgid "Invalid token or cache refreshed." msgstr "" -#: authentication/const.py:6 +#: authentication/errors.py:20 msgid "Username/password check failed" msgstr "用户名/密码 校验失败" -#: authentication/const.py:7 +#: authentication/errors.py:21 msgid "MFA authentication failed" msgstr "MFA 认证失败" -#: authentication/const.py:8 +#: authentication/errors.py:22 msgid "Username does not exist" msgstr "用户名不存在" -#: authentication/const.py:9 +#: authentication/errors.py:23 msgid "Password expired" -msgstr "密码过期" +msgstr "密码已过期" -#: authentication/const.py:10 +#: authentication/errors.py:24 msgid "Disabled or expired" msgstr "禁用或失效" -#: authentication/forms.py:21 -msgid "" -"The username or password you entered is incorrect, please enter it again." -msgstr "您输入的用户名或密码不正确,请重新输入。" - -#: authentication/forms.py:24 +#: authentication/errors.py:25 msgid "This account is inactive." -msgstr "此账户无效" +msgstr "此账户已禁用" -#: authentication/forms.py:26 +#: authentication/errors.py:35 +msgid "No session found, check your cookie" +msgstr "会话已变更,刷新页面" + +#: authentication/errors.py:37 #, python-brace-format msgid "" +"The username or password you entered is incorrect, please enter it again. " "You can also try {times_try} times (The account will be temporarily locked " "for {block_time} minutes)" -msgstr "您还可以尝试 {times_try} 次(账号将被临时锁定 {block_time} 分钟)" +msgstr "" +"您输入的用户名或密码不正确,请重新输入。 您还可以尝试 {times_try} 次(账号将" +"被临时 锁定 {block_time} 分钟)" -#: authentication/forms.py:30 +#: authentication/errors.py:43 msgid "" "The account has been locked (please contact admin to unlock it or try again " "after {} minutes)" msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)" -#: authentication/forms.py:66 users/forms.py:22 +#: authentication/errors.py:46 users/views/user.py:398 users/views/user.py:423 +msgid "MFA code invalid, or ntp sync server time" +msgstr "MFA验证码不正确,或者服务器端时间不对" + +#: authentication/errors.py:48 +msgid "MFA required" +msgstr "" + +#: authentication/errors.py:49 +msgid "Login confirm required" +msgstr "需要登录复核" + +#: authentication/errors.py:50 +msgid "Wait login confirm ticket for accept" +msgstr "等待登录复核处理" + +#: authentication/errors.py:51 +msgid "Login confirm ticket was {}" +msgstr "登录复核 {}" + +#: authentication/forms.py:32 users/forms.py:22 msgid "MFA code" msgstr "MFA 验证码" -#: authentication/models.py:35 +#: authentication/models.py:39 msgid "Private Token" msgstr "ssh密钥" +#: authentication/models.py:44 users/templates/users/user_detail.html:266 +msgid "Reviewers" +msgstr "审批人" + +#: authentication/models.py:53 tickets/models/ticket.py:25 +#: users/templates/users/user_detail.html:258 +msgid "Login confirm" +msgstr "登录复核" + #: authentication/templates/authentication/_access_key_modal.html:6 msgid "API key list" msgstr "API Key列表" @@ -2515,14 +2554,14 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/models/user.py:330 users/templates/users/user_profile.html:94 +#: users/models/user.py:351 users/templates/users/user_profile.html:94 #: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:166 msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/models/user.py:331 users/templates/users/user_profile.html:92 +#: users/models/user.py:352 users/templates/users/user_profile.html:92 #: users/templates/users/user_profile.html:170 msgid "Enable" msgstr "启用" @@ -2582,35 +2621,30 @@ msgstr "" msgid "Changes the world, starting with a little bit." msgstr "改变世界,从一点点开始。" -#: authentication/templates/authentication/login.html:46 -#: authentication/templates/authentication/login.html:73 -#: authentication/templates/authentication/new_login.html:101 +#: authentication/templates/authentication/login.html:45 +#: authentication/templates/authentication/login.html:76 +#: authentication/templates/authentication/xpack_login.html:112 #: templates/_header_bar.html:83 msgid "Login" msgstr "登录" #: authentication/templates/authentication/login.html:54 -#: authentication/templates/authentication/new_login.html:80 -msgid "The user password has expired" -msgstr "用户密码已过期" - -#: authentication/templates/authentication/login.html:57 -#: authentication/templates/authentication/new_login.html:83 +#: authentication/templates/authentication/xpack_login.html:87 msgid "Captcha invalid" msgstr "验证码错误" -#: authentication/templates/authentication/login.html:84 -#: authentication/templates/authentication/new_login.html:105 +#: authentication/templates/authentication/login.html:87 +#: authentication/templates/authentication/xpack_login.html:116 #: users/templates/users/forgot_password.html:10 #: users/templates/users/forgot_password.html:25 msgid "Forgot password" msgstr "忘记密码" -#: authentication/templates/authentication/login.html:91 +#: authentication/templates/authentication/login.html:94 msgid "More login options" msgstr "更多登录方式" -#: authentication/templates/authentication/login.html:95 +#: authentication/templates/authentication/login.html:98 msgid "Keycloak" msgstr "" @@ -2651,24 +2685,40 @@ msgstr "下一步" msgid "Can't provide security? Please contact the administrator!" msgstr "如果不能提供MFA验证码,请联系管理员!" -#: authentication/templates/authentication/new_login.html:67 +#: authentication/templates/authentication/login_wait_confirm.html:47 +msgid "Copy link" +msgstr "复制链接" + +#: authentication/templates/authentication/login_wait_confirm.html:52 +#: templates/flash_message_standalone.html:47 +msgid "Return" +msgstr "返回" + +#: authentication/templates/authentication/xpack_login.html:74 msgid "Welcome back, please enter username and password to login" msgstr "欢迎回来,请输入用户名和密码登录" -#: authentication/views/login.py:81 +#: authentication/views/login.py:70 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: authentication/views/login.py:174 users/views/user.py:393 -#: users/views/user.py:418 -msgid "MFA code invalid, or ntp sync server time" -msgstr "MFA验证码不正确,或者服务器端时间不对" +#: authentication/views/login.py:157 +msgid "" +"Wait for {} confirm, You also can copy link to her/him
\n" +" Don't close this page" +msgstr "" +"等待 {} 确认, 你也可以复制链接发给他/她
\n" +" 不要关闭本页面" -#: authentication/views/login.py:205 +#: authentication/views/login.py:162 +msgid "No ticket found" +msgstr "没有发现工单" + +#: authentication/views/login.py:185 msgid "Logout success" msgstr "退出登录成功" -#: authentication/views/login.py:206 +#: authentication/views/login.py:186 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" @@ -2753,7 +2803,17 @@ msgstr "" #: jumpserver/views.py:233 msgid "Websocket server run on port: {}, you should proxy it on nginx" +msgstr "Websocket 服务运行在端口: {}, 请检查nginx是否代理是否设置" + +#: jumpserver/views.py:241 +msgid "" +"
Koko is a separately deployed program, you need to deploy Koko, " +"configure nginx for url distribution,
If you see this page, " +"prove that you are not accessing the nginx listening port. Good luck.
" msgstr "" +"
Koko是单独部署的一个程序,你需要部署Koko, 并确保nginx配置转发,
如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运" #: ops/api/celery.py:54 msgid "Waiting task start" @@ -2761,7 +2821,7 @@ msgstr "等待任务开始" #: ops/api/command.py:35 msgid "Not has host {} permission" -msgstr "" +msgstr "没有该主机 {} 权限" #: ops/models/adhoc.py:38 msgid "Interval" @@ -2824,67 +2884,67 @@ msgstr "Become" msgid "Create by" msgstr "创建者" -#: ops/models/adhoc.py:252 +#: ops/models/adhoc.py:254 msgid "{} Start task: {}" msgstr "{} 任务开始: {}" -#: ops/models/adhoc.py:264 +#: ops/models/adhoc.py:263 msgid "{} Task finish" msgstr "{} 任务结束" -#: ops/models/adhoc.py:356 +#: ops/models/adhoc.py:360 msgid "Start time" msgstr "开始时间" -#: ops/models/adhoc.py:357 +#: ops/models/adhoc.py:361 msgid "End time" msgstr "完成时间" -#: ops/models/adhoc.py:358 ops/templates/ops/adhoc_history.html:57 +#: ops/models/adhoc.py:362 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 +#: xpack/plugins/gathered_user/models.py:143 msgid "Time" msgstr "时间" -#: ops/models/adhoc.py:359 ops/templates/ops/adhoc_detail.html:106 +#: ops/models/adhoc.py:363 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:360 ops/templates/ops/adhoc_history.html:56 +#: ops/models/adhoc.py:364 ops/templates/ops/adhoc_history.html:56 #: ops/templates/ops/task_history.html:62 msgid "Is success" msgstr "是否成功" -#: ops/models/adhoc.py:361 +#: ops/models/adhoc.py:365 msgid "Adhoc raw result" msgstr "结果" -#: ops/models/adhoc.py:362 +#: ops/models/adhoc.py:366 msgid "Adhoc result summary" msgstr "汇总" -#: ops/models/command.py:22 +#: ops/models/command.py:23 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:56 #: xpack/plugins/cloud/models.py:273 msgid "Result" msgstr "结果" -#: ops/models/command.py:57 +#: ops/models/command.py:58 msgid "Task start" msgstr "任务开始" -#: ops/models/command.py:71 +#: ops/models/command.py:75 msgid "Command `{}` is forbidden ........" msgstr "命令 `{}` 不允许被执行 ......." -#: ops/models/command.py:77 +#: ops/models/command.py:81 msgid "Task end" msgstr "任务结束" @@ -3110,7 +3170,7 @@ msgstr "命令执行列表" msgid "Command execution" msgstr "命令执行" -#: orgs/mixins/models.py:58 orgs/mixins/serializers.py:26 orgs/models.py:31 +#: orgs/mixins/models.py:44 orgs/mixins/serializers.py:26 orgs/models.py:31 msgid "Organization" msgstr "组织" @@ -3130,12 +3190,12 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件" #: perms/forms/asset_permission.py:81 perms/forms/remote_app_permission.py:37 #: perms/models/base.py:50 perms/templates/perms/asset_permission_list.html:51 -#: perms/templates/perms/asset_permission_list.html:71 -#: perms/templates/perms/asset_permission_list.html:118 +#: perms/templates/perms/asset_permission_list.html:109 +#: perms/templates/perms/asset_permission_list.html:211 #: perms/templates/perms/remote_app_permission_list.html:16 #: 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/models/user.py:435 users/templates/users/_select_user_modal.html:16 +#: users/templates/users/user_detail.html:219 #: users/templates/users/user_list.html:38 #: xpack/plugins/orgs/templates/orgs/org_list.html:16 msgid "User group" @@ -3170,13 +3230,14 @@ msgid "Actions" msgstr "动作" #: perms/models/asset_permission.py:87 templates/_nav.html:72 +#: tickets/templates/tickets/ticket_list.html:22 msgid "Asset permission" msgstr "资产授权" #: perms/models/base.py:53 #: perms/templates/perms/asset_permission_detail.html:90 #: perms/templates/perms/remote_app_permission_detail.html:82 -#: users/models/user.py:411 users/templates/users/user_detail.html:107 +#: users/models/user.py:467 users/templates/users/user_detail.html:107 #: users/templates/users/user_profile.html:120 msgid "Date expired" msgstr "失效日期" @@ -3229,7 +3290,7 @@ msgid "Add node to this permission" msgstr "添加节点" #: perms/templates/perms/asset_permission_asset.html:109 -#: users/templates/users/user_detail.html:235 +#: users/templates/users/user_detail.html:236 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:105 msgid "Join" msgstr "加入" @@ -3278,7 +3339,7 @@ msgid "Refresh permission cache" msgstr "刷新授权缓存" #: perms/templates/perms/asset_permission_list.html:55 -#: perms/templates/perms/asset_permission_list.html:69 +#: perms/templates/perms/asset_permission_list.html:209 #: perms/templates/perms/remote_app_permission_list.html:19 #: users/templates/users/user_list.html:40 xpack/plugins/cloud/models.py:74 #: xpack/plugins/cloud/templates/cloud/account_detail.html:58 @@ -3286,7 +3347,7 @@ msgstr "刷新授权缓存" msgid "Validity" msgstr "有效" -#: perms/templates/perms/asset_permission_list.html:244 +#: perms/templates/perms/asset_permission_list.html:232 msgid "Refresh success" msgstr "刷新成功" @@ -3403,31 +3464,31 @@ msgstr "连接LDAP成功" #: settings/api.py:107 msgid "LDAP attr map not valid" -msgstr "LDAP 属性映射无效" +msgstr "" #: settings/api.py:116 msgid "Match {} s users" msgstr "匹配 {} 个用户" -#: settings/api.py:224 +#: settings/api.py:226 msgid "Get ldap users is None" msgstr "获取 LDAP 用户为 None" -#: settings/api.py:231 +#: settings/api.py:233 msgid "Imported {} users successfully" msgstr "导入 {} 个用户成功" -#: settings/api.py:262 settings/api.py:298 +#: settings/api.py:264 settings/api.py:300 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:268 settings/api.py:304 +#: settings/api.py:270 settings/api.py:306 msgid "Create succeed" msgstr "创建成功" -#: settings/api.py:286 settings/api.py:324 +#: settings/api.py:288 settings/api.py:326 #: settings/templates/settings/terminal_setting.html:154 msgid "Delete succeed" msgstr "删除成功" @@ -3635,12 +3696,11 @@ msgstr "" #: settings/forms.py:220 msgid "Connection max idle time" -msgstr "SSH最大空闲时间" +msgstr "连接最大空闲时间" #: settings/forms.py:222 -msgid "" -"If idle time more than it, disconnect connection(only ssh now) Unit: minute" -msgstr "提示:(单位:分)如果超过该配置没有操作,连接会被断开(仅ssh)" +msgid "If idle time more than it, disconnect connection Unit: minute" +msgstr "提示:如果超过该配置没有操作,连接会被断开 (单位:分)" #: settings/forms.py:228 msgid "Password expiration time" @@ -3752,7 +3812,7 @@ 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/models/user.py:431 users/templates/users/user_detail.html:71 #: users/templates/users/user_profile.html:59 msgid "Email" msgstr "邮件" @@ -3952,7 +4012,7 @@ msgstr "在ou:{}中没有匹配条目" #: 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 +#: settings/views.py:160 settings/views.py:187 templates/_nav.html:178 msgid "Settings" msgstr "系统设置" @@ -4145,7 +4205,7 @@ msgstr "终端管理" msgid "Job Center" msgstr "作业中心" -#: templates/_nav.html:116 templates/_nav.html:136 +#: templates/_nav.html:116 templates/_nav.html:144 msgid "Batch command" msgstr "批量命令" @@ -4153,15 +4213,19 @@ msgstr "批量命令" msgid "Task monitor" msgstr "任务监控" -#: templates/_nav.html:146 +#: templates/_nav.html:128 tickets/views.py:19 tickets/views.py:37 +msgid "Tickets" +msgstr "工单管理" + +#: templates/_nav.html:154 msgid "XPack" msgstr "" -#: templates/_nav.html:154 xpack/plugins/cloud/views.py:28 +#: templates/_nav.html:162 xpack/plugins/cloud/views.py:28 msgid "Account list" msgstr "账户列表" -#: templates/_nav.html:155 +#: templates/_nav.html:163 msgid "Sync instance" msgstr "同步实例" @@ -4190,10 +4254,6 @@ msgstr "语言播放验证码" msgid "Captcha" msgstr "验证码" -#: templates/flash_message_standalone.html:47 -msgid "Return" -msgstr "返回" - #: templates/index.html:11 msgid "Total users" msgstr "用户总数" @@ -4507,6 +4567,8 @@ msgid "Accept" msgstr "接受" #: terminal/templates/terminal/terminal_list.html:80 +#: tickets/models/ticket.py:31 tickets/templates/tickets/ticket_detail.html:101 +#: tickets/templates/tickets/ticket_list.html:107 msgid "Reject" msgstr "拒绝" @@ -4543,11 +4605,159 @@ msgid "" "You should use your ssh client tools connect terminal: {}

{}" msgstr "你可以使用ssh客户端工具连接终端" -#: users/api/user.py:173 +#: tickets/models/ticket.py:18 tickets/models/ticket.py:70 +#: tickets/templates/tickets/ticket_list.html:102 +msgid "Open" +msgstr "开启" + +#: tickets/models/ticket.py:19 tickets/templates/tickets/ticket_list.html:103 +msgid "Closed" +msgstr "关闭" + +#: tickets/models/ticket.py:24 +msgid "General" +msgstr "一般" + +#: tickets/models/ticket.py:30 tickets/templates/tickets/ticket_detail.html:100 +#: tickets/templates/tickets/ticket_list.html:106 +msgid "Approve" +msgstr "同意" + +#: tickets/models/ticket.py:34 tickets/models/ticket.py:129 +msgid "User display name" +msgstr "用户显示名称" + +#: tickets/models/ticket.py:36 tickets/templates/tickets/ticket_list.html:33 +#: tickets/templates/tickets/ticket_list.html:99 +msgid "Title" +msgstr "标题" + +#: tickets/models/ticket.py:37 tickets/models/ticket.py:130 +msgid "Body" +msgstr "内容" + +#: tickets/models/ticket.py:38 +msgid "Meta" +msgstr "" + +#: tickets/models/ticket.py:39 tickets/templates/tickets/ticket_detail.html:51 +msgid "Assignee" +msgstr "处理人" + +#: tickets/models/ticket.py:40 +msgid "Assignee display name" +msgstr "处理人名称" + +#: tickets/models/ticket.py:41 tickets/templates/tickets/ticket_detail.html:50 +msgid "Assignees" +msgstr "待处理人" + +#: tickets/models/ticket.py:42 +msgid "Assignees display name" +msgstr "待处理人名称" + +#: tickets/models/ticket.py:71 +msgid "{} {} this ticket" +msgstr "{} {} 这个工单" + +#: tickets/models/ticket.py:82 +msgid "this ticket" +msgstr "这个工单" + +#: tickets/templates/tickets/ticket_detail.html:66 +#: tickets/templates/tickets/ticket_detail.html:80 +msgid "ago" +msgstr "前" + +#: tickets/templates/tickets/ticket_list.html:9 +msgid "My tickets" +msgstr "我的工单" + +#: tickets/templates/tickets/ticket_list.html:10 +msgid "Assigned me" +msgstr "待处理" + +#: tickets/templates/tickets/ticket_list.html:19 +msgid "Create ticket" +msgstr "提交工单" + +#: tickets/utils.py:18 +msgid "New ticket" +msgstr "新工单" + +#: tickets/utils.py:21 +#, python-brace-format +msgid "" +"\n" +"
\n" +"

Your has a new ticket

\n" +"
\n" +" {body}\n" +"
\n" +" click here to review \n" +"
\n" +"
\n" +" " +msgstr "" +"\n" +"
\n" +"

你有一个新工单

\n" +"
\n" +" {body}\n" +"
\n" +" 点击我查看 \n" +"
\n" +"
\n" +" " + +#: tickets/utils.py:40 +msgid "Ticket has been reply" +msgstr "工单已被回复" + +#: tickets/utils.py:41 +#, python-brace-format +msgid "" +"\n" +"
\n" +"

Your ticket has been replay

\n" +"
\n" +" Title: {ticket.title}\n" +"
\n" +" Assignee: {ticket.assignee_display}\n" +"
\n" +" Status: {ticket.status_display}\n" +"
\n" +"
\n" +"
\n" +" " +msgstr "" +"\n" +"
\n" +"

您的工单已被回复

\n" +"
\n" +" 标题: {ticket.title}\n" +"
\n" +" 处理人: {ticket.assignee_display}\n" +"
\n" +" 状态: {ticket.status_display}\n" +"
\n" +"
\n" +"
\n" +" " + +#: tickets/views.py:20 +msgid "Ticket list" +msgstr "工单列表" + +#: tickets/views.py:38 +msgid "Ticket detail" +msgstr "工单详情" + +#: users/api/user.py:174 msgid "Could not reset self otp, use profile reset instead" msgstr "不能再该页面重置MFA, 请去个人信息页面重置" -#: users/forms.py:47 users/models/user.py:383 +#: users/forms.py:47 users/models/user.py:439 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:87 #: users/templates/users/user_list.html:37 @@ -4555,7 +4765,7 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置" msgid "Role" msgstr "角色" -#: users/forms.py:51 users/models/user.py:418 +#: users/forms.py:51 users/models/user.py:474 #: users/templates/users/user_detail.html:103 #: users/templates/users/user_list.html:39 #: users/templates/users/user_profile.html:102 @@ -4575,7 +4785,7 @@ msgstr "" msgid "Paste user id_rsa.pub here." msgstr "复制用户公钥到这里" -#: users/forms.py:71 users/templates/users/user_detail.html:226 +#: users/forms.py:71 users/templates/users/user_detail.html:227 msgid "Join user groups" msgstr "添加到用户组" @@ -4583,7 +4793,7 @@ msgstr "添加到用户组" msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms.py:110 users/forms.py:271 users/serializers/user.py:110 +#: users/forms.py:110 users/forms.py:271 users/serializers/user.py:122 msgid "Not a valid ssh public key" msgstr "ssh密钥不合法" @@ -4599,8 +4809,8 @@ msgstr "生成重置密码链接,通过邮件发送给用户" msgid "Set password" msgstr "设置密码" -#: 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 +#: 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:49 #: 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 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:16 @@ -4668,49 +4878,53 @@ msgstr "复制你的公钥到这里" msgid "Select users" msgstr "选择用户" -#: users/models/user.py:50 users/templates/users/user_update.html:22 +#: users/models/user.py:51 users/templates/users/user_update.html:22 #: users/views/login.py:46 users/views/login.py:107 msgid "User auth from {}, go there change password" msgstr "用户认证源来自 {}, 请去相应系统修改密码" -#: users/models/user.py:126 users/models/user.py:508 +#: users/models/user.py:147 users/models/user.py:564 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:128 +#: users/models/user.py:149 msgid "Application" msgstr "应用程序" -#: users/models/user.py:129 xpack/plugins/orgs/forms.py:30 +#: users/models/user.py:150 xpack/plugins/orgs/forms.py:30 #: xpack/plugins/orgs/templates/orgs/org_list.html:14 msgid "Auditor" msgstr "审计员" -#: users/models/user.py:139 +#: users/models/user.py:160 msgid "Org admin" msgstr "组织管理员" -#: users/models/user.py:141 +#: users/models/user.py:162 msgid "Org auditor" msgstr "组织审计员" -#: users/models/user.py:332 users/templates/users/user_profile.html:90 +#: users/models/user.py:353 users/templates/users/user_profile.html:90 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:386 +#: users/models/user.py:419 +msgid "Local" +msgstr "数据库" + +#: users/models/user.py:442 msgid "Avatar" msgstr "头像" -#: users/models/user.py:389 users/templates/users/user_detail.html:82 +#: users/models/user.py:445 users/templates/users/user_detail.html:82 msgid "Wechat" msgstr "微信" -#: users/models/user.py:422 +#: users/models/user.py:478 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:511 +#: users/models/user.py:567 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -4718,42 +4932,42 @@ msgstr "Administrator是初始的超级管理员" msgid "Auditors cannot be join in the user group" msgstr "审计员不能被加入到用户组" -#: users/serializers/user.py:40 -msgid "Groups name" -msgstr "用户组名" - -#: users/serializers/user.py:41 -msgid "Source name" -msgstr "用户来源名" - -#: users/serializers/user.py:42 +#: users/serializers/user.py:35 msgid "Is first login" msgstr "首次登录" -#: users/serializers/user.py:43 -msgid "Role name" -msgstr "角色名" - -#: users/serializers/user.py:44 +#: users/serializers/user.py:36 msgid "Is valid" msgstr "账户是否有效" -#: users/serializers/user.py:45 +#: users/serializers/user.py:37 msgid "Is expired" msgstr " 是否过期" -#: users/serializers/user.py:46 +#: users/serializers/user.py:38 msgid "Avatar url" msgstr "头像路径" -#: users/serializers/user.py:66 +#: users/serializers/user.py:46 msgid "Role limit to {}" msgstr "角色只能为 {}" -#: users/serializers/user.py:78 +#: users/serializers/user.py:58 msgid "Password does not match security rules" msgstr "密码不满足安全规则" +#: users/serializers/user.py:107 +msgid "Groups name" +msgstr "用户组名" + +#: users/serializers/user.py:108 +msgid "Source name" +msgstr "用户来源名" + +#: users/serializers/user.py:109 +msgid "Role name" +msgstr "角色名" + #: users/serializers_v2/user.py:36 msgid "name not unique" msgstr "名称重复" @@ -4882,7 +5096,7 @@ msgid "Always young, always with tears in my eyes. Stay foolish Stay hungry" msgstr "永远年轻,永远热泪盈眶 stay foolish stay hungry" #: users/templates/users/reset_password.html:46 -#: users/templates/users/user_detail.html:379 users/utils.py:83 +#: users/templates/users/user_detail.html:433 users/utils.py:83 msgid "Reset password" msgstr "重置密码" @@ -4981,72 +5195,72 @@ msgstr "最后更新密码" msgid "Force enabled MFA" msgstr "强制启用MFA" -#: users/templates/users/user_detail.html:175 +#: users/templates/users/user_detail.html:177 msgid "Reset MFA" msgstr "重置MFA" -#: users/templates/users/user_detail.html:184 +#: users/templates/users/user_detail.html:186 msgid "Send reset password mail" msgstr "发送重置密码邮件" -#: users/templates/users/user_detail.html:187 -#: users/templates/users/user_detail.html:197 +#: users/templates/users/user_detail.html:189 +#: users/templates/users/user_detail.html:199 msgid "Send" msgstr "发送" -#: users/templates/users/user_detail.html:194 +#: users/templates/users/user_detail.html:196 msgid "Send reset ssh key mail" msgstr "发送重置密钥邮件" -#: users/templates/users/user_detail.html:203 -#: users/templates/users/user_detail.html:467 +#: users/templates/users/user_detail.html:205 +#: users/templates/users/user_detail.html:521 msgid "Unblock user" msgstr "解除登录限制" -#: users/templates/users/user_detail.html:206 +#: users/templates/users/user_detail.html:208 msgid "Unblock" msgstr "解除" -#: users/templates/users/user_detail.html:322 +#: users/templates/users/user_detail.html:376 msgid "Goto profile page enable MFA" msgstr "请去个人信息页面启用自己的MFA" -#: users/templates/users/user_detail.html:378 +#: users/templates/users/user_detail.html:432 msgid "An e-mail has been sent to the user`s mailbox." msgstr "已发送邮件到用户邮箱" -#: users/templates/users/user_detail.html:389 +#: users/templates/users/user_detail.html:443 msgid "This will reset the user password and send a reset mail" msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱" -#: users/templates/users/user_detail.html:404 +#: users/templates/users/user_detail.html:458 msgid "" "The reset-ssh-public-key E-mail has been sent successfully. Please inform " "the user to update his new ssh public key." msgstr "重设密钥邮件将会发送到用户邮箱" -#: users/templates/users/user_detail.html:405 +#: users/templates/users/user_detail.html:459 msgid "Reset SSH public key" msgstr "重置SSH密钥" -#: users/templates/users/user_detail.html:415 +#: users/templates/users/user_detail.html:469 msgid "This will reset the user public key and send a reset mail" msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱" -#: users/templates/users/user_detail.html:433 +#: users/templates/users/user_detail.html:487 msgid "Successfully updated the SSH public key." msgstr "更新ssh密钥成功" -#: users/templates/users/user_detail.html:434 -#: users/templates/users/user_detail.html:438 +#: users/templates/users/user_detail.html:488 +#: users/templates/users/user_detail.html:492 msgid "User SSH public key update" msgstr "ssh密钥" -#: users/templates/users/user_detail.html:483 +#: users/templates/users/user_detail.html:537 msgid "After unlocking the user, the user can log in normally." msgstr "解除用户登录限制后,此用户即可正常登录" -#: users/templates/users/user_detail.html:497 +#: users/templates/users/user_detail.html:551 msgid "Reset user MFA success" msgstr "重置用户MFA成功" @@ -5161,13 +5375,13 @@ msgid "Set MFA" msgstr "设置MFA" #: users/templates/users/user_profile.html:178 -msgid "Update password" -msgstr "更改密码" - -#: users/templates/users/user_profile.html:188 msgid "Update MFA" msgstr "更改MFA" +#: users/templates/users/user_profile.html:188 +msgid "Update password" +msgstr "更改密码" + #: users/templates/users/user_profile.html:198 msgid "Update SSH public key" msgstr "更改SSH密钥" @@ -5469,19 +5683,19 @@ msgstr "密钥更新" msgid "Password invalid" msgstr "用户名或密码无效" -#: users/views/user.py:448 +#: users/views/user.py:453 msgid "MFA enable success" msgstr "MFA 绑定成功" -#: users/views/user.py:449 +#: users/views/user.py:454 msgid "MFA enable success, return login page" msgstr "MFA 绑定成功,返回到登录页面" -#: users/views/user.py:451 +#: users/views/user.py:456 msgid "MFA disable success" msgstr "MFA 解绑成功" -#: users/views/user.py:452 +#: users/views/user.py:457 msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" @@ -5490,7 +5704,7 @@ msgid "Password length" msgstr "密码长度" #: xpack/plugins/change_auth_plan/forms.py:75 -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:60 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:58 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:81 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:17 #: xpack/plugins/cloud/forms.py:33 xpack/plugins/cloud/forms.py:87 @@ -5529,8 +5743,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 @@ -5541,20 +5755,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 @@ -5563,8 +5777,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 @@ -5573,37 +5787,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 "连接超时" @@ -5643,7 +5857,7 @@ msgstr "添加资产" msgid "Add node to this plan" msgstr "添加节点" -#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:12 +#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:11 msgid "" "When the user password on the asset is changed, the action is performed " "using the admin user associated with the asset" @@ -5953,19 +6167,19 @@ msgid "Periodic" msgstr "定时执行" #: xpack/plugins/gathered_user/models.py:57 -#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:48 +#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:38 msgid "Gather user task" msgstr "收集用户任务" -#: xpack/plugins/gathered_user/models.py:140 +#: xpack/plugins/gathered_user/models.py:137 msgid "Task" msgstr "任务" -#: xpack/plugins/gathered_user/models.py:152 +#: xpack/plugins/gathered_user/models.py:149 msgid "gather user task execution" msgstr "收集用户执行" -#: xpack/plugins/gathered_user/models.py:158 +#: xpack/plugins/gathered_user/models.py:155 msgid "Assets is empty, please change nodes" msgstr "资产为空,请更改节点" @@ -6213,14 +6427,142 @@ 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 "Assigned ticket" +#~ msgstr "处理人" + +#~ msgid "My ticket" +#~ msgstr "我的工单" + +#~ msgid "User login confirm: {}" +#~ msgstr "用户登录复核: {}" + +#~ msgid "" +#~ "User: {}\n" +#~ "IP: {}\n" +#~ "City: {}\n" +#~ "Date: {}\n" +#~ msgstr "" +#~ "用户: {}\n" +#~ "IP: {}\n" +#~ "城市: {}\n" +#~ "日期: {}\n" + +#~ msgid "this order" +#~ msgstr "这个工单" + +#~ msgid "Approve selected" +#~ msgstr "同意所选" + +#~ msgid "Reject selected" +#~ msgstr "拒绝所选" + +#~ msgid "" +#~ "\n" +#~ "
\n" +#~ "

Your has a new ticket

\n" +#~ "
\n" +#~ " Title: {ticket.title}\n" +#~ "
\n" +#~ " User: {user}\n" +#~ "
\n" +#~ " Assignees: {ticket.assignees_display}\n" +#~ "
\n" +#~ " City: {ticket.city}\n" +#~ "
\n" +#~ " IP: {ticket.ip}\n" +#~ "
\n" +#~ " click here to review \n" +#~ "
\n" +#~ "
\n" +#~ " " +#~ msgstr "" +#~ "\n" +#~ "
\n" +#~ "

您有一个新工单

\n" +#~ "
\n" +#~ " 标题: {ticket.title}\n" +#~ "
\n" +#~ " 用户: {user}\n" +#~ "
\n" +#~ " 待处理人: {ticket.assignees_display}\n" +#~ "
\n" +#~ " 城市: {ticket.city}\n" +#~ "
\n" +#~ " IP: {ticket.ip}\n" +#~ "
\n" +#~ " 点我查看 \n" +#~ "
\n" +#~ "
\n" +#~ " " + +#~ msgid "Login confirm ticket list" +#~ msgstr "登录复核工单列表" + +#~ msgid "Login confirm ticket detail" +#~ msgstr "登录复核工单详情" + +#, fuzzy +#~| msgid "Login" +#~ msgid "Login IP" +#~ msgstr "登录" + +#~ msgid "succeed: {} failed: {} total: {}" +#~ msgstr "成功:{} 失败:{} 总数:{}" + +#~ msgid "The user source is not LDAP" +#~ msgstr "用户来源不是LDAP" + +#~ msgid "selected" +#~ msgstr "所选" + +#~ msgid "not found" +#~ msgstr "没有发现" + +#~ msgid "Log in frequently and try again later" +#~ msgstr "登录频繁, 稍后重试" + +#~ msgid "Please carry seed value and conduct MFA secondary certification" +#~ msgstr "请携带seed值, 进行MFA二次认证" + +#~ msgid "Please verify the user name and password first" +#~ msgstr "请先进行用户名和密码验证" + +#~ msgid "MFA certification failed" +#~ msgstr "MFA认证失败" + +#~ msgid "Accepted" +#~ msgstr "已接受" + +#~ msgid "New order" +#~ msgstr "新工单" + +#~ msgid "Orders" +#~ msgstr "工单管理" + +#~ msgid "" +#~ "The username or password you entered is incorrect, please enter it again." +#~ msgstr "您输入的用户名或密码不正确,请重新输入。" + +#~ msgid "" +#~ "You can also try {times_try} times (The account will be temporarily " +#~ "locked for {block_time} minutes)" +#~ msgstr "您还可以尝试 {times_try} 次(账号将被临时锁定 {block_time} 分钟)" + +#~ msgid "No order found or order expired" +#~ msgstr "没有找到工单,或者已过期" + +#~ msgid "Order was rejected by {}" +#~ msgstr "工单被拒绝 {}" + +#~ msgid "login_confirm_setting" +#~ msgstr "登录复核设置" + +#~ msgid "The user password has expired" +#~ msgstr "用户密码已过期" + #~ msgid "Recipient" #~ msgstr "收件人" @@ -6387,9 +6729,6 @@ msgstr "创建" #~ msgid "Start" #~ msgstr "开始" -#~ msgid "User login settings" -#~ msgstr "用户登录设置" - #~ msgid "Bit" #~ msgstr " 位" diff --git a/apps/ops/models/adhoc.py b/apps/ops/models/adhoc.py index b9c1c4a74..3eebb6636 100644 --- a/apps/ops/models/adhoc.py +++ b/apps/ops/models/adhoc.py @@ -246,30 +246,35 @@ class AdHoc(models.Model): time_start = time.time() date_start = timezone.now() is_success = False + summary = {} + raw = '' try: date_start_s = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(_("{} Start task: {}").format(date_start_s, self.task.name)) raw, summary = self._run_only() is_success = summary.get('success', False) - return raw, summary except Exception as e: logger.error(e, exc_info=True) - summary = {} raw = {"dark": {"all": str(e)}, "contacted": []} - return raw, summary finally: date_end = timezone.now() date_end_s = date_end.strftime('%Y-%m-%d %H:%M:%S') print(_("{} Task finish").format(date_end_s)) print('.\n\n.') + try: + summary_text = json.dumps(summary) + except json.JSONDecodeError: + summary_text = '{}' AdHocRunHistory.objects.filter(id=history.id).update( date_start=date_start, is_finished=True, is_success=is_success, date_finished=timezone.now(), - timedelta=time.time() - time_start + timedelta=time.time() - time_start, + _summary=summary_text ) + return raw, summary def _run_only(self): Task.objects.filter(id=self.task.id).update(date_updated=timezone.now()) @@ -321,10 +326,9 @@ class AdHoc(models.Model): except AdHocRunHistory.DoesNotExist: return None - def save(self, force_insert=False, force_update=False, using=None, - update_fields=None): - super().save(force_insert=force_insert, force_update=force_update, - using=using, update_fields=update_fields) + def save(self, **kwargs): + instance = super().save(**kwargs) + return instance def __str__(self): return "{} of {}".format(self.task.name, self.short_id) @@ -393,7 +397,10 @@ class AdHocRunHistory(models.Model): @summary.setter def summary(self, item): - self._summary = json.dumps(item) + try: + self._summary = json.dumps(item) + except json.JSONDecodeError: + self._summary = json.dumps({}) @property def success_hosts(self): diff --git a/apps/ops/models/command.py b/apps/ops/models/command.py index dfa1dbe4d..3df42d5bc 100644 --- a/apps/ops/models/command.py +++ b/apps/ops/models/command.py @@ -3,6 +3,7 @@ import uuid import json +from celery.exceptions import SoftTimeLimitExceeded from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext @@ -64,6 +65,9 @@ class CommandExecution(models.Model): try: result = runner.execute(self.command, 'all') self.result = result.results_command + except SoftTimeLimitExceeded as e: + print("Run timeout than 60s") + self.result = {"error": str(e)} except Exception as e: print("Error occur: {}".format(e)) self.result = {"error": str(e)} diff --git a/apps/orgs/mixins/models.py b/apps/orgs/mixins/models.py index 4ffa24c2a..30429b874 100644 --- a/apps/orgs/mixins/models.py +++ b/apps/orgs/mixins/models.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # -import traceback from django.db import models from django.utils.translation import ugettext_lazy as _ from django.core.exceptions import ValidationError @@ -9,7 +8,7 @@ from django.core.exceptions import ValidationError from common.utils import get_logger from ..utils import ( set_current_org, get_current_org, current_org, - get_org_filters + filter_org_queryset ) from ..models import Organization @@ -20,17 +19,18 @@ __all__ = [ ] -class OrgQuerySet(models.QuerySet): - pass - - class OrgManager(models.Manager): + def get_queryset(self): - queryset = super().get_queryset() - kwargs = get_org_filters() - if kwargs: - return queryset.filter(**kwargs) - return queryset + queryset = super(OrgManager, self).get_queryset() + return filter_org_queryset(queryset) + + def all(self): + if not current_org: + msg = 'You can `objects.set_current_org(org).all()` then run it' + return self + else: + return super(OrgManager, self).all() def set_current_org(self, org): if isinstance(org, str): @@ -38,20 +38,6 @@ class OrgManager(models.Manager): set_current_org(org) return self - def all(self): - # print("Call all: {}".format(current_org)) - # - # lines = traceback.format_stack() - # print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>") - # for line in lines[-10:-1]: - # print(line) - # print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<") - if not current_org: - msg = 'You can `objects.set_current_org(org).all()` then run it' - return self - else: - return super().all() - class OrgModelMixin(models.Model): org_id = models.CharField(max_length=36, blank=True, default='', diff --git a/apps/orgs/models.py b/apps/orgs/models.py index f337864fc..544e50712 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -4,7 +4,7 @@ from django.conf import settings from django.db import models from django.utils.translation import ugettext_lazy as _ -from common.utils import is_uuid +from common.utils import is_uuid, lazyproperty class Organization(models.Model): @@ -72,7 +72,8 @@ class Organization(models.Model): org = cls.default() if default else None return org - def get_org_users(self): + @lazyproperty + def org_users(self): from users.models import User if self.is_real(): return self.users.all() @@ -81,18 +82,29 @@ class Organization(models.Model): users = users.filter(related_user_orgs__isnull=True) return users - def get_org_admins(self): + def get_org_users(self): + return self.org_users + + @lazyproperty + def org_admins(self): from users.models import User if self.is_real(): return self.admins.all() return User.objects.filter(role=User.ROLE_ADMIN) - def get_org_auditors(self): + def get_org_admins(self): + return self.org_admins + + @lazyproperty + def org_auditors(self): from users.models import User if self.is_real(): return self.auditors.all() return User.objects.filter(role=User.ROLE_AUDITOR) + def get_org_auditors(self): + return self.org_auditors + def get_org_members(self, exclude=()): from users.models import User members = User.objects.none() diff --git a/apps/perms/templates/perms/asset_permission_create_update.html b/apps/perms/templates/perms/asset_permission_create_update.html index 5e4c650d0..0c05de831 100644 --- a/apps/perms/templates/perms/asset_permission_create_update.html +++ b/apps/perms/templates/perms/asset_permission_create_update.html @@ -100,16 +100,6 @@ diff --git a/apps/templates/_foot_js.html b/apps/templates/_foot_js.html index f9cfbba59..509b23ad3 100644 --- a/apps/templates/_foot_js.html +++ b/apps/templates/_foot_js.html @@ -9,7 +9,7 @@ - \ No newline at end of file + diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 5690c961d..03f9d83cb 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -121,6 +121,14 @@ {% endif %} +{% if request.user.can_admin_current_org and LOGIN_CONFIRM_ENABLE and LICENSE_VALID %} +
  • + + + {% trans 'Tickets' %} + +
  • +{% endif %} {# Audits #} {% if request.user.can_admin_or_audit_current_org %} @@ -175,4 +183,4 @@ \ No newline at end of file + diff --git a/apps/templates/_user_profile.html b/apps/templates/_user_profile.html index 248aa1d5a..dcab1c1a3 100644 --- a/apps/templates/_user_profile.html +++ b/apps/templates/_user_profile.html @@ -19,7 +19,8 @@ -
    {% if request.user.can_admin_current_org %} + {% if user_object.can_user_current_org or user_object.can_admin_current_org %}
    @@ -220,38 +223,78 @@
    - - - - - - - - + + + + + + + + - {% for group in user_object.groups.all %} - - - - - {% endfor %} + {% for group in user_object.groups.all %} + + + + + {% endfor %}
    - -
    - -
    + +
    + +
    - {{ group.name }} - - -
    + {{ group.name }} + + +
    {% endif %} + + {% if LOGIN_CONFIRM_ENABLE %} +
    +
    + {% trans 'Login confirm' %} +
    +
    + + + + + + + + + + + {% if user_object.get_login_confirm_setting %} + {% for u in user_object.login_confirm_setting.reviewers.all %} + + + + + {% endfor %} + {% endif %} + +
    + +
    + +
    + {{ u }} + + +
    +
    +
    + {% endif %} + {% endif %}
    @@ -264,6 +307,7 @@ {% block custom_foot_js %} {% endblock %} diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index cd9273681..265d4a4db 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -26,7 +26,7 @@ {% endblock %} {% block table_container %}
    {% trans "Create user" %}
    - +
    + {% if request.user.mfa_enabled %} + + + + + {% endif %} {% if request.user.can_update_password %} @@ -183,16 +193,6 @@ {% endif %} - {% if request.user.otp_enabled and request.user.otp_secret_key %} - - - - - {% endif %} {% if request.user.can_update_ssh_key %} diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py index 27d5fd5fa..f0fe3db6f 100644 --- a/apps/users/urls/api_urls.py +++ b/apps/users/urls/api_urls.py @@ -14,14 +14,12 @@ app_name = 'users' router = BulkRouter() router.register(r'users', api.UserViewSet, 'user') router.register(r'groups', api.UserGroupViewSet, 'user-group') +router.register(r'users-groups-relations', api.UserUserGroupRelationViewSet, 'user-group-relation') urlpatterns = [ path('connection-token/', auth_api.UserConnectionTokenApi.as_view(), name='connection-token'), - path('auth/', auth_api.UserAuthApi.as_view(), name='user-auth'), - path('otp/auth/', auth_api.UserOtpAuthApi.as_view(), name='user-otp-auth'), - path('profile/', api.UserProfileApi.as_view(), name='user-profile'), path('otp/reset/', api.UserResetOTPApi.as_view(), name='my-otp-reset'), path('users//otp/reset/', api.UserResetOTPApi.as_view(), name='user-reset-otp'), diff --git a/apps/users/utils.py b/apps/users/utils.py index 30868358c..a955350a9 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -193,7 +193,6 @@ def send_reset_ssh_key_mail(user): send_mail_async.delay(subject, message, recipient_list, html_message=message) - def get_user_or_tmp_user(request): user = request.user tmp_user = get_tmp_user_from_cache(request) @@ -212,16 +211,18 @@ def get_tmp_user_from_cache(request): return user -def set_tmp_user_to_cache(request, user): - cache.set(request.session.session_key+'user', user, 600) +def set_tmp_user_to_cache(request, user, ttl=3600): + cache.set(request.session.session_key+'user', user, ttl) def redirect_user_first_login_or_index(request, redirect_field_name): if request.user.is_first_login: return reverse('users:user-first-login') - return request.POST.get( - redirect_field_name, - request.GET.get(redirect_field_name, reverse('index'))) + url_in_post = request.POST.get(redirect_field_name) + if url_in_post: + return url_in_post + url_in_get = request.GET.get(redirect_field_name, reverse('index')) + return url_in_get def generate_otp_uri(request, issuer="Jumpserver"): diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 3f0f88e8f..52a721374 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -170,12 +170,12 @@ class UserFirstLoginView(PermissionsMixin, SessionWizardView): form.instance = self.request.user if isinstance(form, forms.UserMFAForm): - choices = form.fields["otp_level"].choices - if self.request.user.otp_force_enabled: + choices = form.fields["mfa_level"].choices + if self.request.user.mfa_force_enabled: choices = [(k, v) for k, v in choices if k == 2] else: choices = [(k, v) for k, v in choices if k in [0, 1]] - form.fields["otp_level"].choices = choices - form.fields["otp_level"].initial = self.request.user.otp_level + form.fields["mfa_level"].choices = choices + form.fields["mfa_level"].initial = self.request.user.mfa_level return form diff --git a/apps/users/views/user.py b/apps/users/views/user.py index 36922f47e..6063c55e9 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -347,7 +347,12 @@ class UserOtpEnableAuthenticationView(FormView): if not user: form.add_error("password", _("Password invalid")) return self.form_invalid(form) - return redirect(self.get_success_url()) + if user.mfa_is_otp(): + return redirect(self.get_success_url()) + else: + user.enable_mfa() + user.save() + return redirect('users:user-otp-settings-success') def get_success_url(self): return reverse('users:user-otp-enable-install-app') @@ -395,7 +400,7 @@ class UserOtpEnableBindView(TemplateView, FormView): def save_otp(self, otp_secret_key): user = get_user_or_tmp_user(self.request) - user.enable_otp() + user.enable_mfa() user.otp_secret_key = otp_secret_key user.save() @@ -411,7 +416,7 @@ class UserOtpDisableAuthenticationView(FormView): otp_secret_key = user.otp_secret_key if check_otp_code(otp_secret_key, otp_code): - user.disable_otp() + user.disable_mfa() user.save() return super().form_valid(form) else: @@ -447,7 +452,7 @@ class UserOtpSettingsSuccessView(TemplateView): auth_logout(self.request) title = _('MFA enable success') describe = _('MFA enable success, return login page') - if not user.otp_enabled: + if not user.mfa_enabled: title = _('MFA disable success') describe = _('MFA disable success, return login page')
    @@ -124,7 +124,7 @@ function initTable() { {data: "role"}, {data: "groups_display", orderable: false}, {data: "source"}, - {data: "is_valid", orderable: false}, + {data: "is_valid", orderable: false, width: "50px"}, {data: "id", orderable: false, width: "100px"} ], op_html: $('#actions').html() diff --git a/apps/users/templates/users/user_profile.html b/apps/users/templates/users/user_profile.html index 9c6644baa..8c9088504 100644 --- a/apps/users/templates/users/user_profile.html +++ b/apps/users/templates/users/user_profile.html @@ -86,9 +86,9 @@
    {% trans 'MFA certification' %} - {% if user.otp_force_enabled %} + {% if user.mfa_force_enabled %} {% trans 'Force enable' %} - {% elif user.otp_enabled%} + {% elif user.mfa_enabled%} {% trans 'Enable' %} {% else %} {% trans 'Disable' %} @@ -158,8 +158,8 @@ {% trans 'Disable' %} {% else %} {% url 'users:user-otp-disable-authentication' %} @@ -173,6 +173,16 @@
    {% trans 'Update MFA' %}: + + {% trans 'Update' %} + +
    {% trans 'Update password' %}:
    {% trans 'Update MFA' %}: - - {% trans 'Update' %} - -
    {% trans 'Update SSH public key' %}: