From 6035d1f130efaa3ba845c6620876c64343bba615 Mon Sep 17 00:00:00 2001 From: chnliyong Date: Sat, 10 Mar 2018 10:46:28 +0800 Subject: [PATCH 01/34] python-gssapi libkrb5-dev for gssapi/gssapi.h --- requirements/deb_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/deb_requirements.txt b/requirements/deb_requirements.txt index a0ddb7642..f4131a3ea 100644 --- a/requirements/deb_requirements.txt +++ b/requirements/deb_requirements.txt @@ -1 +1 @@ -libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite gcc automake +libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.5-dev tk8.5-dev python-tk python-dev openssl libssl-dev libldap2-dev libsasl2-dev sqlite gcc automake libkrb5-dev From 4fd83bd5be712dbe4b152137f052d50245062323 Mon Sep 17 00:00:00 2001 From: Nidhoggur1993 Date: Tue, 3 Apr 2018 16:02:51 +0800 Subject: [PATCH 02/34] Update make_migrations.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加自动合并而不是在弹出warning结束 --- utils/make_migrations.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/make_migrations.sh b/utils/make_migrations.sh index fdf1f6efb..b6068a1ca 100755 --- a/utils/make_migrations.sh +++ b/utils/make_migrations.sh @@ -4,3 +4,5 @@ python3 ../apps/manage.py makemigrations python3 ../apps/manage.py migrate + +python3 ../apps/manage.py makemigrations –merge From 7c35e75586c098cab51b457a0d4d7f54c17718ba Mon Sep 17 00:00:00 2001 From: wojiushixiaobai <296015668@qq.com> Date: Wed, 25 Apr 2018 19:49:22 +0800 Subject: [PATCH 03/34] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/upgrade.sh | 49 +++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/utils/upgrade.sh b/utils/upgrade.sh index 878bf770a..e1cdabb76 100644 --- a/utils/upgrade.sh +++ b/utils/upgrade.sh @@ -1,24 +1,14 @@ #!/bin/bash -if [ ! -d "/opt/py3" ]; then -echo -e "\033[31m python3虚拟环境不是默认路径 \033[0m" -ps -ef | grep jumpserver/tmp/beat.pid | grep -v grep -if [ $? -ne 0 ] -then -echo -e "\033[31m jumpserver未运行,请到jumpserver目录使用 ./jms start all -d 启动 \033[0m" -exit 0 -else -echo -e "\033[31m 正在计算python3虚拟环境路径 \033[0m" -fi -py3pid=`ps -ef | grep jumpserver/tmp/beat.pid | grep -v grep | awk '{print $2}'` -py3file=`cat /proc/$py3pid/cmdline` -py3even=`echo ${py3file%/bin/python3*}` -echo -e "\033[31m python3虚拟环境路径为$py3even \033[0m" -source $py3even/bin/activate +if grep -q 'source ~/.autoenv/activate.sh' ~/.bashrc; then + echo -e "\033[31m 正在自动载入 python 环境 \033[0m" else -source /opt/py3/bin/activate + echo -e "\033[31m 不支持自动升级,请参考 http://docs.jumpserver.org/zh/docs/upgrade.html 手动升级 \033[0m" + exit 0 fi +source ~/.bashrc + cd `dirname $0`/ && cd .. && ./jms stop jumpserver_backup=/tmp/jumpserver_backup$(date -d "today" +"%Y%m%d_%H%M%S") @@ -29,21 +19,20 @@ echo -e "\033[31m 是否需要备份Jumpserver数据库 \033[0m" stty erase ^H read -p "确认备份请按Y,否则按其他键跳过备份 " a if [ "$a" == y -o "$a" == Y ];then -echo -e "\033[31m 正在备份数据库 \033[0m" -echo -e "\033[31m 请手动输入数据库信息 \033[0m" -read -p '请输入Jumpserver数据库ip:' DB_HOST -read -p '请输入Jumpserver数据库端口:' DB_PORT -read -p '请输入Jumpserver数据库名称:' DB_NAME -read -p '请输入有权限导出数据库的用户:' DB_USER -read -p '请输入该用户的密码:' DB_PASSWORD -mysqldump -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASSWORD $DB_NAME > /$jumpserver_backup/$DB_NAME$(date -d "today" +"%Y%m%d_%H%M%S").sql || { -echo -e "\033[31m 备份数据库失败,请检查输入是否有误 \033[0m" -exit 1 -} -echo -e "\033[31m 备份数据库完成 \033[0m" - + echo -e "\033[31m 正在备份数据库 \033[0m" + echo -e "\033[31m 请手动输入数据库信息 \033[0m" + read -p '请输入Jumpserver数据库ip:' DB_HOST + read -p '请输入Jumpserver数据库端口:' DB_PORT + read -p '请输入Jumpserver数据库名称:' DB_NAME + read -p '请输入有权限导出数据库的用户:' DB_USER + read -p '请输入该用户的密码:' DB_PASSWORD + mysqldump -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASSWORD $DB_NAME > /$jumpserver_backup/$DB_NAME$(date -d "today" +"%Y%m%d_%H%M%S").sql || { + echo -e "\033[31m 备份数据库失败,请检查输入是否有误 \033[0m" + exit 1 + } + echo -e "\033[31m 备份数据库完成 \033[0m" else -echo -e "\033[31m 已取消备份数据库操作 \033[0m" + echo -e "\033[31m 已取消备份数据库操作 \033[0m" fi git pull && pip install -r requirements/requirements.txt && cd utils && sh make_migrations.sh From a2f23e96819a755d5e2d31a3f43c5299b9681e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E5=BF=97=E8=89=B3?= Date: Fri, 8 Jun 2018 11:01:07 +0800 Subject: [PATCH 04/34] =?UTF-8?q?=E6=96=B0=E5=A2=9ERedis=20DB=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/settings.py | 6 ++++-- config_example.py | 24 +++++++++++++----------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 6fd9f0fdd..c8ed1505e 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -343,10 +343,11 @@ if AUTH_LDAP: AUTHENTICATION_BACKENDS.insert(0, AUTH_LDAP_BACKEND) # Celery using redis as broker -CELERY_BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/3' % { +CELERY_BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/%(db)s' % { 'password': CONFIG.REDIS_PASSWORD if CONFIG.REDIS_PASSWORD else '', 'host': CONFIG.REDIS_HOST or '127.0.0.1', 'port': CONFIG.REDIS_PORT or 6379, + 'db':CONFIG.REDIS_DB_CELERY_BROKER or 3, } CELERY_TASK_SERIALIZER = 'pickle' CELERY_RESULT_SERIALIZER = 'pickle' @@ -367,10 +368,11 @@ CELERY_WORKER_HIJACK_ROOT_LOGGER = False CACHES = { 'default': { 'BACKEND': 'redis_cache.RedisCache', - 'LOCATION': 'redis://:%(password)s@%(host)s:%(port)s/4' % { + 'LOCATION': 'redis://:%(password)s@%(host)s:%(port)s/%(db)s' % { 'password': CONFIG.REDIS_PASSWORD if CONFIG.REDIS_PASSWORD else '', 'host': CONFIG.REDIS_HOST or '127.0.0.1', 'port': CONFIG.REDIS_PORT or 6379, + 'db':CONFIG.REDIS_DB_CACHE or 4, } } } diff --git a/config_example.py b/config_example.py index 0c8d87094..a96f0d7c9 100644 --- a/config_example.py +++ b/config_example.py @@ -21,10 +21,10 @@ class Config: ALLOWED_HOSTS = ['*'] # Development env open this, when error occur display the full process track, Production disable it - DEBUG = True + DEBUG = os.environ.get("DEBUG") or True # DEBUG, INFO, WARNING, ERROR, CRITICAL can set. See https://docs.djangoproject.com/en/1.10/topics/logging/ - LOG_LEVEL = 'DEBUG' + LOG_LEVEL = os.environ.get("LOG_LEVEL") or 'DEBUG' LOG_DIR = os.path.join(BASE_DIR, 'logs') # Database setting, Support sqlite3, mysql, postgres .... @@ -35,12 +35,12 @@ class Config: DB_NAME = os.path.join(BASE_DIR, 'data', 'db.sqlite3') # MySQL or postgres setting like: - # DB_ENGINE = 'mysql' - # DB_HOST = '127.0.0.1' - # DB_PORT = 3306 - # DB_USER = 'root' - # DB_PASSWORD = '' - # DB_NAME = 'jumpserver' + # DB_ENGINE = os.environ.get("DB_ENGINE") or 'mysql' + # DB_HOST = os.environ.get("DB_HOST") or '127.0.0.1' + # DB_PORT = os.environ.get("DB_PORT") or 3306 + # DB_USER = os.environ.get("DB_USER") or 'jumpserver' + # DB_PASSWORD = os.environ.get("DB_PASSWORD") or 'weakPassword' + # DB_NAME = os.environ.get("DB_NAME") or 'jumpserver' # When Django start it will bind this host and port # ./manage.py runserver 127.0.0.1:8080 @@ -48,9 +48,11 @@ class Config: HTTP_LISTEN_PORT = 8080 # Use Redis as broker for celery and web socket - REDIS_HOST = '127.0.0.1' - REDIS_PORT = 6379 - REDIS_PASSWORD = '' + REDIS_HOST = os.environ.get("REDIS_HOST") or '127.0.0.1' + REDIS_PORT = os.environ.get("REDIS_PORT") or 6379 + REDIS_PASSWORD = os.environ.get("REDIS_PASSWORD") or '' + REDIS_DB_CELERY = os.environ.get('REDIS_DB') or 3 + REDIS_DB_CACHE = os.environ.get('REDIS_DB') or 4 def __init__(self): pass From dd07fa678f05d022da23cc1539cd93f6222effc3 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Jun 2018 16:27:40 +0800 Subject: [PATCH 05/34] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E7=B3=BB=E7=BB=9F=E7=94=A8=E6=88=B7=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E7=99=BB=E5=BD=95=E6=A8=A1=E5=BC=8F(=E8=87=AA?= =?UTF-8?q?=E5=8A=A8/=E6=89=8B=E5=8A=A8=E7=99=BB=E5=BD=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/forms/user.py | 21 +- apps/assets/models/base.py | 2 +- apps/assets/models/user.py | 8 + apps/assets/serializers/system_user.py | 7 + .../assets/templates/assets/_system_user.html | 36 ++- .../templates/assets/system_user_detail.html | 4 + .../templates/assets/system_user_list.html | 11 +- .../templates/assets/system_user_update.html | 1 - apps/i18n/zh/LC_MESSAGES/django.mo | Bin 34591 -> 35051 bytes apps/i18n/zh/LC_MESSAGES/django.po | 240 ++++++++++-------- 10 files changed, 208 insertions(+), 122 deletions(-) diff --git a/apps/assets/forms/user.py b/apps/assets/forms/user.py index 2295dc005..0bc4ff094 100644 --- a/apps/assets/forms/user.py +++ b/apps/assets/forms/user.py @@ -109,12 +109,24 @@ class SystemUserForm(PasswordAndKeyAuthForm): if not self.instance and not auto_generate: super().validate_password_key() + def is_valid(self): + validated = super().is_valid() + username = self.cleaned_data.get('username') + login_mode = self.cleaned_data.get('login_mode') + if login_mode == SystemUser.AUTO_LOGIN and not username: + self.add_error( + "username", _('* Automatic login mode,' + ' must fill in the username.') + ) + return False + return validated + class Meta: model = SystemUser fields = [ 'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'auto_push', 'sudo', - 'comment', 'shell', 'priority', + 'comment', 'shell', 'priority', 'login_mode', ] widgets = { 'name': forms.TextInput(attrs={'placeholder': _('Name')}), @@ -124,5 +136,8 @@ class SystemUserForm(PasswordAndKeyAuthForm): 'name': '* required', 'username': '* required', 'auto_push': _('Auto push system user to asset'), - 'priority': _('High level will be using login asset as default, if user was granted more than 2 system user'), - } \ No newline at end of file + 'priority': _('High level will be using login asset as default, ' + 'if user was granted more than 2 system user'), + 'login_mode': _('If you choose manual login mode, you do not ' + 'need to fill in the username and password.') + } diff --git a/apps/assets/models/base.py b/apps/assets/models/base.py index cb9bb96ae..908e6b647 100644 --- a/apps/assets/models/base.py +++ b/apps/assets/models/base.py @@ -19,7 +19,7 @@ signer = get_signer() class AssetUser(models.Model): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) - username = models.CharField(max_length=32, verbose_name=_('Username'), validators=[alphanumeric]) + username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric]) _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) _private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ]) _public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key')) diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index bf31b8491..f835e387f 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -100,6 +100,13 @@ class SystemUser(AssetUser): (RDP_PROTOCOL, 'rdp'), ) + AUTO_LOGIN = 'auto' + MANUAL_LOGIN = 'manual' + LOGIN_MODE_CHOICES = ( + (AUTO_LOGIN, _('Automatic login')), + (MANUAL_LOGIN, _('Manually login')) + ) + nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes")) assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets")) priority = models.IntegerField(default=10, verbose_name=_("Priority")) @@ -107,6 +114,7 @@ class SystemUser(AssetUser): auto_push = models.BooleanField(default=True, verbose_name=_('Auto push')) sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo')) shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell')) + login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=AUTO_LOGIN, max_length=10, verbose_name=_('Login mode')) def __str__(self): return '{0.name}({0.username})'.format(self) diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 7abd09d29..9caf150b4 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -18,6 +18,13 @@ class SystemUserSerializer(serializers.ModelSerializer): model = SystemUser exclude = ('_password', '_private_key', '_public_key') + def get_field_names(self, declared_fields, info): + fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info) + fields.extend([ + 'get_login_mode_display', + ]) + return fields + @staticmethod def get_unreachable_assets(obj): return obj.unreachable_assets diff --git a/apps/assets/templates/assets/_system_user.html b/apps/assets/templates/assets/_system_user.html index 314967d22..cf8539293 100644 --- a/apps/assets/templates/assets/_system_user.html +++ b/apps/assets/templates/assets/_system_user.html @@ -36,12 +36,13 @@ {% endif %}

{% trans 'Basic' %}

{% bootstrap_field form.name layout="horizontal" %} + {% bootstrap_field form.login_mode layout="horizontal" %} {% bootstrap_field form.username layout="horizontal" %} {% bootstrap_field form.priority layout="horizontal" %} {% bootstrap_field form.protocol layout="horizontal" %} +

{% trans 'Auth' %}

{% block auth %} -

{% trans 'Auth' %}

@@ -80,15 +81,22 @@ {% endblock %} {% block custom_foot_js %} {% endblock %} \ No newline at end of file diff --git a/apps/assets/templates/assets/system_user_detail.html b/apps/assets/templates/assets/system_user_detail.html index 9a7bc255a..b68bf2689 100644 --- a/apps/assets/templates/assets/system_user_detail.html +++ b/apps/assets/templates/assets/system_user_detail.html @@ -62,6 +62,10 @@ {% trans 'Username' %}: {{ system_user.username }} + + {% trans 'Login mode' %}: + {{ system_user.get_login_mode_display }} + {% trans 'Protocol' %}: {{ system_user.protocol }} diff --git a/apps/assets/templates/assets/system_user_list.html b/apps/assets/templates/assets/system_user_list.html index 3daa47a81..3fa887df6 100644 --- a/apps/assets/templates/assets/system_user_list.html +++ b/apps/assets/templates/assets/system_user_list.html @@ -26,6 +26,7 @@ {% trans 'Name' %} {% trans 'Username' %} {% trans 'Protocol' %} + {% trans 'Login mode' %} {% trans 'Asset' %} {% trans 'Reachable' %} {% trans 'Unreachable' %} @@ -48,7 +49,7 @@ function initTable() { var detail_btn = '' + cellData + ''; $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); }}, - {targets: 5, createdCell: function (td, cellData) { + {targets: 6, createdCell: function (td, cellData) { var innerHtml = ""; if (cellData !== 0) { innerHtml = "" + cellData + ""; @@ -57,7 +58,7 @@ function initTable() { } $(td).html('' + innerHtml + ''); }}, - {targets: 6, createdCell: function (td, cellData) { + {targets: 7, createdCell: function (td, cellData) { var innerHtml = ""; if (cellData !== 0) { innerHtml = "" + cellData + ""; @@ -66,7 +67,7 @@ function initTable() { } $(td).html('' + innerHtml + ''); }}, - {targets: 7, createdCell: function (td, cellData, rowData) { + {targets: 8, createdCell: function (td, cellData, rowData) { var val = 0; var innerHtml = ""; var total = rowData.assets_amount; @@ -84,14 +85,14 @@ function initTable() { $(td).html('' + innerHtml + ''); }}, - {targets: 9, createdCell: function (td, cellData, rowData) { + {targets: 10, createdCell: function (td, cellData, rowData) { var update_btn = '{% trans "Update" %}'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '{% trans "Delete" %}'.replace('{{ DEFAULT_PK }}', cellData); $(td).html(update_btn + del_btn) }}], ajax_url: '{% url "api-assets:system-user-list" %}', columns: [ - {data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "assets_amount" }, + {data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "get_login_mode_display"}, {data: "assets_amount" }, {data: "reachable_amount"}, {data: "unreachable_amount"}, {data: "id"}, {data: "comment" }, {data: "id" } ], op_html: $('#actions').html() diff --git a/apps/assets/templates/assets/system_user_update.html b/apps/assets/templates/assets/system_user_update.html index 7e1590db5..977c3ac31 100644 --- a/apps/assets/templates/assets/system_user_update.html +++ b/apps/assets/templates/assets/system_user_update.html @@ -4,7 +4,6 @@ {% load bootstrap3 %} {% block auth %} -

{% trans 'Auth' %}

{% bootstrap_field form.password layout="horizontal" %} {% bootstrap_field form.private_key_file layout="horizontal" %}
diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index 7d2f547cd060a8f9bcb6920ecac28ce737ebe3df..df59e43d67e7b758c445eae254496702a7583bf4 100644 GIT binary patch delta 12416 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 O?Aehr#tdDS=YIeSRQ&V+ delta 11980 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 PjvW!~lig\n" "Language-Team: Jumpserver team\n" @@ -29,10 +29,10 @@ msgstr "" msgid "测试节点下资产是否可连接: {}" msgstr "" -#: assets/forms/asset.py:24 assets/models/asset.py:75 assets/models/user.py:103 +#: assets/forms/asset.py:24 assets/models/asset.py:75 assets/models/user.py:110 #: assets/templates/assets/asset_detail.html:183 #: assets/templates/assets/asset_detail.html:191 -#: assets/templates/assets/system_user_detail.html:175 perms/models.py:33 +#: assets/templates/assets/system_user_detail.html:179 perms/models.py:33 msgid "Nodes" msgstr "节点管理" @@ -103,7 +103,7 @@ msgstr "端口" #: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_list.html:15 #: assets/templates/assets/label_list.html:16 -#: assets/templates/assets/system_user_list.html:29 audits/models.py:11 +#: assets/templates/assets/system_user_list.html:30 audits/models.py:11 #: audits/templates/audits/ftp_log_list.html:41 #: audits/templates/audits/ftp_log_list.html:72 perms/forms.py:37 #: perms/models.py:32 @@ -118,7 +118,7 @@ msgstr "端口" msgid "Asset" msgstr "资产" -#: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:120 +#: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:131 #: assets/models/base.py:21 assets/models/cluster.py:18 #: assets/models/domain.py:17 assets/models/group.py:20 #: assets/models/label.py:17 assets/templates/assets/admin_user_detail.html:56 @@ -147,15 +147,15 @@ msgstr "资产" msgid "Name" msgstr "名称" -#: assets/forms/domain.py:55 assets/forms/user.py:80 assets/forms/user.py:121 +#: assets/forms/domain.py:55 assets/forms/user.py:80 assets/forms/user.py:132 #: assets/models/base.py:22 assets/templates/assets/admin_user_detail.html:60 #: assets/templates/assets/admin_user_list.html:24 #: assets/templates/assets/domain_gateway_list.html:60 #: assets/templates/assets/system_user_detail.html:62 #: assets/templates/assets/system_user_list.html:27 #: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13 -#: users/forms.py:21 users/forms.py:30 users/models/authentication.py:45 -#: users/models/user.py:47 users/templates/users/_select_user_modal.html:14 +#: users/forms.py:31 users/models/authentication.py:45 users/models/user.py:47 +#: users/templates/users/_select_user_modal.html:14 #: users/templates/users/login.html:56 #: users/templates/users/login_log_list.html:49 #: users/templates/users/user_detail.html:67 @@ -169,7 +169,7 @@ msgid "Password or private key passphrase" msgstr "密码或密钥密码" #: assets/forms/user.py:25 assets/models/base.py:23 common/forms.py:113 -#: users/forms.py:15 users/forms.py:23 users/forms.py:32 users/forms.py:44 +#: users/forms.py:15 users/forms.py:33 users/forms.py:45 #: users/templates/users/login.html:59 #: users/templates/users/reset_password.html:53 #: users/templates/users/user_create.html:10 @@ -192,16 +192,26 @@ msgstr "ssh密钥不合法" msgid "Password and private key file must be input one" msgstr "密码和私钥, 必须输入一个" -#: assets/forms/user.py:126 +#: assets/forms/user.py:118 +msgid "* Automatic login mode, must fill in the username." +msgstr "自动登录模式,必须填写用户名" + +#: assets/forms/user.py:137 msgid "Auto push system user to asset" msgstr "自动推送系统用户到资产" -#: assets/forms/user.py:127 +#: assets/forms/user.py:138 msgid "" "High level will be using login asset as default, if user was granted more " "than 2 system user" msgstr "高优先级的系统用户将会作为默认登录用户" +#: assets/forms/user.py:139 +msgid "" +"If you choose manual login mode, you do not need to fill in the username and " +"password." +msgstr "如果选择手动登录模式,用户名和密码则不需要填写" + #: assets/models/asset.py:63 assets/models/domain.py:43 #: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/admin_user_assets.html:52 @@ -309,7 +319,7 @@ msgstr "标签管理" #: assets/templates/assets/admin_user_detail.html:68 #: assets/templates/assets/asset_detail.html:117 #: assets/templates/assets/domain_detail.html:72 -#: assets/templates/assets/system_user_detail.html:96 +#: assets/templates/assets/system_user_detail.html:100 #: ops/templates/ops/adhoc_detail.html:86 perms/models.py:38 perms/models.py:81 #: perms/templates/perms/asset_permission_detail.html:98 #: users/models/user.py:90 users/templates/users/user_detail.html:111 @@ -320,7 +330,7 @@ msgstr "创建者" #: assets/models/domain.py:20 assets/models/group.py:22 #: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:64 #: assets/templates/assets/domain_detail.html:68 -#: assets/templates/assets/system_user_detail.html:92 +#: assets/templates/assets/system_user_detail.html:96 #: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:63 #: perms/models.py:39 perms/models.py:82 #: perms/templates/perms/asset_permission_detail.html:94 @@ -338,8 +348,8 @@ msgstr "创建日期" #: assets/templates/assets/domain_detail.html:76 #: assets/templates/assets/domain_gateway_list.html:61 #: assets/templates/assets/domain_list.html:17 -#: assets/templates/assets/system_user_detail.html:100 -#: assets/templates/assets/system_user_list.html:33 common/models.py:30 +#: assets/templates/assets/system_user_detail.html:104 +#: assets/templates/assets/system_user_list.html:34 common/models.py:30 #: ops/models/adhoc.py:42 perms/models.py:40 perms/models.py:83 #: perms/templates/perms/asset_permission_detail.html:102 terminal/models.py:26 #: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:13 @@ -392,7 +402,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:13 -#: users/models/user.py:343 +#: users/models/user.py:345 msgid "System" msgstr "系统" @@ -404,9 +414,9 @@ msgstr "默认Cluster" msgid "Cluster" msgstr "集群" -#: assets/models/domain.py:45 assets/models/user.py:106 +#: assets/models/domain.py:45 assets/models/user.py:113 #: assets/templates/assets/domain_gateway_list.html:59 -#: assets/templates/assets/system_user_detail.html:66 +#: assets/templates/assets/system_user_detail.html:70 #: assets/templates/assets/system_user_list.html:28 #: terminal/templates/terminal/session_list.html:75 msgid "Protocol" @@ -431,10 +441,10 @@ msgstr "默认资产组" #: terminal/templates/terminal/command_list.html:32 #: terminal/templates/terminal/command_list.html:72 #: terminal/templates/terminal/session_list.html:33 -#: terminal/templates/terminal/session_list.html:71 users/forms.py:281 -#: users/models/user.py:31 users/models/user.py:331 +#: terminal/templates/terminal/session_list.html:71 users/forms.py:282 +#: users/models/user.py:31 users/models/user.py:333 #: users/templates/users/user_group_detail.html:78 -#: users/templates/users/user_group_list.html:13 users/views/user.py:362 +#: users/templates/users/user_group_list.html:13 users/views/user.py:359 msgid "User" msgstr "用户" @@ -451,7 +461,15 @@ msgstr "分类" msgid "Key" msgstr "" -#: assets/models/user.py:104 +#: assets/models/user.py:106 +msgid "Automatic login" +msgstr "自动登录" + +#: assets/models/user.py:107 +msgid "Manually login" +msgstr "手动登录" + +#: assets/models/user.py:111 #: assets/templates/assets/_asset_group_bulk_update_modal.html:11 #: assets/templates/assets/system_user_asset.html:21 #: assets/views/admin_user.py:29 assets/views/admin_user.py:47 @@ -469,25 +487,30 @@ msgstr "" msgid "Assets" msgstr "资产管理" -#: assets/models/user.py:105 +#: assets/models/user.py:112 msgid "Priority" msgstr "优先级" -#: assets/models/user.py:107 assets/templates/assets/_system_user.html:58 -#: assets/templates/assets/system_user_detail.html:118 -#: assets/templates/assets/system_user_update.html:11 +#: assets/models/user.py:114 assets/templates/assets/_system_user.html:59 +#: assets/templates/assets/system_user_detail.html:122 +#: assets/templates/assets/system_user_update.html:10 msgid "Auto push" msgstr "自动推送" -#: assets/models/user.py:108 assets/templates/assets/system_user_detail.html:70 +#: assets/models/user.py:115 assets/templates/assets/system_user_detail.html:74 msgid "Sudo" msgstr "Sudo" -#: assets/models/user.py:109 assets/templates/assets/system_user_detail.html:75 +#: assets/models/user.py:116 assets/templates/assets/system_user_detail.html:79 msgid "Shell" msgstr "Shell" -#: assets/models/user.py:149 audits/models.py:12 +#: assets/models/user.py:117 assets/templates/assets/system_user_detail.html:66 +#: assets/templates/assets/system_user_list.html:29 +msgid "Login mode" +msgstr "登录模式" + +#: assets/models/user.py:157 audits/models.py:12 #: audits/templates/audits/ftp_log_list.html:49 #: audits/templates/audits/ftp_log_list.html:73 perms/forms.py:43 #: perms/models.py:34 perms/models.py:78 @@ -604,16 +627,15 @@ msgstr "基本" #: assets/templates/assets/asset_create.html:25 #: assets/templates/assets/asset_update.html:30 #: assets/templates/assets/gateway_create_update.html:45 -#: assets/templates/assets/system_user_update.html:7 #: users/templates/users/_user.html:21 msgid "Auth" msgstr "认证" -#: assets/templates/assets/_system_user.html:47 +#: assets/templates/assets/_system_user.html:48 msgid "Auto generate key" msgstr "自动生成密钥" -#: assets/templates/assets/_system_user.html:64 +#: assets/templates/assets/_system_user.html:65 #: assets/templates/assets/asset_create.html:59 #: assets/templates/assets/asset_update.html:63 #: assets/templates/assets/gateway_create_update.html:53 @@ -622,7 +644,7 @@ msgstr "自动生成密钥" msgid "Other" msgstr "其它" -#: assets/templates/assets/_system_user.html:70 +#: assets/templates/assets/_system_user.html:71 #: assets/templates/assets/admin_user_create_update.html:45 #: assets/templates/assets/asset_bulk_update.html:23 #: assets/templates/assets/asset_create.html:66 @@ -647,7 +669,7 @@ msgstr "其它" msgid "Reset" msgstr "重置" -#: assets/templates/assets/_system_user.html:71 +#: assets/templates/assets/_system_user.html:72 #: assets/templates/assets/admin_user_create_update.html:46 #: assets/templates/assets/asset_bulk_update.html:24 #: assets/templates/assets/asset_create.html:67 @@ -702,14 +724,14 @@ msgstr "资产列表" #: assets/templates/assets/admin_user_assets.html:54 #: assets/templates/assets/admin_user_list.html:26 #: assets/templates/assets/system_user_asset.html:52 -#: assets/templates/assets/system_user_list.html:30 +#: assets/templates/assets/system_user_list.html:31 #: users/templates/users/user_group_granted_asset.html:47 msgid "Reachable" msgstr "可连接" #: assets/templates/assets/admin_user_assets.html:66 #: assets/templates/assets/system_user_asset.html:64 -#: assets/templates/assets/system_user_detail.html:112 +#: assets/templates/assets/system_user_detail.html:116 #: perms/templates/perms/asset_permission_detail.html:114 msgid "Quick update" msgstr "快速更新" @@ -722,7 +744,7 @@ msgstr "测试可连接性" #: assets/templates/assets/admin_user_assets.html:75 #: assets/templates/assets/asset_detail.html:171 #: assets/templates/assets/system_user_asset.html:81 -#: assets/templates/assets/system_user_detail.html:147 +#: assets/templates/assets/system_user_detail.html:151 msgid "Test" msgstr "测试" @@ -736,7 +758,7 @@ msgstr "测试" #: assets/templates/assets/domain_list.html:42 #: assets/templates/assets/label_list.html:38 #: assets/templates/assets/system_user_detail.html:26 -#: assets/templates/assets/system_user_list.html:88 +#: assets/templates/assets/system_user_list.html:89 #: perms/templates/perms/asset_permission_detail.html:30 #: perms/templates/perms/asset_permission_list.html:191 #: terminal/templates/terminal/terminal_detail.html:16 @@ -760,7 +782,7 @@ msgstr "更新" #: assets/templates/assets/domain_list.html:43 #: assets/templates/assets/label_list.html:39 #: assets/templates/assets/system_user_detail.html:30 -#: assets/templates/assets/system_user_list.html:89 +#: assets/templates/assets/system_user_list.html:90 #: ops/templates/ops/task_list.html:72 #: perms/templates/perms/asset_permission_detail.html:34 #: perms/templates/perms/asset_permission_list.html:192 @@ -785,8 +807,8 @@ msgstr "选择节点" #: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/asset_detail.html:200 #: assets/templates/assets/asset_list.html:638 -#: assets/templates/assets/system_user_detail.html:192 -#: assets/templates/assets/system_user_list.html:138 templates/_modal.html:22 +#: assets/templates/assets/system_user_detail.html:196 +#: assets/templates/assets/system_user_list.html:139 templates/_modal.html:22 #: terminal/templates/terminal/session_detail.html:108 #: users/templates/users/user_detail.html:366 #: users/templates/users/user_detail.html:391 @@ -804,12 +826,12 @@ msgid "Create admin user" msgstr "创建管理用户" #: assets/templates/assets/admin_user_list.html:27 -#: assets/templates/assets/system_user_list.html:31 +#: assets/templates/assets/system_user_list.html:32 msgid "Unreachable" msgstr "不可达" #: assets/templates/assets/admin_user_list.html:28 -#: assets/templates/assets/system_user_list.html:32 +#: assets/templates/assets/system_user_list.html:33 #: ops/templates/ops/adhoc_history.html:54 #: ops/templates/ops/task_history.html:60 msgid "Ratio" @@ -820,7 +842,7 @@ msgstr "比例" #: assets/templates/assets/domain_gateway_list.html:62 #: assets/templates/assets/domain_list.html:18 #: assets/templates/assets/label_list.html:17 -#: assets/templates/assets/system_user_list.html:34 +#: assets/templates/assets/system_user_list.html:35 #: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64 #: ops/templates/ops/task_history.html:65 ops/templates/ops/task_list.html:42 #: perms/templates/perms/asset_permission_list.html:60 @@ -978,7 +1000,7 @@ msgid "Have assets, cancel" msgstr "存在资产,不能删除" #: assets/templates/assets/asset_list.html:633 -#: assets/templates/assets/system_user_list.html:133 +#: assets/templates/assets/system_user_list.html:134 #: users/templates/users/user_detail.html:361 #: users/templates/users/user_detail.html:386 #: users/templates/users/user_group_list.html:81 @@ -1053,17 +1075,17 @@ msgid "Assets of " msgstr "资产" #: assets/templates/assets/system_user_asset.html:70 -#: assets/templates/assets/system_user_detail.html:135 +#: assets/templates/assets/system_user_detail.html:139 msgid "Push system user now" msgstr "立刻推送系统" #: assets/templates/assets/system_user_asset.html:73 -#: assets/templates/assets/system_user_detail.html:138 +#: assets/templates/assets/system_user_detail.html:142 msgid "Push" msgstr "推送" #: assets/templates/assets/system_user_asset.html:78 -#: assets/templates/assets/system_user_detail.html:144 +#: assets/templates/assets/system_user_detail.html:148 msgid "Test assets connective" msgstr "测试资产可连接性" @@ -1075,28 +1097,28 @@ msgstr "任务已下发,查看ops任务列表" msgid "Task has been send, seen left assets status" msgstr "任务已下发,查看左侧资产状态" -#: assets/templates/assets/system_user_detail.html:81 +#: assets/templates/assets/system_user_detail.html:85 msgid "Home" msgstr "家目录" -#: assets/templates/assets/system_user_detail.html:87 +#: assets/templates/assets/system_user_detail.html:91 msgid "Uid" msgstr "Uid" -#: assets/templates/assets/system_user_detail.html:153 -#: assets/templates/assets/system_user_detail.html:339 +#: assets/templates/assets/system_user_detail.html:157 +#: assets/templates/assets/system_user_detail.html:343 msgid "Clear auth" msgstr "清除认证信息" -#: assets/templates/assets/system_user_detail.html:156 +#: assets/templates/assets/system_user_detail.html:160 msgid "Clear" msgstr "清除" -#: assets/templates/assets/system_user_detail.html:183 +#: assets/templates/assets/system_user_detail.html:187 msgid "Add to node" msgstr "添加到节点" -#: assets/templates/assets/system_user_detail.html:339 +#: assets/templates/assets/system_user_detail.html:343 msgid "success" msgstr "成功" @@ -1105,20 +1127,20 @@ msgstr "成功" msgid "Create system user" msgstr "创建系统用户" -#: assets/templates/assets/system_user_list.html:134 +#: assets/templates/assets/system_user_list.html:135 msgid "This will delete the selected System Users !!!" msgstr "删除选择系统用户" -#: assets/templates/assets/system_user_list.html:142 +#: assets/templates/assets/system_user_list.html:143 msgid "System Users Deleted." msgstr "已被删除" -#: assets/templates/assets/system_user_list.html:143 -#: assets/templates/assets/system_user_list.html:148 +#: assets/templates/assets/system_user_list.html:144 +#: assets/templates/assets/system_user_list.html:149 msgid "System Users Delete" msgstr "删除系统用户" -#: assets/templates/assets/system_user_list.html:147 +#: assets/templates/assets/system_user_list.html:148 msgid "System Users Deleting failed." msgstr "系统用户删除失败" @@ -1805,8 +1827,8 @@ msgstr "任务列表" msgid "Task run history" msgstr "执行历史" -#: perms/forms.py:18 users/forms.py:238 users/forms.py:243 users/forms.py:255 -#: users/forms.py:285 +#: perms/forms.py:18 users/forms.py:239 users/forms.py:244 users/forms.py:256 +#: users/forms.py:286 msgid "Select users" msgstr "选择用户" @@ -1958,14 +1980,14 @@ msgstr "商业支持" msgid "Docs" msgstr "文档" -#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:121 +#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:122 #: users/templates/users/_user.html:39 #: users/templates/users/first_login.html:39 #: users/templates/users/user_password_update.html:39 #: users/templates/users/user_profile.html:17 #: users/templates/users/user_profile_update.html:37 #: users/templates/users/user_profile_update.html:57 -#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:344 +#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:341 msgid "Profile" msgstr "个人信息" @@ -2022,13 +2044,13 @@ msgstr "关闭" #: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44 #: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95 -#: users/views/login.py:277 users/views/login.py:335 users/views/user.py:66 -#: users/views/user.py:81 users/views/user.py:103 users/views/user.py:174 -#: users/views/user.py:329 users/views/user.py:381 users/views/user.py:416 +#: users/views/login.py:277 users/views/login.py:335 users/views/user.py:65 +#: users/views/user.py:80 users/views/user.py:102 users/views/user.py:173 +#: users/views/user.py:328 users/views/user.py:378 users/views/user.py:413 msgid "Users" msgstr "用户管理" -#: templates/_nav.html:13 users/views/user.py:67 +#: templates/_nav.html:13 users/views/user.py:66 msgid "User list" msgstr "用户列表" @@ -2334,11 +2356,11 @@ msgstr "" msgid "Invalid token or cache refreshed." msgstr "" -#: users/forms.py:38 +#: users/forms.py:39 msgid "MFA code" msgstr "MFA 验证码" -#: users/forms.py:49 users/models/user.py:59 +#: users/forms.py:50 users/models/user.py:59 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:87 #: users/templates/users/user_list.html:25 @@ -2346,31 +2368,31 @@ msgstr "MFA 验证码" msgid "Role" msgstr "角色" -#: users/forms.py:52 users/forms.py:201 +#: users/forms.py:53 users/forms.py:202 msgid "ssh public key" msgstr "ssh公钥" -#: users/forms.py:53 users/forms.py:202 +#: users/forms.py:54 users/forms.py:203 msgid "ssh-rsa AAAA..." msgstr "" -#: users/forms.py:54 +#: users/forms.py:55 msgid "Paste user id_rsa.pub here." msgstr "复制用户公钥到这里" -#: users/forms.py:72 users/templates/users/user_detail.html:200 +#: users/forms.py:73 users/templates/users/user_detail.html:200 msgid "Join user groups" msgstr "添加到用户组" -#: users/forms.py:83 users/forms.py:216 +#: users/forms.py:84 users/forms.py:217 msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms.py:87 users/forms.py:220 users/serializers.py:48 +#: users/forms.py:88 users/forms.py:221 users/serializers.py:48 msgid "Not a valid ssh public key" msgstr "ssh密钥不合法" -#: users/forms.py:127 +#: users/forms.py:128 msgid "" "Tip: when enabled, you will enter the MFA binding process the next time you " "log in. you can also directly bind in \"personal information -> quick " @@ -2379,16 +2401,16 @@ msgstr "" "提示:启用之后您将会在下次登录时进入MFA绑定流程;您也可以在(个人信息->快速修" "改->更改MFA设置)中直接绑定!" -#: users/forms.py:137 +#: users/forms.py:138 msgid "* Enable MFA authentication to make the account more secure." msgstr "* 启用MFA认证,使账号更加安全." -#: users/forms.py:142 users/models/user.py:71 +#: users/forms.py:143 users/models/user.py:71 #: users/templates/users/first_login.html:45 msgid "MFA" msgstr "MFA" -#: users/forms.py:147 +#: users/forms.py:148 msgid "" "In order to protect you and your company, please keep your account, password " "and key sensitive information properly. (for example: setting complex " @@ -2397,41 +2419,41 @@ msgstr "" "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:" "设置复杂密码,启用MFA认证)" -#: users/forms.py:154 users/templates/users/first_login.html:48 +#: users/forms.py:155 users/templates/users/first_login.html:48 #: users/templates/users/first_login.html:107 #: users/templates/users/first_login.html:130 msgid "Finish" msgstr "完成" -#: users/forms.py:160 +#: users/forms.py:161 msgid "Old password" msgstr "原来密码" -#: users/forms.py:165 +#: users/forms.py:166 msgid "New password" msgstr "新密码" -#: users/forms.py:170 +#: users/forms.py:171 msgid "Confirm password" msgstr "确认密码" -#: users/forms.py:180 +#: users/forms.py:181 msgid "Old password error" msgstr "原来密码错误" -#: users/forms.py:188 +#: users/forms.py:189 msgid "Password does not match" msgstr "密码不一致" -#: users/forms.py:199 +#: users/forms.py:200 msgid "Automatically configure and download the SSH key" msgstr "自动配置并下载SSH密钥" -#: users/forms.py:203 +#: users/forms.py:204 msgid "Paste your id_rsa.pub here." msgstr "复制你的公钥到这里" -#: users/forms.py:231 users/models/user.py:79 +#: users/forms.py:232 users/models/user.py:79 #: users/templates/users/first_login.html:42 #: users/templates/users/user_password_update.html:45 #: users/templates/users/user_profile.html:68 @@ -2464,7 +2486,7 @@ msgstr "Agent" msgid "Date login" msgstr "登录日期" -#: users/models/user.py:30 users/models/user.py:339 +#: users/models/user.py:30 users/models/user.py:341 msgid "Administrator" msgstr "管理员" @@ -2506,7 +2528,7 @@ msgstr "微信" msgid "Source" msgstr "用户来源" -#: users/models/user.py:342 +#: users/models/user.py:344 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -2649,7 +2671,7 @@ msgid "Setting" msgstr "设置" #: users/templates/users/user_create.html:4 -#: users/templates/users/user_list.html:16 users/views/user.py:81 +#: users/templates/users/user_list.html:16 users/views/user.py:80 msgid "Create user" msgstr "创建用户" @@ -2658,7 +2680,7 @@ msgid "Reset link will be generated and sent to the user. " msgstr "生成重置密码连接,通过邮件发送给用户" #: users/templates/users/user_detail.html:19 -#: users/templates/users/user_granted_asset.html:18 users/views/user.py:175 +#: users/templates/users/user_granted_asset.html:18 users/views/user.py:174 msgid "User detail" msgstr "用户详情" @@ -2793,8 +2815,8 @@ msgstr "用户删除失败" msgid "Administrator Settings force MFA login" msgstr "管理员设置强制使用MFA登录" -#: users/templates/users/user_profile.html:116 users/views/user.py:204 -#: users/views/user.py:258 +#: users/templates/users/user_profile.html:116 users/views/user.py:203 +#: users/views/user.py:257 msgid "User groups" msgstr "用户组" @@ -2840,7 +2862,7 @@ msgid "" "corresponding private key." msgstr "新的公钥已设置成功,请下载对应的私钥" -#: users/templates/users/user_update.html:4 users/views/user.py:104 +#: users/templates/users/user_update.html:4 users/views/user.py:103 msgid "Update user" msgstr "更新用户" @@ -2998,7 +3020,7 @@ msgstr "用户组授权资产" msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: users/views/login.py:128 users/views/user.py:501 users/views/user.py:526 +#: users/views/login.py:128 users/views/user.py:498 users/views/user.py:523 msgid "MFA code invalid" msgstr "MFA码认证失败" @@ -3039,7 +3061,7 @@ msgstr "Token错误或失效" msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:239 users/views/user.py:116 users/views/user.py:399 +#: users/views/login.py:239 users/views/user.py:115 users/views/user.py:396 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" @@ -3051,46 +3073,46 @@ msgstr "首次登陆" msgid "Login log list" msgstr "登录日志" -#: users/views/user.py:128 +#: users/views/user.py:127 msgid "Bulk update user success" msgstr "批量更新用户成功" -#: users/views/user.py:233 +#: users/views/user.py:232 msgid "Invalid file." msgstr "文件不合法" -#: users/views/user.py:330 +#: users/views/user.py:329 msgid "User granted assets" msgstr "用户授权资产" -#: users/views/user.py:363 +#: users/views/user.py:360 msgid "Profile setting" msgstr "个人信息设置" -#: users/views/user.py:382 +#: users/views/user.py:379 msgid "Password update" msgstr "密码更新" -#: users/views/user.py:417 +#: users/views/user.py:414 msgid "Public key update" msgstr "密钥更新" -#: users/views/user.py:458 +#: users/views/user.py:455 msgid "Password invalid" msgstr "用户名或密码无效" -#: users/views/user.py:552 +#: users/views/user.py:549 msgid "MFA enable success" msgstr "MFA 绑定成功" -#: users/views/user.py:553 +#: users/views/user.py:550 msgid "MFA enable success, return login page" msgstr "MFA 绑定成功,返回到登录页面" -#: users/views/user.py:555 +#: users/views/user.py:552 msgid "MFA disable success" msgstr "MFA 解绑成功" -#: users/views/user.py:556 +#: users/views/user.py:553 msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" From f5a4370b80050de4e6dc744c26afe6aed5411a4e Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Jun 2018 17:56:20 +0800 Subject: [PATCH 06/34] =?UTF-8?q?[Update]=20=E7=B3=BB=E7=BB=9F=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E9=80=89=E6=8B=A9=E6=89=8B=E5=8A=A8=E7=99=BB=E5=BD=95?= =?UTF-8?q?=EF=BC=8C=E5=8F=96=E6=B6=88=E8=87=AA=E5=8A=A8=E6=8E=A8=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/forms/user.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/assets/forms/user.py b/apps/assets/forms/user.py index 0bc4ff094..c2a1d7b67 100644 --- a/apps/assets/forms/user.py +++ b/apps/assets/forms/user.py @@ -93,14 +93,20 @@ class SystemUserForm(PasswordAndKeyAuthForm): # Because we define custom field, so we need rewrite :method: `save` system_user = super().save() password = self.cleaned_data.get('password', '') or None + login_mode = self.cleaned_data.get('login_mode', '') or None auto_generate_key = self.cleaned_data.get('auto_generate_key', False) private_key, public_key = super().gen_keys() + if login_mode == SystemUser.MANUAL_LOGIN: + system_user.auto_push = 0 + system_user.save() + if auto_generate_key: logger.info('Auto generate key and set system user auth') system_user.auto_gen_auth() else: system_user.set_auth(password=password, private_key=private_key, public_key=public_key) + return system_user def clean(self): From 2e6d238c76110f9d17073febac78d3432a2e52ea Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 11 Jun 2018 19:09:24 +0800 Subject: [PATCH 07/34] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=94=A8=E6=88=B7=E9=80=89=E6=8B=A9=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E7=99=BB=E5=BD=95=EF=BC=8Cssh=E5=91=BD=E4=BB=A4=E8=A1=8C?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E8=8E=B7=E5=8F=96=E4=B8=8D=E5=88=B0login=5Fm?= =?UTF-8?q?ode=E5=AD=97=E6=AE=B5=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/system_user.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 9caf150b4..7a4e3aadc 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -63,7 +63,10 @@ class AssetSystemUserSerializer(serializers.ModelSerializer): """ class Meta: model = SystemUser - fields = ('id', 'name', 'username', 'priority', 'protocol', 'comment',) + fields = ( + 'id', 'name', 'username', 'priority', + 'protocol', 'comment', 'login_mode' + ) class SystemUserSimpleSerializer(serializers.ModelSerializer): From 2950613b69e369e08c92920b451c268867ee8657 Mon Sep 17 00:00:00 2001 From: wojiushixiaobai <296015668@qq.com> Date: Wed, 20 Jun 2018 15:58:01 +0800 Subject: [PATCH 08/34] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E6=96=87=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/templates/assets/admin_user_list.html | 5 +---- apps/assets/templates/assets/domain_list.html | 11 ++++++++--- apps/static/js/jumpserver.js | 9 +++++---- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html index bcb40fb29..15a870800 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -5,7 +5,7 @@ {% block help_message %}
- 管理用户是服务器的root,或拥有 NOPASSWD: ALL sudo权限的用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。 + 管理用户是资产(被控服务器)上的root,或拥有 NOPASSWD: ALL sudo权限的用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。 Windows或其它硬件可以随意设置一个
{% endblock %} @@ -107,6 +107,3 @@ $(document).ready(function(){ }); {% endblock %} - - - diff --git a/apps/assets/templates/assets/domain_list.html b/apps/assets/templates/assets/domain_list.html index 926c4bbc3..a4042d57e 100644 --- a/apps/assets/templates/assets/domain_list.html +++ b/apps/assets/templates/assets/domain_list.html @@ -1,6 +1,14 @@ {% extends '_base_list.html' %} {% load i18n static %} {% block table_search %}{% endblock %} + +{% block help_message %} +
+ 网域功能是为了解决部分环境(如:混合云)无法直接连接而新增的功能,原理是通过网关服务器进行跳转登 +录。 +
+{% endblock %} + {% block table_container %}
{% trans "Create domain" %} @@ -69,6 +77,3 @@ $(document).ready(function(){ }); {% endblock %} - - - diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 461455085..2b5b74110 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -173,14 +173,14 @@ function APIUpdateAttr(props) { } if (typeof props.success === 'function') { return props.success(data); - } + } }).fail(function(jqXHR, textStatus, errorThrown) { if (flash_message) { toastr.error(fail_message); } if (typeof props.error === 'function') { return props.error(jqXHR.responseText); - } + } }); // return true; } @@ -198,7 +198,8 @@ function objectDelete(obj, name, url, redirectTo) { } }; var fail = function() { - swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error"); + // swal("错误", "删除"+"[ "+name+" ]"+"遇到错误", "error"); + swal("错误", "[ "+name+" ]"+"正在被资产使用中,请先解除资产绑定", "error"); }; APIUpdateAttr({ url: url, @@ -219,7 +220,7 @@ function objectDelete(obj, name, url, redirectTo) { confirmButtonText: '确认', closeOnConfirm: true, }, function () { - doDelete() + doDelete() }); } From 7276bd0b2a822b98fd3c71a807577d436b398c7a Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 26 Jun 2018 22:13:39 +0800 Subject: [PATCH 09/34] =?UTF-8?q?[Update]=20=E6=9B=B4=E6=96=B0=E8=8E=B7?= =?UTF-8?q?=E5=8F=96session=20log=E7=9A=84=E6=96=B9=E6=B3=95=EF=BC=8C?= =?UTF-8?q?=E4=BB=A5=E5=90=8E=E7=BB=9F=E4=B8=80=E5=88=B0media/replay?= =?UTF-8?q?=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/api.py | 89 +++++++++++++++++++++++++++++--------------- apps/users/api.py | 12 ++---- 2 files changed, 63 insertions(+), 38 deletions(-) diff --git a/apps/terminal/api.py b/apps/terminal/api.py index fbbae5e22..2fde3fb60 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -258,10 +258,35 @@ class SessionReplayViewSet(viewsets.ViewSet): serializer_class = ReplaySerializer permission_classes = (IsSuperUserOrAppUser,) session = None + upload_to = 'replay' # 仅添加到本地存储中 - def gen_session_path(self): + def get_session_path(self, version=2): + """ + 获取session日志的文件路径 + :param version: 原来后缀是 .gz,为了统一新版本改为 .replay.gz + :return: + """ + suffix = '.replay.gz' + if version == 1: + suffix = '.gz' date = self.session.date_start.strftime('%Y-%m-%d') - return os.path.join(date, str(self.session.id) + '.gz') + return os.path.join(date, str(self.session.id) + suffix) + + def get_local_path(self, version=2): + session_path = self.get_session_path(version=version) + if version == 2: + local_path = os.path.join(self.upload_to, session_path) + else: + local_path = session_path + return local_path + + def save_to_storage(self, f): + local_path = self.get_local_path() + try: + name = default_storage.save(local_path, f) + return name, None + except OSError as e: + return None, e def create(self, request, *args, **kwargs): session_id = kwargs.get('pk') @@ -270,42 +295,46 @@ class SessionReplayViewSet(viewsets.ViewSet): if serializer.is_valid(): file = serializer.validated_data['file'] - file_path = self.gen_session_path() - try: - default_storage.save(file_path, file) - return Response({'url': default_storage.url(file_path)}, - status=201) - except IOError: - return Response("Save error", status=500) + name, err = self.save_to_storage(file) + if not name: + msg = "Failed save replay `{}`: {}".format(session_id, err) + logger.error(msg) + return Response({'msg': str(err)}, status=400) + url = default_storage.url(name) + return Response({'url': url}, status=201) else: - logger.error( - 'Update load data invalid: {}'.format(serializer.errors)) + msg = 'Upload data invalid: {}'.format(serializer.errors) + logger.error(msg) return Response({'msg': serializer.errors}, status=401) def retrieve(self, request, *args, **kwargs): session_id = kwargs.get('pk') self.session = get_object_or_404(Session, id=session_id) - path = self.gen_session_path() + # 新版本和老版本的文件后缀不同 + session_path = self.get_session_path() # 存在外部存储上的路径 + local_path = self.get_local_path() + local_path_v1 = self.get_local_path(version=1) - if default_storage.exists(path): - url = default_storage.url(path) - return redirect(url) - else: - configs = settings.TERMINAL_REPLAY_STORAGE - configs = [cfg for cfg in configs if cfg['TYPE'] != 'server'] - if not configs: - return HttpResponseNotFound() + # 去default storage中查找 + for _local_path in (local_path, local_path_v1, session_path): + print("Check {}".format(_local_path)) + if default_storage.exists(_local_path): + url = default_storage.url(_local_path) + return redirect(url) - date = self.session.date_start.strftime('%Y-%m-%d') - file_path = os.path.join(date, str(self.session.id) + '.replay.gz') - target_path = default_storage.base_location + '/' + path - storage = jms_storage.get_multi_object_storage(configs) - ok, err = storage.download(file_path, target_path) - if ok: - return redirect(default_storage.url(path)) - else: - logger.error("Failed download replay file: {}".format(err)) - return HttpResponseNotFound() + # 去定义的外部storage查找 + configs = settings.TERMINAL_REPLAY_STORAGE + configs = {k: v for k, v in configs.items() if v['TYPE'] != 'server'} + if not configs: + return HttpResponseNotFound() + + target_path = os.path.join(default_storage.base_location, local_path) # 保存到storage的路径 + storage = jms_storage.get_multi_object_storage(configs) + ok, err = storage.download(session_path, target_path) + if not ok: + logger.error("Failed download replay file: {}".format(err)) + return HttpResponseNotFound() + return redirect(default_storage.url(local_path)) class SessionReplayV2ViewSet(SessionReplayViewSet): diff --git a/apps/users/api.py b/apps/users/api.py index dbc5b66a8..ae4e47b60 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -128,16 +128,12 @@ class UserToken(APIView): return Response({'error': msg}, status=406) -class UserProfile(APIView): - permission_classes = (IsValidUser,) +class UserProfile(generics.RetrieveAPIView): + permission_classes = (IsAuthenticated,) serializer_class = UserSerializer - def get(self, request): - # return Response(request.user.to_json()) - return Response(self.serializer_class(request.user).data) - - def post(self, request): - return Response(self.serializer_class(request.user).data) + def get_object(self): + return self.request.user class UserOtpAuthApi(APIView): From 401a7f88a848f80ad0a0edadcc497701fc5f027c Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 26 Jun 2018 22:27:16 +0800 Subject: [PATCH 10/34] =?UTF-8?q?[Update]=20=E6=9B=B4=E6=96=B0=E4=BE=9D?= =?UTF-8?q?=E8=B5=96storage=E7=9A=84=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index bcba2627c..25d28f259 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -61,7 +61,7 @@ pytz==2018.3 PyYAML==3.12 redis==2.10.6 requests==2.18.4 -jms-storage==0.0.17 +jms-storage==0.0.18 s3transfer==0.1.13 simplejson==3.13.2 six==1.11.0 From eb95a0a9123a968d01874243f03d8d67a9e698f6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 27 Jun 2018 10:34:16 +0800 Subject: [PATCH 11/34] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E6=94=B9=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E5=BD=95=E5=83=8F=E6=B2=A1=E6=9C=89=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/terminal/api.py b/apps/terminal/api.py index 2fde3fb60..f5c76a73f 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -317,7 +317,6 @@ class SessionReplayViewSet(viewsets.ViewSet): # 去default storage中查找 for _local_path in (local_path, local_path_v1, session_path): - print("Check {}".format(_local_path)) if default_storage.exists(_local_path): url = default_storage.url(_local_path) return redirect(url) @@ -329,6 +328,9 @@ class SessionReplayViewSet(viewsets.ViewSet): return HttpResponseNotFound() target_path = os.path.join(default_storage.base_location, local_path) # 保存到storage的路径 + target_dir = os.path.dirname(target_path) + if not os.path.isdir(target_dir): + os.makedirs(target_dir, exist_ok=True) storage = jms_storage.get_multi_object_storage(configs) ok, err = storage.download(session_path, target_path) if not ok: From f1f06491d6816576ffe3e2f0c773b423947a208f Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Fri, 29 Jun 2018 14:59:43 +0800 Subject: [PATCH 12/34] =?UTF-8?q?[Feature]=20=E6=B7=BB=E5=8A=A0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD,=20=E6=94=AF=E6=8C=81=20telnet=20server.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/forms/asset.py | 4 +- apps/assets/forms/user.py | 3 +- apps/assets/models/asset.py | 14 ++ apps/assets/models/user.py | 2 + apps/assets/serializers/asset.py | 2 +- .../assets/templates/assets/_system_user.html | 11 +- .../assets/templates/assets/asset_create.html | 11 +- .../assets/templates/assets/asset_update.html | 1 + apps/i18n/zh/LC_MESSAGES/django.mo | Bin 35051 -> 35966 bytes apps/i18n/zh/LC_MESSAGES/django.po | 179 +++++++++--------- apps/perms/api.py | 8 +- 11 files changed, 131 insertions(+), 104 deletions(-) diff --git a/apps/assets/forms/asset.py b/apps/assets/forms/asset.py index f8f187b4d..5000c087d 100644 --- a/apps/assets/forms/asset.py +++ b/apps/assets/forms/asset.py @@ -16,7 +16,7 @@ class AssetCreateForm(forms.ModelForm): fields = [ 'hostname', 'ip', 'public_ip', 'port', 'comment', 'nodes', 'is_active', 'admin_user', 'labels', 'platform', - 'domain', + 'domain', 'protocol', ] widgets = { @@ -56,7 +56,7 @@ class AssetUpdateForm(forms.ModelForm): fields = [ 'hostname', 'ip', 'port', 'nodes', 'is_active', 'platform', 'public_ip', 'number', 'comment', 'admin_user', 'labels', - 'domain', + 'domain', 'protocol', ] widgets = { 'nodes': forms.SelectMultiple(attrs={ diff --git a/apps/assets/forms/user.py b/apps/assets/forms/user.py index c2a1d7b67..b25e19d87 100644 --- a/apps/assets/forms/user.py +++ b/apps/assets/forms/user.py @@ -94,10 +94,11 @@ class SystemUserForm(PasswordAndKeyAuthForm): system_user = super().save() password = self.cleaned_data.get('password', '') or None login_mode = self.cleaned_data.get('login_mode', '') or None + protocol = self.cleaned_data.get('protocol') or None auto_generate_key = self.cleaned_data.get('auto_generate_key', False) private_key, public_key = super().gen_keys() - if login_mode == SystemUser.MANUAL_LOGIN: + if login_mode == SystemUser.MANUAL_LOGIN or protocol == SystemUser.TELNET_PROTOCOL: system_user.auto_push = 0 system_user.save() diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index a974d3385..7a2b3fe57 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -57,13 +57,27 @@ class Asset(models.Model): ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), + ('Windows2016', 'Windows(2016)'), ('Other', 'Other'), ) + + SSH_PROTOCOL = 'ssh' + RDP_PROTOCOL = 'rdp' + TELNET_PROTOCOL = 'telnet' + PROTOCOL_CHOICES = ( + (SSH_PROTOCOL, 'ssh'), + (RDP_PROTOCOL, 'rdp'), + (TELNET_PROTOCOL, 'telnet (beta)'), + ) + id = models.UUIDField(default=uuid.uuid4, primary_key=True) ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True) hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname')) + protocol = models.CharField(max_length=128, default=SSH_PROTOCOL, + choices=PROTOCOL_CHOICES, + verbose_name=_('Protocol')) port = models.IntegerField(default=22, verbose_name=_('Port')) platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform')) diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index f835e387f..5faca5da8 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -95,9 +95,11 @@ class AdminUser(AssetUser): class SystemUser(AssetUser): SSH_PROTOCOL = 'ssh' RDP_PROTOCOL = 'rdp' + TELNET_PROTOCOL = 'telnet' PROTOCOL_CHOICES = ( (SSH_PROTOCOL, 'ssh'), (RDP_PROTOCOL, 'rdp'), + (TELNET_PROTOCOL, 'telnet (beta)'), ) AUTO_LOGIN = 'auto' diff --git a/apps/assets/serializers/asset.py b/apps/assets/serializers/asset.py index a0fdfab73..e63735794 100644 --- a/apps/assets/serializers/asset.py +++ b/apps/assets/serializers/asset.py @@ -43,7 +43,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer): fields = ( "id", "hostname", "ip", "port", "system_users_granted", "is_active", "system_users_join", "os", 'domain', - "platform", "comment" + "platform", "comment", "protocol", ) @staticmethod diff --git a/apps/assets/templates/assets/_system_user.html b/apps/assets/templates/assets/_system_user.html index cf8539293..4e1bc51a8 100644 --- a/apps/assets/templates/assets/_system_user.html +++ b/apps/assets/templates/assets/_system_user.html @@ -104,7 +104,14 @@ function protocolChange() { $.each(need_change_field, function (index, value) { $(value).closest('.form-group').addClass('hidden') }); - } else { + } + else if ($(protocol_id + " option:selected").text() === 'telnet (beta)') { + $('.auth-fields').removeClass('hidden'); + $.each(need_change_field, function (index, value) { + $(value).closest('.form-group').addClass('hidden') + }); + } + else { if($(login_mode_id).val() === 'manual'){ $(sudo_id).closest('.form-group').removeClass('hidden'); $(shell_id).closest('.form-group').removeClass('hidden'); @@ -133,8 +140,8 @@ function loginModeChange(){ } else if($(login_mode_id).val() === 'auto'){ $('#auth_title_id').removeClass('hidden'); - protocolChange(); $(password_id).closest('.form-group').removeClass('hidden') + protocolChange(); } } diff --git a/apps/assets/templates/assets/asset_create.html b/apps/assets/templates/assets/asset_create.html index 99703d2e3..55e233d0d 100644 --- a/apps/assets/templates/assets/asset_create.html +++ b/apps/assets/templates/assets/asset_create.html @@ -17,6 +17,7 @@ {% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.platform layout="horizontal" %} {% bootstrap_field form.ip layout="horizontal" %} + {% bootstrap_field form.protocol layout="horizontal" %} {% bootstrap_field form.port layout="horizontal" %} {% bootstrap_field form.public_ip layout="horizontal" %} {% bootstrap_field form.domain layout="horizontal" %} @@ -85,14 +86,14 @@ $(document).ready(function () { allowClear: true, templateSelection: format }); - $("#id_platform").change(function (){ - var platform = $("#id_platform option:selected").text(); + $("#id_protocol").change(function (){ + var protocol = $("#id_protocol option:selected").text(); var port = 22; - if(platform === 'Windows'){ + if(protocol === 'rdp'){ port = 3389; } - if(platform === 'Other'){ - port = null; + if(protocol === 'telnet (beta)'){ + port = 23; } $("#id_port").val(port); }); diff --git a/apps/assets/templates/assets/asset_update.html b/apps/assets/templates/assets/asset_update.html index 3d42ca2b5..7ed1da05a 100644 --- a/apps/assets/templates/assets/asset_update.html +++ b/apps/assets/templates/assets/asset_update.html @@ -21,6 +21,7 @@

{% trans 'Basic' %}

{% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.ip layout="horizontal" %} + {% bootstrap_field form.protocol layout="horizontal" %} {% bootstrap_field form.port layout="horizontal" %} {% bootstrap_field form.platform layout="horizontal" %} {% bootstrap_field form.public_ip layout="horizontal" %} diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index df59e43d67e7b758c445eae254496702a7583bf4..d842930e4e5b0eece76196f4cd257d50a9117912 100644 GIT binary patch delta 12296 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 delta 12043 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 I>>rr;f5cTk diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index b5d9a5c56..c596db6ac 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/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: 2018-06-11 13:36+0800\n" +"POT-Creation-Date: 2018-06-25 12:19+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -29,7 +29,7 @@ msgstr "" msgid "测试节点下资产是否可连接: {}" msgstr "" -#: assets/forms/asset.py:24 assets/models/asset.py:75 assets/models/user.py:110 +#: assets/forms/asset.py:24 assets/models/asset.py:89 assets/models/user.py:112 #: assets/templates/assets/asset_detail.html:183 #: assets/templates/assets/asset_detail.html:191 #: assets/templates/assets/system_user_detail.html:179 perms/models.py:33 @@ -37,30 +37,30 @@ msgid "Nodes" msgstr "节点管理" #: assets/forms/asset.py:27 assets/forms/asset.py:66 assets/forms/asset.py:109 -#: assets/forms/asset.py:113 assets/models/asset.py:80 +#: assets/forms/asset.py:113 assets/models/asset.py:94 #: assets/models/cluster.py:19 assets/models/user.py:72 #: assets/templates/assets/asset_detail.html:73 templates/_nav.html:25 msgid "Admin user" msgstr "管理用户" #: assets/forms/asset.py:30 assets/forms/asset.py:69 assets/forms/asset.py:125 -#: assets/templates/assets/asset_create.html:35 -#: assets/templates/assets/asset_create.html:37 +#: assets/templates/assets/asset_create.html:36 +#: assets/templates/assets/asset_create.html:38 #: assets/templates/assets/asset_list.html:75 -#: assets/templates/assets/asset_update.html:40 -#: assets/templates/assets/asset_update.html:42 +#: assets/templates/assets/asset_update.html:41 +#: assets/templates/assets/asset_update.html:43 #: assets/templates/assets/user_asset_list.html:34 msgid "Label" msgstr "标签" -#: assets/forms/asset.py:34 assets/forms/asset.py:73 assets/models/asset.py:71 +#: assets/forms/asset.py:34 assets/forms/asset.py:73 assets/models/asset.py:85 #: assets/models/domain.py:46 msgid "Domain" msgstr "网域" #: assets/forms/asset.py:38 assets/forms/asset.py:63 assets/forms/asset.py:77 -#: assets/forms/asset.py:128 assets/templates/assets/asset_create.html:29 -#: assets/templates/assets/asset_update.html:34 perms/forms.py:40 +#: assets/forms/asset.py:128 assets/templates/assets/asset_create.html:30 +#: assets/templates/assets/asset_update.html:35 perms/forms.py:40 #: perms/forms.py:47 perms/models.py:76 #: perms/templates/perms/asset_permission_list.html:57 #: perms/templates/perms/asset_permission_list.html:142 @@ -90,7 +90,7 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域, msgid "Select assets" msgstr "选择资产" -#: assets/forms/asset.py:105 assets/models/asset.py:67 +#: assets/forms/asset.py:105 assets/models/asset.py:81 #: assets/models/domain.py:44 assets/templates/assets/admin_user_assets.html:53 #: assets/templates/assets/asset_detail.html:69 #: assets/templates/assets/domain_gateway_list.html:58 @@ -99,7 +99,7 @@ msgid "Port" msgstr "端口" #: assets/forms/domain.py:14 assets/forms/label.py:13 -#: assets/models/asset.py:223 assets/templates/assets/admin_user_list.html:25 +#: assets/models/asset.py:237 assets/templates/assets/admin_user_list.html:25 #: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_list.html:15 #: assets/templates/assets/label_list.html:16 @@ -118,7 +118,7 @@ msgstr "端口" msgid "Asset" msgstr "资产" -#: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:131 +#: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:138 #: assets/models/base.py:21 assets/models/cluster.py:18 #: assets/models/domain.py:17 assets/models/group.py:20 #: assets/models/label.py:17 assets/templates/assets/admin_user_detail.html:56 @@ -147,7 +147,7 @@ msgstr "资产" msgid "Name" msgstr "名称" -#: assets/forms/domain.py:55 assets/forms/user.py:80 assets/forms/user.py:132 +#: assets/forms/domain.py:55 assets/forms/user.py:80 assets/forms/user.py:139 #: assets/models/base.py:22 assets/templates/assets/admin_user_detail.html:60 #: assets/templates/assets/admin_user_list.html:24 #: assets/templates/assets/domain_gateway_list.html:60 @@ -192,27 +192,27 @@ msgstr "ssh密钥不合法" msgid "Password and private key file must be input one" msgstr "密码和私钥, 必须输入一个" -#: assets/forms/user.py:118 +#: assets/forms/user.py:124 msgid "* Automatic login mode, must fill in the username." msgstr "自动登录模式,必须填写用户名" -#: assets/forms/user.py:137 +#: assets/forms/user.py:144 msgid "Auto push system user to asset" msgstr "自动推送系统用户到资产" -#: assets/forms/user.py:138 +#: assets/forms/user.py:145 msgid "" "High level will be using login asset as default, if user was granted more " "than 2 system user" msgstr "高优先级的系统用户将会作为默认登录用户" -#: assets/forms/user.py:139 +#: assets/forms/user.py:147 msgid "" "If you choose manual login mode, you do not need to fill in the username and " "password." msgstr "如果选择手动登录模式,用户名和密码则不需要填写" -#: assets/models/asset.py:63 assets/models/domain.py:43 +#: assets/models/asset.py:74 assets/models/domain.py:43 #: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/admin_user_assets.html:52 #: assets/templates/assets/asset_detail.html:61 @@ -227,7 +227,7 @@ msgstr "如果选择手动登录模式,用户名和密码则不需要填写" msgid "IP" msgstr "IP" -#: assets/models/asset.py:66 assets/templates/assets/_asset_list_modal.html:45 +#: assets/models/asset.py:77 assets/templates/assets/_asset_list_modal.html:45 #: assets/templates/assets/admin_user_assets.html:51 #: assets/templates/assets/asset_detail.html:57 #: assets/templates/assets/asset_list.html:86 @@ -239,82 +239,91 @@ msgstr "IP" msgid "Hostname" msgstr "主机名" -#: assets/models/asset.py:69 assets/templates/assets/asset_detail.html:97 +#: assets/models/asset.py:80 assets/models/domain.py:45 +#: assets/models/user.py:115 +#: assets/templates/assets/domain_gateway_list.html:59 +#: assets/templates/assets/system_user_detail.html:70 +#: assets/templates/assets/system_user_list.html:28 +#: terminal/templates/terminal/session_list.html:75 +msgid "Protocol" +msgstr "协议" + +#: assets/models/asset.py:83 assets/templates/assets/asset_detail.html:97 msgid "Platform" msgstr "系统平台" -#: assets/models/asset.py:76 assets/models/domain.py:48 +#: assets/models/asset.py:90 assets/models/domain.py:48 #: assets/models/label.py:20 assets/templates/assets/asset_detail.html:105 msgid "Is active" msgstr "激活" -#: assets/models/asset.py:85 assets/templates/assets/asset_detail.html:65 +#: assets/models/asset.py:99 assets/templates/assets/asset_detail.html:65 msgid "Public IP" msgstr "公网IP" -#: assets/models/asset.py:87 assets/templates/assets/asset_detail.html:113 +#: assets/models/asset.py:101 assets/templates/assets/asset_detail.html:113 msgid "Asset number" msgstr "资产编号" -#: assets/models/asset.py:91 assets/templates/assets/asset_detail.html:77 +#: assets/models/asset.py:105 assets/templates/assets/asset_detail.html:77 msgid "Vendor" msgstr "制造商" -#: assets/models/asset.py:93 assets/templates/assets/asset_detail.html:81 +#: assets/models/asset.py:107 assets/templates/assets/asset_detail.html:81 msgid "Model" msgstr "型号" -#: assets/models/asset.py:95 assets/templates/assets/asset_detail.html:109 +#: assets/models/asset.py:109 assets/templates/assets/asset_detail.html:109 msgid "Serial number" msgstr "序列号" -#: assets/models/asset.py:98 +#: assets/models/asset.py:112 msgid "CPU model" msgstr "CPU型号" -#: assets/models/asset.py:99 +#: assets/models/asset.py:113 msgid "CPU count" msgstr "CPU数量" -#: assets/models/asset.py:100 +#: assets/models/asset.py:114 msgid "CPU cores" msgstr "CPU核数" -#: assets/models/asset.py:102 assets/templates/assets/asset_detail.html:89 +#: assets/models/asset.py:116 assets/templates/assets/asset_detail.html:89 msgid "Memory" msgstr "内存" -#: assets/models/asset.py:104 +#: assets/models/asset.py:118 msgid "Disk total" msgstr "硬盘大小" -#: assets/models/asset.py:106 +#: assets/models/asset.py:120 msgid "Disk info" msgstr "硬盘信息" -#: assets/models/asset.py:109 assets/templates/assets/asset_detail.html:101 +#: assets/models/asset.py:123 assets/templates/assets/asset_detail.html:101 msgid "OS" msgstr "操作系统" -#: assets/models/asset.py:111 +#: assets/models/asset.py:125 msgid "OS version" msgstr "系统版本" -#: assets/models/asset.py:113 +#: assets/models/asset.py:127 msgid "OS arch" msgstr "系统架构" -#: assets/models/asset.py:115 +#: assets/models/asset.py:129 msgid "Hostname raw" msgstr "主机名原始" -#: assets/models/asset.py:119 assets/templates/assets/asset_create.html:33 +#: assets/models/asset.py:133 assets/templates/assets/asset_create.html:34 #: assets/templates/assets/asset_detail.html:220 -#: assets/templates/assets/asset_update.html:38 templates/_nav.html:27 +#: assets/templates/assets/asset_update.html:39 templates/_nav.html:27 msgid "Labels" msgstr "标签管理" -#: assets/models/asset.py:121 assets/models/base.py:29 +#: assets/models/asset.py:135 assets/models/base.py:29 #: assets/models/cluster.py:28 assets/models/group.py:21 #: assets/templates/assets/admin_user_detail.html:68 #: assets/templates/assets/asset_detail.html:117 @@ -326,7 +335,7 @@ msgstr "标签管理" msgid "Created by" msgstr "创建者" -#: assets/models/asset.py:124 assets/models/cluster.py:26 +#: assets/models/asset.py:138 assets/models/cluster.py:26 #: assets/models/domain.py:20 assets/models/group.py:22 #: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:64 #: assets/templates/assets/domain_detail.html:68 @@ -339,7 +348,7 @@ msgstr "创建者" msgid "Date created" msgstr "创建日期" -#: assets/models/asset.py:126 assets/models/base.py:26 +#: assets/models/asset.py:140 assets/models/base.py:26 #: assets/models/cluster.py:29 assets/models/domain.py:18 #: assets/models/domain.py:47 assets/models/group.py:23 #: assets/models/label.py:21 assets/templates/assets/admin_user_detail.html:72 @@ -414,14 +423,6 @@ msgstr "默认Cluster" msgid "Cluster" msgstr "集群" -#: assets/models/domain.py:45 assets/models/user.py:113 -#: assets/templates/assets/domain_gateway_list.html:59 -#: assets/templates/assets/system_user_detail.html:70 -#: assets/templates/assets/system_user_list.html:28 -#: terminal/templates/terminal/session_list.html:75 -msgid "Protocol" -msgstr "协议" - #: assets/models/group.py:30 msgid "Asset group" msgstr "资产组" @@ -444,7 +445,7 @@ msgstr "默认资产组" #: terminal/templates/terminal/session_list.html:71 users/forms.py:282 #: users/models/user.py:31 users/models/user.py:333 #: users/templates/users/user_group_detail.html:78 -#: users/templates/users/user_group_list.html:13 users/views/user.py:359 +#: users/templates/users/user_group_list.html:13 users/views/user.py:361 msgid "User" msgstr "用户" @@ -461,15 +462,15 @@ msgstr "分类" msgid "Key" msgstr "" -#: assets/models/user.py:106 +#: assets/models/user.py:108 msgid "Automatic login" msgstr "自动登录" -#: assets/models/user.py:107 +#: assets/models/user.py:109 msgid "Manually login" msgstr "手动登录" -#: assets/models/user.py:111 +#: assets/models/user.py:113 #: assets/templates/assets/_asset_group_bulk_update_modal.html:11 #: assets/templates/assets/system_user_asset.html:21 #: assets/views/admin_user.py:29 assets/views/admin_user.py:47 @@ -487,30 +488,30 @@ msgstr "手动登录" msgid "Assets" msgstr "资产管理" -#: assets/models/user.py:112 +#: assets/models/user.py:114 msgid "Priority" msgstr "优先级" -#: assets/models/user.py:114 assets/templates/assets/_system_user.html:59 +#: assets/models/user.py:116 assets/templates/assets/_system_user.html:59 #: assets/templates/assets/system_user_detail.html:122 #: assets/templates/assets/system_user_update.html:10 msgid "Auto push" msgstr "自动推送" -#: assets/models/user.py:115 assets/templates/assets/system_user_detail.html:74 +#: assets/models/user.py:117 assets/templates/assets/system_user_detail.html:74 msgid "Sudo" msgstr "Sudo" -#: assets/models/user.py:116 assets/templates/assets/system_user_detail.html:79 +#: assets/models/user.py:118 assets/templates/assets/system_user_detail.html:79 msgid "Shell" msgstr "Shell" -#: assets/models/user.py:117 assets/templates/assets/system_user_detail.html:66 +#: assets/models/user.py:119 assets/templates/assets/system_user_detail.html:66 #: assets/templates/assets/system_user_list.html:29 msgid "Login mode" msgstr "登录模式" -#: assets/models/user.py:157 audits/models.py:12 +#: assets/models/user.py:159 audits/models.py:12 #: audits/templates/audits/ftp_log_list.html:49 #: audits/templates/audits/ftp_log_list.html:73 perms/forms.py:43 #: perms/models.py:34 perms/models.py:78 @@ -624,8 +625,8 @@ msgid "Basic" msgstr "基本" #: assets/templates/assets/_system_user.html:44 -#: assets/templates/assets/asset_create.html:25 -#: assets/templates/assets/asset_update.html:30 +#: assets/templates/assets/asset_create.html:26 +#: assets/templates/assets/asset_update.html:31 #: assets/templates/assets/gateway_create_update.html:45 #: users/templates/users/_user.html:21 msgid "Auth" @@ -636,8 +637,8 @@ msgid "Auto generate key" msgstr "自动生成密钥" #: assets/templates/assets/_system_user.html:65 -#: assets/templates/assets/asset_create.html:59 -#: assets/templates/assets/asset_update.html:63 +#: assets/templates/assets/asset_create.html:60 +#: assets/templates/assets/asset_update.html:64 #: assets/templates/assets/gateway_create_update.html:53 #: perms/templates/perms/asset_permission_create_update.html:45 #: terminal/templates/terminal/terminal_update.html:42 @@ -647,8 +648,8 @@ msgstr "其它" #: assets/templates/assets/_system_user.html:71 #: assets/templates/assets/admin_user_create_update.html:45 #: assets/templates/assets/asset_bulk_update.html:23 -#: assets/templates/assets/asset_create.html:66 -#: assets/templates/assets/asset_update.html:70 +#: assets/templates/assets/asset_create.html:67 +#: assets/templates/assets/asset_update.html:71 #: assets/templates/assets/domain_create_update.html:16 #: assets/templates/assets/gateway_create_update.html:58 #: assets/templates/assets/label_create_update.html:18 @@ -672,9 +673,9 @@ msgstr "重置" #: assets/templates/assets/_system_user.html:72 #: assets/templates/assets/admin_user_create_update.html:46 #: assets/templates/assets/asset_bulk_update.html:24 -#: assets/templates/assets/asset_create.html:67 +#: assets/templates/assets/asset_create.html:68 #: assets/templates/assets/asset_list.html:108 -#: assets/templates/assets/asset_update.html:71 +#: assets/templates/assets/asset_update.html:72 #: assets/templates/assets/domain_create_update.html:17 #: assets/templates/assets/gateway_create_update.html:59 #: assets/templates/assets/label_create_update.html:19 @@ -1025,7 +1026,7 @@ msgstr "删除" msgid "Asset Deleting failed." msgstr "删除失败" -#: assets/templates/assets/asset_update.html:59 +#: assets/templates/assets/asset_update.html:60 msgid "Configuration" msgstr "配置" @@ -1987,7 +1988,7 @@ msgstr "文档" #: users/templates/users/user_profile.html:17 #: users/templates/users/user_profile_update.html:37 #: users/templates/users/user_profile_update.html:57 -#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:341 +#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:343 msgid "Profile" msgstr "个人信息" @@ -2045,8 +2046,8 @@ msgstr "关闭" #: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44 #: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95 #: users/views/login.py:277 users/views/login.py:335 users/views/user.py:65 -#: users/views/user.py:80 users/views/user.py:102 users/views/user.py:173 -#: users/views/user.py:328 users/views/user.py:378 users/views/user.py:413 +#: users/views/user.py:80 users/views/user.py:102 users/views/user.py:175 +#: users/views/user.py:330 users/views/user.py:380 users/views/user.py:415 msgid "Users" msgstr "用户管理" @@ -2680,7 +2681,7 @@ msgid "Reset link will be generated and sent to the user. " msgstr "生成重置密码连接,通过邮件发送给用户" #: users/templates/users/user_detail.html:19 -#: users/templates/users/user_granted_asset.html:18 users/views/user.py:174 +#: users/templates/users/user_granted_asset.html:18 users/views/user.py:176 msgid "User detail" msgstr "用户详情" @@ -2815,8 +2816,8 @@ msgstr "用户删除失败" msgid "Administrator Settings force MFA login" msgstr "管理员设置强制使用MFA登录" -#: users/templates/users/user_profile.html:116 users/views/user.py:203 -#: users/views/user.py:257 +#: users/templates/users/user_profile.html:116 users/views/user.py:205 +#: users/views/user.py:259 msgid "User groups" msgstr "用户组" @@ -2871,7 +2872,7 @@ msgid "Create account successfully" msgstr "创建账户成功" #: users/utils.py:43 -#, fuzzy, python-format +#, python-format msgid "" "\n" " Hello %(name)s:\n" @@ -3020,7 +3021,7 @@ msgstr "用户组授权资产" msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: users/views/login.py:128 users/views/user.py:498 users/views/user.py:523 +#: users/views/login.py:128 users/views/user.py:500 users/views/user.py:525 msgid "MFA code invalid" msgstr "MFA码认证失败" @@ -3061,7 +3062,7 @@ msgstr "Token错误或失效" msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:239 users/views/user.py:115 users/views/user.py:396 +#: users/views/login.py:239 users/views/user.py:118 users/views/user.py:398 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" @@ -3073,46 +3074,46 @@ msgstr "首次登陆" msgid "Login log list" msgstr "登录日志" -#: users/views/user.py:127 +#: users/views/user.py:129 msgid "Bulk update user success" msgstr "批量更新用户成功" -#: users/views/user.py:232 +#: users/views/user.py:234 msgid "Invalid file." msgstr "文件不合法" -#: users/views/user.py:329 +#: users/views/user.py:331 msgid "User granted assets" msgstr "用户授权资产" -#: users/views/user.py:360 +#: users/views/user.py:362 msgid "Profile setting" msgstr "个人信息设置" -#: users/views/user.py:379 +#: users/views/user.py:381 msgid "Password update" msgstr "密码更新" -#: users/views/user.py:414 +#: users/views/user.py:416 msgid "Public key update" msgstr "密钥更新" -#: users/views/user.py:455 +#: users/views/user.py:457 msgid "Password invalid" msgstr "用户名或密码无效" -#: users/views/user.py:549 +#: users/views/user.py:551 msgid "MFA enable success" msgstr "MFA 绑定成功" -#: users/views/user.py:550 +#: users/views/user.py:552 msgid "MFA enable success, return login page" msgstr "MFA 绑定成功,返回到登录页面" -#: users/views/user.py:552 +#: users/views/user.py:554 msgid "MFA disable success" msgstr "MFA 解绑成功" -#: users/views/user.py:553 +#: users/views/user.py:555 msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" diff --git a/apps/perms/api.py b/apps/perms/api.py index 33a027064..40366a19b 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -73,9 +73,9 @@ class UserGrantedAssetsApi(ListAPIView): util = AssetPermissionUtil(user) for k, v in util.get_assets().items(): if k.is_unixlike(): - system_users_granted = [s for s in v if s.protocol == 'ssh'] + system_users_granted = [s for s in v if s.protocol in ['ssh', 'telnet']] else: - system_users_granted = [s for s in v if s.protocol == 'rdp'] + system_users_granted = [s for s in v if s.protocol in ['rdp', 'telnet']] k.system_users_granted = system_users_granted queryset.append(k) return queryset @@ -124,9 +124,9 @@ class UserGrantedNodesWithAssetsApi(ListAPIView): assets = _assets.keys() for k, v in _assets.items(): if k.is_unixlike(): - system_users_granted = [s for s in v if s.protocol == 'ssh'] + system_users_granted = [s for s in v if s.protocol in ['ssh', 'telnet']] else: - system_users_granted = [s for s in v if s.protocol == 'rdp'] + system_users_granted = [s for s in v if s.protocol in ['rdp', 'telnet']] k.system_users_granted = system_users_granted node.assets_granted = assets queryset.append(node) From 40022899743152448d7d271ff038e5f3b904b7d9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 3 Jul 2018 13:23:21 +0800 Subject: [PATCH 13/34] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E4=B8=AD=E5=88=A0=E9=99=A4=E6=97=B6=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/templates/users/user_detail.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 22ab4b5d4..d40251563 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -421,8 +421,8 @@ $(document).ready(function() { APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail}); }).on('click', '.btn-delete-user', function () { var $this = $(this); - var name = "{{ user.name }}"; - var uid = "{{ user.id }}"; + var name = "{{ user_object.name }}"; + var uid = "{{ user_object.id }}"; var the_url = '{% url "api-users:user-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid); var redirect_url = "{% url 'users:user-list' %}"; objectDelete($this, name, the_url, redirect_url); From e1be86791351e987e706126e6a2c74a0fa20910d Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Tue, 3 Jul 2018 17:47:12 +0800 Subject: [PATCH 14/34] =?UTF-8?q?[Update]=20=E6=9B=B4=E6=96=B0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97=EF=BC=8C=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E7=99=BB=E5=BD=95=E5=A4=B1=E8=B4=A5=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E5=B9=B6=E6=B7=BB=E5=8A=A0MFA=E5=90=AF=E7=94=A8=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 35966 -> 36216 bytes apps/i18n/zh/LC_MESSAGES/django.po | 118 +++++++++++------- apps/users/api.py | 66 +++++++--- apps/users/models/authentication.py | 28 +++++ .../users/templates/users/login_log_list.html | 6 + apps/users/utils.py | 12 +- apps/users/views/login.py | 66 +++++++--- 7 files changed, 217 insertions(+), 79 deletions(-) diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index d842930e4e5b0eece76196f4cd257d50a9117912..339af4456cb72a4e21841737e7c98bce3a39fc6a 100644 GIT binary patch delta 12337 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\n" "Language-Team: Jumpserver team\n" @@ -118,7 +118,7 @@ msgstr "端口" msgid "Asset" msgstr "资产" -#: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:138 +#: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:139 #: assets/models/base.py:21 assets/models/cluster.py:18 #: assets/models/domain.py:17 assets/models/group.py:20 #: assets/models/label.py:17 assets/templates/assets/admin_user_detail.html:56 @@ -147,14 +147,14 @@ msgstr "资产" msgid "Name" msgstr "名称" -#: assets/forms/domain.py:55 assets/forms/user.py:80 assets/forms/user.py:139 +#: assets/forms/domain.py:55 assets/forms/user.py:80 assets/forms/user.py:140 #: assets/models/base.py:22 assets/templates/assets/admin_user_detail.html:60 #: assets/templates/assets/admin_user_list.html:24 #: assets/templates/assets/domain_gateway_list.html:60 #: assets/templates/assets/system_user_detail.html:62 #: assets/templates/assets/system_user_list.html:27 #: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13 -#: users/forms.py:31 users/models/authentication.py:45 users/models/user.py:47 +#: users/forms.py:31 users/models/authentication.py:70 users/models/user.py:47 #: users/templates/users/_select_user_modal.html:14 #: users/templates/users/login.html:56 #: users/templates/users/login_log_list.html:49 @@ -192,21 +192,21 @@ msgstr "ssh密钥不合法" msgid "Password and private key file must be input one" msgstr "密码和私钥, 必须输入一个" -#: assets/forms/user.py:124 +#: assets/forms/user.py:125 msgid "* Automatic login mode, must fill in the username." msgstr "自动登录模式,必须填写用户名" -#: assets/forms/user.py:144 +#: assets/forms/user.py:145 msgid "Auto push system user to asset" msgstr "自动推送系统用户到资产" -#: assets/forms/user.py:145 +#: assets/forms/user.py:146 msgid "" "High level will be using login asset as default, if user was granted more " "than 2 system user" msgstr "高优先级的系统用户将会作为默认登录用户" -#: assets/forms/user.py:147 +#: assets/forms/user.py:148 msgid "" "If you choose manual login mode, you do not need to fill in the username and " "password." @@ -1237,7 +1237,7 @@ msgid "Filename" msgstr "文件名" #: audits/models.py:15 audits/templates/audits/ftp_log_list.html:77 -#: ops/templates/ops/task_list.html:39 +#: ops/templates/ops/task_list.html:39 users/models/authentication.py:66 msgid "Success" msgstr "成功" @@ -1485,7 +1485,8 @@ msgstr "" msgid "discard time" msgstr "" -#: common/models.py:29 users/templates/users/user_detail.html:96 +#: common/models.py:29 users/models/authentication.py:51 +#: users/templates/users/user_detail.html:96 msgid "Enabled" msgstr "启用" @@ -1803,7 +1804,7 @@ msgid "Versions" msgstr "版本" #: ops/templates/ops/task_list.html:40 -#: users/templates/users/login_log_list.html:54 +#: users/templates/users/login_log_list.html:57 msgid "Date" msgstr "日期" @@ -2045,7 +2046,7 @@ msgstr "关闭" #: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44 #: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95 -#: users/views/login.py:277 users/views/login.py:335 users/views/user.py:65 +#: users/views/login.py:311 users/views/login.py:369 users/views/user.py:65 #: users/views/user.py:80 users/views/user.py:102 users/views/user.py:175 #: users/views/user.py:330 users/views/user.py:380 users/views/user.py:415 msgid "Users" @@ -2406,8 +2407,9 @@ msgstr "" msgid "* Enable MFA authentication to make the account more secure." msgstr "* 启用MFA认证,使账号更加安全." -#: users/forms.py:143 users/models/user.py:71 +#: users/forms.py:143 users/models/authentication.py:75 users/models/user.py:71 #: users/templates/users/first_login.html:45 +#: users/templates/users/login_log_list.html:54 msgid "MFA" msgstr "MFA" @@ -2467,23 +2469,53 @@ msgstr "ssh公钥" msgid "Private Token" msgstr "ssh密钥" -#: users/models/authentication.py:46 +#: users/models/authentication.py:50 users/templates/users/user_detail.html:98 +msgid "Disabled" +msgstr "禁用" + +#: users/models/authentication.py:52 users/models/authentication.py:60 +msgid "-" +msgstr "" + +#: users/models/authentication.py:61 +msgid "Username/password check failed" +msgstr "用户名/密码 校验失败" + +#: users/models/authentication.py:62 +msgid "MFA authentication failed" +msgstr "MFA 认证失败" + +#: users/models/authentication.py:67 +msgid "Failed" +msgstr "失败" + +#: users/models/authentication.py:71 msgid "Login type" msgstr "登录方式" -#: users/models/authentication.py:47 +#: users/models/authentication.py:72 msgid "Login ip" msgstr "登录IP" -#: users/models/authentication.py:48 +#: users/models/authentication.py:73 msgid "Login city" msgstr "登录城市" -#: users/models/authentication.py:49 +#: users/models/authentication.py:74 msgid "User agent" msgstr "Agent" -#: users/models/authentication.py:50 +#: users/models/authentication.py:76 +#: users/templates/users/login_log_list.html:55 +msgid "Reason" +msgstr "原因" + +#: users/models/authentication.py:77 +#: users/templates/users/login_log_list.html:56 +msgid "Status" +msgstr "状态" + +#: users/models/authentication.py:78 msgid "Date login" msgstr "登录日期" @@ -2646,7 +2678,7 @@ msgid "Can't provide security? Please contact the administrator!" msgstr "如果不能提供MFA验证码,请联系管理员!" #: users/templates/users/reset_password.html:46 -#: users/templates/users/user_detail.html:352 users/utils.py:80 +#: users/templates/users/user_detail.html:352 users/utils.py:81 msgid "Reset password" msgstr "重置密码" @@ -2696,10 +2728,6 @@ msgstr "授权的资产" msgid "Force enabled" msgstr "强制启用" -#: users/templates/users/user_detail.html:98 -msgid "Disabled" -msgstr "禁用" - #: users/templates/users/user_detail.html:119 #: users/templates/users/user_profile.html:108 msgid "Last login" @@ -2867,11 +2895,11 @@ msgstr "新的公钥已设置成功,请下载对应的私钥" msgid "Update user" msgstr "更新用户" -#: users/utils.py:41 +#: users/utils.py:42 msgid "Create account successfully" msgstr "创建账户成功" -#: users/utils.py:43 +#: users/utils.py:44 #, python-format msgid "" "\n" @@ -2916,7 +2944,7 @@ msgstr "" "
\n" " " -#: users/utils.py:82 +#: users/utils.py:83 #, python-format msgid "" "\n" @@ -2960,11 +2988,11 @@ msgstr "" "
\n" " " -#: users/utils.py:113 +#: users/utils.py:114 msgid "SSH Key Reset" msgstr "重置ssh密钥" -#: users/utils.py:115 +#: users/utils.py:116 #, python-format msgid "" "\n" @@ -2989,15 +3017,15 @@ msgstr "" "
\n" " " -#: users/utils.py:148 +#: users/utils.py:149 msgid "User not exist" msgstr "用户不存在" -#: users/utils.py:150 +#: users/utils.py:151 msgid "Disabled or expired" msgstr "禁用或失效" -#: users/utils.py:163 +#: users/utils.py:164 msgid "Password or SSH public key invalid" msgstr "密码或密钥不合法" @@ -3017,60 +3045,60 @@ msgstr "更新用户组" msgid "User group granted asset" msgstr "用户组授权资产" -#: users/views/login.py:62 +#: users/views/login.py:63 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: users/views/login.py:128 users/views/user.py:500 users/views/user.py:525 +#: users/views/login.py:159 users/views/user.py:500 users/views/user.py:525 msgid "MFA code invalid" msgstr "MFA码认证失败" -#: users/views/login.py:154 +#: users/views/login.py:188 msgid "Logout success" msgstr "退出登录成功" -#: users/views/login.py:155 +#: users/views/login.py:189 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: users/views/login.py:171 +#: users/views/login.py:205 msgid "Email address invalid, please input again" msgstr "邮箱地址错误,重新输入" -#: users/views/login.py:184 +#: users/views/login.py:218 msgid "Send reset password message" msgstr "发送重置密码邮件" -#: users/views/login.py:185 +#: users/views/login.py:219 msgid "Send reset password mail success, login your mail box and follow it " msgstr "" "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)" -#: users/views/login.py:198 +#: users/views/login.py:232 msgid "Reset password success" msgstr "重置密码成功" -#: users/views/login.py:199 +#: users/views/login.py:233 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: users/views/login.py:220 users/views/login.py:233 +#: users/views/login.py:254 users/views/login.py:267 msgid "Token invalid or expired" msgstr "Token错误或失效" -#: users/views/login.py:229 +#: users/views/login.py:263 msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:239 users/views/user.py:118 users/views/user.py:398 +#: users/views/login.py:273 users/views/user.py:118 users/views/user.py:398 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/views/login.py:277 +#: users/views/login.py:311 msgid "First login" msgstr "首次登陆" -#: users/views/login.py:336 +#: users/views/login.py:370 msgid "Login log list" msgstr "登录日志" diff --git a/apps/users/api.py b/apps/users/api.py index dbc5b66a8..5df312fcf 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -14,7 +14,7 @@ from .serializers import UserSerializer, UserGroupSerializer, \ UserGroupUpdateMemeberSerializer, UserPKUpdateSerializer, \ UserUpdateGroupSerializer, ChangeUserPasswordSerializer from .tasks import write_login_log_async -from .models import User, UserGroup +from .models import User, UserGroup, LoginLog from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly, \ IsSuperUserOrAppUser from .utils import check_user_valid, generate_token, get_login_ip, check_otp_code @@ -153,10 +153,25 @@ class UserOtpAuthApi(APIView): return Response({'msg': '请先进行用户名和密码验证'}, status=401) if not check_otp_code(user.otp_secret_key, otp_code): + # Write login failed log + kwargs = { + 'username': user.username, + 'mfa': int(user.otp_enabled), + 'reason': LoginLog.REASON_MFA, + 'status': False + } + self.write_login_log(request, **kwargs) return Response({'msg': 'MFA认证失败'}, status=401) + # Write login success log + kwargs = { + 'username': user.username, + 'mfa': int(user.otp_enabled), + 'reason': LoginLog.REASON_NOTHING, + 'status': True + } + self.write_login_log(request, **kwargs) token = generate_token(request, user) - self.write_login_log(request, user) return Response( { 'token': token, @@ -165,7 +180,7 @@ class UserOtpAuthApi(APIView): ) @staticmethod - def write_login_log(request, user): + def write_login_log(request, **kwargs): login_ip = request.data.get('remote_addr', None) login_type = request.data.get('login_type', '') user_agent = request.data.get('HTTP_USER_AGENT', '') @@ -173,10 +188,13 @@ class UserOtpAuthApi(APIView): if not login_ip: login_ip = get_login_ip(request) - write_login_log_async.delay( - user.username, ip=login_ip, - type=login_type, user_agent=user_agent, - ) + data = { + 'ip': login_ip, + 'type': login_type, + 'user_agent': user_agent + } + kwargs.update(data) + write_login_log_async.delay(**kwargs) class UserAuthApi(APIView): @@ -187,11 +205,26 @@ class UserAuthApi(APIView): user, msg = self.check_user_valid(request) if not user: + # Write login failed log + kwargs = { + 'username': request.data.get('username', ''), + 'mfa': LoginLog.MFA_UNKNOWN, + 'reason': LoginLog.REASON_PASSWORD, + 'status': False + } + self.write_login_log(request, **kwargs) return Response({'msg': msg}, status=401) if not user.otp_enabled: + # Write login success log + kwargs = { + 'username': user.username, + 'mfa': int(user.otp_enabled), + 'reason': LoginLog.REASON_NOTHING, + 'status': True + } + self.write_login_log(request, **kwargs) token = generate_token(request, user) - self.write_login_log(request, user) return Response( { 'token': token, @@ -208,7 +241,8 @@ class UserAuthApi(APIView): 'otp_url': reverse('api-users:user-otp-auth'), 'seed': seed, 'user': self.serializer_class(user).data - }, status=300) + }, status=300 + ) @staticmethod def check_user_valid(request): @@ -222,7 +256,7 @@ class UserAuthApi(APIView): return user, msg @staticmethod - def write_login_log(request, user): + def write_login_log(request, **kwargs): login_ip = request.data.get('remote_addr', None) login_type = request.data.get('login_type', '') user_agent = request.data.get('HTTP_USER_AGENT', '') @@ -230,10 +264,14 @@ class UserAuthApi(APIView): if not login_ip: login_ip = get_login_ip(request) - write_login_log_async.delay( - user.username, ip=login_ip, - type=login_type, user_agent=user_agent, - ) + data = { + 'ip': login_ip, + 'type': login_type, + 'user_agent': user_agent, + } + kwargs.update(data) + + write_login_log_async.delay(**kwargs) class UserConnectionTokenApi(APIView): diff --git a/apps/users/models/authentication.py b/apps/users/models/authentication.py index 5169a79d2..493e4bb59 100644 --- a/apps/users/models/authentication.py +++ b/apps/users/models/authentication.py @@ -41,12 +41,40 @@ class LoginLog(models.Model): ('W', 'Web'), ('T', 'Terminal'), ) + + MFA_DISABLED = 0 + MFA_ENABLED = 1 + MFA_UNKNOWN = 2 + + MFA_CHOICE = ( + (MFA_DISABLED, _('Disabled')), + (MFA_ENABLED, _('Enabled')), + (MFA_UNKNOWN, _('-')), + ) + + REASON_NOTHING = 0 + REASON_PASSWORD = 1 + REASON_MFA = 2 + + REASON_CHOICE = ( + (REASON_NOTHING, _('-')), + (REASON_PASSWORD, _('Username/password check failed')), + (REASON_MFA, _('MFA authentication failed')), + ) + + STATUS_CHOICE = ( + (True, _('Success')), + (False, _('Failed')) + ) id = models.UUIDField(default=uuid.uuid4, primary_key=True) username = models.CharField(max_length=20, verbose_name=_('Username')) type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, verbose_name=_('Login type')) ip = models.GenericIPAddressField(verbose_name=_('Login ip')) city = models.CharField(max_length=254, blank=True, null=True, verbose_name=_('Login city')) user_agent = models.CharField(max_length=254, blank=True, null=True, verbose_name=_('User agent')) + mfa = models.SmallIntegerField(default=MFA_DISABLED, choices=MFA_CHOICE, verbose_name=_('MFA')) + reason = models.SmallIntegerField(default=REASON_NOTHING, choices=REASON_CHOICE, verbose_name=_('Reason')) + status = models.BooleanField(max_length=2, default=True, choices=STATUS_CHOICE, verbose_name=_('Status')) datetime = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login')) class Meta: diff --git a/apps/users/templates/users/login_log_list.html b/apps/users/templates/users/login_log_list.html index 4a08c28db..afaf671a5 100644 --- a/apps/users/templates/users/login_log_list.html +++ b/apps/users/templates/users/login_log_list.html @@ -51,6 +51,9 @@ {% trans 'UA' %} {% trans 'IP' %} {% trans 'City' %} + {% trans 'MFA' %} + {% trans 'Reason' %} + {% trans 'Status' %} {% trans 'Date' %} {% endblock %} @@ -65,6 +68,9 @@ {{ login_log.ip }} {{ login_log.city }} + {{ login_log.get_mfa_display }} + {{ login_log.get_reason_display }} + {{ login_log.get_status_display }} {{ login_log.datetime }} {% endfor %} diff --git a/apps/users/utils.py b/apps/users/utils.py index 989632e2c..fb2a8d93e 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -13,7 +13,7 @@ import ipaddress from django.http import Http404 from django.conf import settings from django.contrib.auth.mixins import UserPassesTestMixin -from django.contrib.auth import authenticate, login as auth_login +from django.contrib.auth import authenticate from django.utils.translation import ugettext as _ from django.core.cache import cache @@ -22,6 +22,7 @@ from common.utils import reverse, get_object_or_none from common.models import Setting from common.forms import SecuritySettingForm from .models import User, LoginLog +# from .tasks import write_login_log_async logger = logging.getLogger('jumpserver') @@ -200,16 +201,15 @@ def get_login_ip(request): return login_ip -def write_login_log(username, type='', ip='', user_agent=''): +def write_login_log(*args, **kwargs): + ip = kwargs.get('ip', '') if not (ip and validate_ip(ip)): ip = ip[:15] city = "Unknown" else: city = get_ip_city(ip) - LoginLog.objects.create( - username=username, type=type, - ip=ip, city=city, user_agent=user_agent - ) + kwargs.update({'ip': ip, 'city': city}) + LoginLog.objects.create(**kwargs) def get_ip_city(ip, timeout=10): diff --git a/apps/users/views/login.py b/apps/users/views/login.py index feaf47e89..02f3b66af 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -25,8 +25,9 @@ from common.utils import get_object_or_none from common.mixins import DatetimeSearchMixin, AdminUserRequiredMixin from common.models import Setting from ..models import User, LoginLog -from ..utils import send_reset_password_mail, check_otp_code, get_login_ip, redirect_user_first_login_or_index, \ - get_user_or_tmp_user, set_tmp_user_to_cache, get_password_check_rules, check_password_rules +from ..utils import send_reset_password_mail, check_otp_code, get_login_ip, \ + redirect_user_first_login_or_index, get_user_or_tmp_user, \ + set_tmp_user_to_cache, get_password_check_rules, check_password_rules from ..tasks import write_login_log_async from .. import forms @@ -65,6 +66,15 @@ class UserLoginView(FormView): return redirect(self.get_success_url()) def form_invalid(self, form): + # Write login failed log + kwargs = { + 'username': form.cleaned_data.get('username'), + 'mfa': LoginLog.MFA_UNKNOWN, + 'reason': LoginLog.REASON_PASSWORD, + 'status': False + } + self.write_login_log(**kwargs) + ip = get_login_ip(self.request) cache.set(self.key_prefix.format(ip), 1, 3600) old_form = form @@ -91,7 +101,14 @@ class UserLoginView(FormView): elif not user.otp_enabled: # 0 & T,F auth_login(self.request, user) - self.write_login_log() + # Write login success log + kwargs = { + 'username': self.request.user.username, + 'mfa': int(self.request.user.otp_enabled), + 'reason': LoginLog.REASON_NOTHING, + 'status': True + } + self.write_login_log(**kwargs) return redirect_user_first_login_or_index(self.request, self.redirect_field_name) def get_context_data(self, **kwargs): @@ -101,13 +118,16 @@ class UserLoginView(FormView): kwargs.update(context) return super().get_context_data(**kwargs) - def write_login_log(self): + def write_login_log(self, **kwargs): login_ip = get_login_ip(self.request) user_agent = self.request.META.get('HTTP_USER_AGENT', '') - write_login_log_async.delay( - self.request.user.username, type='W', - ip=login_ip, user_agent=user_agent - ) + data = { + 'ip': login_ip, + 'type': 'W', + 'user_agent': user_agent + } + kwargs.update(data) + write_login_log_async.delay(**kwargs) class UserLoginOtpView(FormView): @@ -122,22 +142,40 @@ class UserLoginOtpView(FormView): if check_otp_code(otp_secret_key, otp_code): auth_login(self.request, user) - self.write_login_log() + # Write login success log + kwargs = { + 'username': self.request.user.username, + 'mfa': int(self.request.user.otp_enabled), + 'reason': LoginLog.REASON_NOTHING, + 'status': True + } + self.write_login_log(**kwargs) return redirect(self.get_success_url()) else: + # Write login failed log + kwargs = { + 'username': user.username, + 'mfa': int(user.otp_enabled), + 'reason': LoginLog.REASON_MFA, + 'status': False + } + self.write_login_log(**kwargs) form.add_error('otp_code', _('MFA code invalid')) return super().form_invalid(form) def get_success_url(self): return redirect_user_first_login_or_index(self.request, self.redirect_field_name) - def write_login_log(self): + def write_login_log(self, **kwargs): login_ip = get_login_ip(self.request) user_agent = self.request.META.get('HTTP_USER_AGENT', '') - write_login_log_async.delay( - self.request.user.username, type='W', - ip=login_ip, user_agent=user_agent - ) + data = { + 'ip': login_ip, + 'type': 'W', + 'user_agent': user_agent + } + kwargs.update(data) + write_login_log_async.delay(**kwargs) @method_decorator(never_cache, name='dispatch') From 43370c547ae94ed74aa5e37cbc14e3a396f64b28 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Wed, 4 Jul 2018 10:57:37 +0800 Subject: [PATCH 15/34] =?UTF-8?q?[Update]=20=E7=99=BB=E5=BD=95=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E9=80=BB=E8=BE=91=E4=BF=AE=E6=94=B9=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/api.py | 32 ++++++++++++++++---------------- apps/users/views/login.py | 32 ++++++++++++++++---------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/apps/users/api.py b/apps/users/api.py index 18457a2b4..dd1b76ba3 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -150,23 +150,23 @@ class UserOtpAuthApi(APIView): if not check_otp_code(user.otp_secret_key, otp_code): # Write login failed log - kwargs = { + data = { 'username': user.username, 'mfa': int(user.otp_enabled), 'reason': LoginLog.REASON_MFA, 'status': False } - self.write_login_log(request, **kwargs) + self.write_login_log(request, data) return Response({'msg': 'MFA认证失败'}, status=401) # Write login success log - kwargs = { + data = { 'username': user.username, 'mfa': int(user.otp_enabled), 'reason': LoginLog.REASON_NOTHING, 'status': True } - self.write_login_log(request, **kwargs) + self.write_login_log(request, data) token = generate_token(request, user) return Response( { @@ -176,7 +176,7 @@ class UserOtpAuthApi(APIView): ) @staticmethod - def write_login_log(request, **kwargs): + def write_login_log(request, data): login_ip = request.data.get('remote_addr', None) login_type = request.data.get('login_type', '') user_agent = request.data.get('HTTP_USER_AGENT', '') @@ -184,13 +184,13 @@ class UserOtpAuthApi(APIView): if not login_ip: login_ip = get_login_ip(request) - data = { + tmp_data = { 'ip': login_ip, 'type': login_type, 'user_agent': user_agent } - kwargs.update(data) - write_login_log_async.delay(**kwargs) + data.update(tmp_data) + write_login_log_async.delay(**data) class UserAuthApi(APIView): @@ -202,24 +202,24 @@ class UserAuthApi(APIView): if not user: # Write login failed log - kwargs = { + data = { 'username': request.data.get('username', ''), 'mfa': LoginLog.MFA_UNKNOWN, 'reason': LoginLog.REASON_PASSWORD, 'status': False } - self.write_login_log(request, **kwargs) + self.write_login_log(request, data) return Response({'msg': msg}, status=401) if not user.otp_enabled: # Write login success log - kwargs = { + data = { 'username': user.username, 'mfa': int(user.otp_enabled), 'reason': LoginLog.REASON_NOTHING, 'status': True } - self.write_login_log(request, **kwargs) + self.write_login_log(request, data) token = generate_token(request, user) return Response( { @@ -252,7 +252,7 @@ class UserAuthApi(APIView): return user, msg @staticmethod - def write_login_log(request, **kwargs): + def write_login_log(request, data): login_ip = request.data.get('remote_addr', None) login_type = request.data.get('login_type', '') user_agent = request.data.get('HTTP_USER_AGENT', '') @@ -260,14 +260,14 @@ class UserAuthApi(APIView): if not login_ip: login_ip = get_login_ip(request) - data = { + tmp_data = { 'ip': login_ip, 'type': login_type, 'user_agent': user_agent, } - kwargs.update(data) + data.update(tmp_data) - write_login_log_async.delay(**kwargs) + write_login_log_async.delay(**data) class UserConnectionTokenApi(APIView): diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 02f3b66af..7e853e45f 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -67,13 +67,13 @@ class UserLoginView(FormView): def form_invalid(self, form): # Write login failed log - kwargs = { + data = { 'username': form.cleaned_data.get('username'), 'mfa': LoginLog.MFA_UNKNOWN, 'reason': LoginLog.REASON_PASSWORD, 'status': False } - self.write_login_log(**kwargs) + self.write_login_log(data) ip = get_login_ip(self.request) cache.set(self.key_prefix.format(ip), 1, 3600) @@ -102,13 +102,13 @@ class UserLoginView(FormView): # 0 & T,F auth_login(self.request, user) # Write login success log - kwargs = { + data = { 'username': self.request.user.username, 'mfa': int(self.request.user.otp_enabled), 'reason': LoginLog.REASON_NOTHING, 'status': True } - self.write_login_log(**kwargs) + self.write_login_log(data) return redirect_user_first_login_or_index(self.request, self.redirect_field_name) def get_context_data(self, **kwargs): @@ -118,16 +118,16 @@ class UserLoginView(FormView): kwargs.update(context) return super().get_context_data(**kwargs) - def write_login_log(self, **kwargs): + def write_login_log(self, data): login_ip = get_login_ip(self.request) user_agent = self.request.META.get('HTTP_USER_AGENT', '') - data = { + tmp_data = { 'ip': login_ip, 'type': 'W', 'user_agent': user_agent } - kwargs.update(data) - write_login_log_async.delay(**kwargs) + data.update(tmp_data) + write_login_log_async.delay(**data) class UserLoginOtpView(FormView): @@ -143,39 +143,39 @@ class UserLoginOtpView(FormView): if check_otp_code(otp_secret_key, otp_code): auth_login(self.request, user) # Write login success log - kwargs = { + data = { 'username': self.request.user.username, 'mfa': int(self.request.user.otp_enabled), 'reason': LoginLog.REASON_NOTHING, 'status': True } - self.write_login_log(**kwargs) + self.write_login_log(data) return redirect(self.get_success_url()) else: # Write login failed log - kwargs = { + data = { 'username': user.username, 'mfa': int(user.otp_enabled), 'reason': LoginLog.REASON_MFA, 'status': False } - self.write_login_log(**kwargs) + self.write_login_log(data) form.add_error('otp_code', _('MFA code invalid')) return super().form_invalid(form) def get_success_url(self): return redirect_user_first_login_or_index(self.request, self.redirect_field_name) - def write_login_log(self, **kwargs): + def write_login_log(self, data): login_ip = get_login_ip(self.request) user_agent = self.request.META.get('HTTP_USER_AGENT', '') - data = { + tmp_data = { 'ip': login_ip, 'type': 'W', 'user_agent': user_agent } - kwargs.update(data) - write_login_log_async.delay(**kwargs) + data.update(tmp_data) + write_login_log_async.delay(**data) @method_decorator(never_cache, name='dispatch') From ff9b1a887fe914defd9db127cd1392c090c50e4a Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Wed, 4 Jul 2018 16:55:11 +0800 Subject: [PATCH 16/34] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=99=BB=E5=BD=95=E5=A4=B1=E8=B4=A5=E6=AC=A1=E6=95=B0?= =?UTF-8?q?=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 36216 -> 36297 bytes apps/i18n/zh/LC_MESSAGES/django.po | 44 ++++++++++++++------------ apps/users/templates/users/login.html | 6 +++- apps/users/views/login.py | 31 +++++++++++++----- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index 339af4456cb72a4e21841737e7c98bce3a39fc6a..b5474cbe1cdec6a6fb164cf841611e659e75bd94 100644 GIT binary patch delta 11843 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 delta 11771 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 V4I4LTwEf6{bJ6V6y-K diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index 4396c01fb..56fabe11a 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/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: 2018-07-03 16:48+0800\n" +"POT-Creation-Date: 2018-07-04 16:46+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -156,7 +156,7 @@ msgstr "名称" #: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13 #: users/forms.py:31 users/models/authentication.py:70 users/models/user.py:47 #: users/templates/users/_select_user_modal.html:14 -#: users/templates/users/login.html:56 +#: users/templates/users/login.html:60 #: users/templates/users/login_log_list.html:49 #: users/templates/users/user_detail.html:67 #: users/templates/users/user_list.html:24 @@ -170,7 +170,7 @@ msgstr "密码或密钥密码" #: assets/forms/user.py:25 assets/models/base.py:23 common/forms.py:113 #: users/forms.py:15 users/forms.py:33 users/forms.py:45 -#: users/templates/users/login.html:59 +#: users/templates/users/login.html:63 #: users/templates/users/reset_password.html:53 #: users/templates/users/user_create.html:10 #: users/templates/users/user_password_authentication.html:14 @@ -2006,7 +2006,7 @@ msgid "Logout" msgstr "注销登录" #: templates/_header_bar.html:49 users/templates/users/login.html:44 -#: users/templates/users/login.html:64 +#: users/templates/users/login.html:68 msgid "Login" msgstr "登录" @@ -2046,7 +2046,7 @@ msgstr "关闭" #: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44 #: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95 -#: users/views/login.py:311 users/views/login.py:369 users/views/user.py:65 +#: users/views/login.py:330 users/views/login.py:388 users/views/user.py:65 #: users/views/user.py:80 users/views/user.py:102 users/views/user.py:175 #: users/views/user.py:330 users/views/user.py:380 users/views/user.py:415 msgid "Users" @@ -2641,7 +2641,7 @@ msgid " for more information" msgstr "获取更多信息" #: users/templates/users/forgot_password.html:26 -#: users/templates/users/login.html:73 +#: users/templates/users/login.html:77 msgid "Forgot password" msgstr "忘记密码" @@ -2650,6 +2650,10 @@ msgid "Input your email, that will send a mail to your" msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中" #: users/templates/users/login.html:50 +msgid "Log in frequently and try again later" +msgstr "登录频繁, 稍后重试" + +#: users/templates/users/login.html:53 msgid "Captcha invalid" msgstr "验证码错误" @@ -3045,60 +3049,60 @@ msgstr "更新用户组" msgid "User group granted asset" msgstr "用户组授权资产" -#: users/views/login.py:63 +#: users/views/login.py:74 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: users/views/login.py:159 users/views/user.py:500 users/views/user.py:525 +#: users/views/login.py:178 users/views/user.py:500 users/views/user.py:525 msgid "MFA code invalid" msgstr "MFA码认证失败" -#: users/views/login.py:188 +#: users/views/login.py:207 msgid "Logout success" msgstr "退出登录成功" -#: users/views/login.py:189 +#: users/views/login.py:208 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: users/views/login.py:205 +#: users/views/login.py:224 msgid "Email address invalid, please input again" msgstr "邮箱地址错误,重新输入" -#: users/views/login.py:218 +#: users/views/login.py:237 msgid "Send reset password message" msgstr "发送重置密码邮件" -#: users/views/login.py:219 +#: users/views/login.py:238 msgid "Send reset password mail success, login your mail box and follow it " msgstr "" "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)" -#: users/views/login.py:232 +#: users/views/login.py:251 msgid "Reset password success" msgstr "重置密码成功" -#: users/views/login.py:233 +#: users/views/login.py:252 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: users/views/login.py:254 users/views/login.py:267 +#: users/views/login.py:273 users/views/login.py:286 msgid "Token invalid or expired" msgstr "Token错误或失效" -#: users/views/login.py:263 +#: users/views/login.py:282 msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:273 users/views/user.py:118 users/views/user.py:398 +#: users/views/login.py:292 users/views/user.py:118 users/views/user.py:398 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/views/login.py:311 +#: users/views/login.py:330 msgid "First login" msgstr "首次登陆" -#: users/views/login.py:370 +#: users/views/login.py:389 msgid "Login log list" msgstr "登录日志" diff --git a/apps/users/templates/users/login.html b/apps/users/templates/users/login.html index 7dd3f5d0a..7ea824502 100644 --- a/apps/users/templates/users/login.html +++ b/apps/users/templates/users/login.html @@ -45,13 +45,17 @@
{% csrf_token %} - {% if form.errors %} + + {% if login_limit %} +

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

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

{% trans 'Captcha invalid' %}

{% else %}

{{ form.non_field_errors.as_text }}

{% endif %} {% endif %} +
diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 7e853e45f..f58ef7b8f 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -48,7 +48,8 @@ class UserLoginView(FormView): form_class = forms.UserLoginForm form_class_captcha = forms.UserLoginCaptchaForm redirect_field_name = 'next' - key_prefix = "_LOGIN_INVALID_{}" + key_prefix_captcha = "_LOGIN_INVALID_{}" + key_prefix_limit = "_LOGIN_LIMIT_{}_{}" def get(self, request, *args, **kwargs): if request.user.is_staff: @@ -58,6 +59,16 @@ 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_login_ip(request) + username = self.request.POST.get('username') + count = cache.get(self.key_prefix_limit.format(ip, username)) + if count and count >= 3: + return self.render_to_response(self.get_context_data(login_limit=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.")) @@ -66,17 +77,24 @@ class UserLoginView(FormView): return redirect(self.get_success_url()) def form_invalid(self, form): - # Write login failed log + # write login failed log + username = form.cleaned_data.get('username') data = { - 'username': form.cleaned_data.get('username'), + 'username': username, 'mfa': LoginLog.MFA_UNKNOWN, 'reason': LoginLog.REASON_PASSWORD, 'status': False } self.write_login_log(data) + # limit user login failed times ip = get_login_ip(self.request) - cache.set(self.key_prefix.format(ip), 1, 3600) + key_limit = self.key_prefix_limit.format(ip, username) + count = cache.get(key_limit) + count = count + 1 if count else 1 + cache.set(key_limit, count, 1800) + + cache.set(self.key_prefix_captcha.format(ip), 1, 3600) old_form = form form = self.form_class_captcha(data=form.data) form._errors = old_form.errors @@ -84,7 +102,7 @@ class UserLoginView(FormView): def get_form_class(self): ip = get_login_ip(self.request) - if cache.get(self.key_prefix.format(ip)): + if cache.get(self.key_prefix_captcha.format(ip)): return self.form_class_captcha else: return self.form_class @@ -101,7 +119,6 @@ class UserLoginView(FormView): elif not user.otp_enabled: # 0 & T,F auth_login(self.request, user) - # Write login success log data = { 'username': self.request.user.username, 'mfa': int(self.request.user.otp_enabled), @@ -142,7 +159,6 @@ class UserLoginOtpView(FormView): if check_otp_code(otp_secret_key, otp_code): auth_login(self.request, user) - # Write login success log data = { 'username': self.request.user.username, 'mfa': int(self.request.user.otp_enabled), @@ -152,7 +168,6 @@ class UserLoginOtpView(FormView): self.write_login_log(data) return redirect(self.get_success_url()) else: - # Write login failed log data = { 'username': user.username, 'mfa': int(user.otp_enabled), From f8c2a445f7b65db5d130f781400d74e5c5415f2c Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 5 Jul 2018 10:26:16 +0800 Subject: [PATCH 17/34] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E6=94=B9gateway=20po?= =?UTF-8?q?rt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/assets/utils.py b/apps/assets/utils.py index cc4942ade..f841e4a79 100644 --- a/apps/assets/utils.py +++ b/apps/assets/utils.py @@ -54,7 +54,8 @@ def test_gateway_connectability(gateway): proxy.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: - proxy.connect(gateway.ip, username=gateway.username, + proxy.connect(gateway.ip, gateway.port, + username=gateway.username, password=gateway.password, pkey=gateway.private_key_obj) except(paramiko.AuthenticationException, From 37bb344166f8fe77212772de965aeff170dfc8a3 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 5 Jul 2018 10:58:52 +0800 Subject: [PATCH 18/34] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E7=BD=91?= =?UTF-8?q?=E5=85=B3=E7=AB=AF=E5=8F=A3=E4=B8=8D=E5=AF=B9=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/templates/assets/gateway_create_update.html | 4 ++-- apps/assets/views/domain.py | 5 ----- utils/make_migrations.sh | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/assets/templates/assets/gateway_create_update.html b/apps/assets/templates/assets/gateway_create_update.html index e0b10ba73..7c1bf14b2 100644 --- a/apps/assets/templates/assets/gateway_create_update.html +++ b/apps/assets/templates/assets/gateway_create_update.html @@ -75,10 +75,10 @@ var port = '#' + '{{ form.port.id_for_label }}'; function protocolChange() { if ($(protocol_id + " option:selected").text() === 'rdp') { - $(port).val(3389); + {#$(port).val(3389);#} $(private_key_id).closest('.form-group').addClass('hidden') } else { - $(port).val(22); + {#$(port).val(22);#} $(private_key_id).closest('.form-group').removeClass('hidden') } } diff --git a/apps/assets/views/domain.py b/apps/assets/views/domain.py index e6a353373..be6528219 100644 --- a/apps/assets/views/domain.py +++ b/apps/assets/views/domain.py @@ -140,11 +140,6 @@ class DomainGatewayUpdateView(AdminUserRequiredMixin, UpdateView): domain = self.object.domain return reverse('assets:domain-gateway-list', kwargs={"pk": domain.id}) - def form_valid(self, form): - response = super().form_valid(form) - print(form.cleaned_data) - return response - def get_context_data(self, **kwargs): context = { 'app': _('Assets'), diff --git a/utils/make_migrations.sh b/utils/make_migrations.sh index b6068a1ca..fbe3e577c 100755 --- a/utils/make_migrations.sh +++ b/utils/make_migrations.sh @@ -5,4 +5,4 @@ python3 ../apps/manage.py makemigrations python3 ../apps/manage.py migrate -python3 ../apps/manage.py makemigrations –merge +python3 ../apps/manage.py makemigrations –-merge From 512fc8f8f0fa2ce46f3d03850c2871acf426a15c Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Thu, 5 Jul 2018 16:23:33 +0800 Subject: [PATCH 19/34] =?UTF-8?q?[Update]=20=E4=BC=98=E5=8C=96=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=A4=B1=E8=B4=A5=E6=AC=A1=E6=95=B0=E9=99=90=E5=88=B6?= =?UTF-8?q?=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8C=E5=B9=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E5=AE=89=E5=85=A8=E8=AE=BE=E7=BD=AE=E9=80=89?= =?UTF-8?q?=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/forms.py | 26 +++++++-- .../templates/common/security_setting.html | 4 +- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 36297 -> 36708 bytes apps/i18n/zh/LC_MESSAGES/django.po | 52 ++++++++++++------ apps/jumpserver/settings.py | 2 + apps/users/api.py | 20 +++++-- apps/users/templates/users/login.html | 2 +- apps/users/utils.py | 26 +++++++++ apps/users/views/login.py | 16 +++--- 9 files changed, 109 insertions(+), 39 deletions(-) diff --git a/apps/common/forms.py b/apps/common/forms.py index 87b2f4f12..a11420498 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -170,7 +170,7 @@ class TerminalSettingForm(BaseForm): class SecuritySettingForm(BaseForm): - # MFA全局设置 + # MFA global setting SECURITY_MFA_AUTH = forms.BooleanField( initial=False, required=False, label=_("MFA Secondary certification"), @@ -179,12 +179,26 @@ class SecuritySettingForm(BaseForm): 'authentication (valid for all users, including administrators)' ) ) - # 最小长度 + # limit login count + SECURITY_LOGIN_LIMIT_COUNT = forms.IntegerField( + initial=3, min_value=3, + label=_("Limit the number of login failures") + ) + # limit login time + SECURITY_LOGIN_LIMIT_TIME = forms.IntegerField( + initial=30, min_value=5, + label=_("No logon interval"), + help_text=_( + "Tip :(unit/minute) if the user has failed to log in for a limited " + "number of times, no login is allowed during this time interval." + ) + ) + # min length SECURITY_PASSWORD_MIN_LENGTH = forms.IntegerField( initial=6, label=_("Password minimum length"), min_value=6 ) - # 大写字母 + # upper case SECURITY_PASSWORD_UPPER_CASE = forms.BooleanField( initial=False, required=False, @@ -193,21 +207,21 @@ class SecuritySettingForm(BaseForm): 'After opening, the user password changes ' 'and resets must contain uppercase letters') ) - # 小写字母 + # lower case SECURITY_PASSWORD_LOWER_CASE = forms.BooleanField( initial=False, required=False, label=_("Must contain lowercase letters"), help_text=_('After opening, the user password changes ' 'and resets must contain lowercase letters') ) - # 数字 + # number SECURITY_PASSWORD_NUMBER = forms.BooleanField( initial=False, required=False, label=_("Must contain numeric characters"), help_text=_('After opening, the user password changes ' 'and resets must contain numeric characters') ) - # 特殊字符 + # special char SECURITY_PASSWORD_SPECIAL_CHAR= forms.BooleanField( initial=False, required=False, label=_("Must contain special characters"), diff --git a/apps/common/templates/common/security_setting.html b/apps/common/templates/common/security_setting.html index 2260b76b9..08d978d23 100644 --- a/apps/common/templates/common/security_setting.html +++ b/apps/common/templates/common/security_setting.html @@ -39,9 +39,9 @@ {% endif %} {% csrf_token %} -

{% trans "MFA setting" %}

+

{% trans "User login settings" %}

{% for field in form %} - {% if forloop.counter == 2 %} + {% if forloop.counter == 4 %}

{% trans "Password check rule" %}

{% endif %} diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index b5474cbe1cdec6a6fb164cf841611e659e75bd94..c9b42d9f4194e85347abe5bcb51aa9c2a1901b5a 100644 GIT binary patch delta 12198 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 delta 11803 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 diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index 56fabe11a..b1e588650 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/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: 2018-07-04 16:46+0800\n" +"POT-Creation-Date: 2018-07-05 14:58+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -1433,45 +1433,60 @@ msgid "" "for all users, including administrators)" msgstr "开启后,用户登录必须使用MFA二次认证(对所有用户有效,包括管理员)" -#: common/forms.py:184 +#: common/forms.py:186 +msgid "Limit the number of login failures" +msgstr "限制登录失败次数" + +#: common/forms.py:193 +msgid "No logon interval" +msgstr "禁止登录时间间隔" + +#: common/forms.py:195 +msgid "" +"Tip :(unit/minute) if the user has failed to log in for a limited number of " +"times, no login is allowed during this time interval." +msgstr "" +"提示:(单位 / 分钟)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录." + +#: common/forms.py:201 msgid "Password minimum length" msgstr "密码最小长度 " -#: common/forms.py:191 +#: common/forms.py:208 msgid "Must contain capital letters" msgstr "必须包含大写字母" -#: common/forms.py:193 +#: common/forms.py:210 msgid "" "After opening, the user password changes and resets must contain uppercase " "letters" msgstr "开启后,用户密码修改、重置必须包含大写字母" -#: common/forms.py:199 +#: common/forms.py:216 msgid "Must contain lowercase letters" msgstr "必须包含小写字母" -#: common/forms.py:200 +#: common/forms.py:217 msgid "" "After opening, the user password changes and resets must contain lowercase " "letters" msgstr "开启后,用户密码修改、重置必须包含小写字母" -#: common/forms.py:206 +#: common/forms.py:223 msgid "Must contain numeric characters" msgstr "必须包含数字字符" -#: common/forms.py:207 +#: common/forms.py:224 msgid "" "After opening, the user password changes and resets must contain numeric " "characters" msgstr "开启后,用户密码修改、重置必须包含数字字符" -#: common/forms.py:213 +#: common/forms.py:230 msgid "Must contain special characters" msgstr "必须包含特殊字符" -#: common/forms.py:214 +#: common/forms.py:231 msgid "" "After opening, the user password changes and resets must contain special " "characters" @@ -1532,8 +1547,8 @@ msgid "Security setting" msgstr "安全设置" #: common/templates/common/security_setting.html:42 -msgid "MFA setting" -msgstr "MFA 设置" +msgid "User login settings" +msgstr "用户登录设置" #: common/templates/common/security_setting.html:46 msgid "Password check rule" @@ -2307,6 +2322,10 @@ msgid "" "You should use your ssh client tools connect terminal: {}

{}" msgstr "你可以使用ssh客户端工具连接终端" +#: users/api.py:210 users/templates/users/login.html:50 +msgid "Log in frequently and try again later" +msgstr "登录频繁, 稍后重试" + #: users/authentication.py:56 msgid "Invalid signature header. No credentials provided." msgstr "" @@ -2649,10 +2668,6 @@ msgstr "忘记密码" msgid "Input your email, that will send a mail to your" msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中" -#: users/templates/users/login.html:50 -msgid "Log in frequently and try again later" -msgstr "登录频繁, 稍后重试" - #: users/templates/users/login.html:53 msgid "Captcha invalid" msgstr "验证码错误" @@ -3049,7 +3064,7 @@ msgstr "更新用户组" msgid "User group granted asset" msgstr "用户组授权资产" -#: users/views/login.py:74 +#: users/views/login.py:75 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" @@ -3149,3 +3164,6 @@ msgstr "MFA 解绑成功" #: users/views/user.py:555 msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" + +#~ msgid "MFA setting" +#~ msgstr "MFA 设置" diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index c8ed1505e..c0a536276 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -405,6 +405,8 @@ TERMINAL_REPLAY_STORAGE = { DEFAULT_PASSWORD_MIN_LENGTH = 6 +DEFAULT_LOGIN_LIMIT_COUNT = 3 +DEFAULT_LOGIN_LIMIT_TIME = 30 # Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html BOOTSTRAP3 = { diff --git a/apps/users/api.py b/apps/users/api.py index dd1b76ba3..cf13b9aea 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -3,6 +3,7 @@ import uuid from django.core.cache import cache from django.urls import reverse +from django.utils.translation import ugettext as _ from rest_framework import generics from rest_framework.permissions import AllowAny, IsAuthenticated @@ -17,7 +18,8 @@ from .tasks import write_login_log_async from .models import User, UserGroup, LoginLog from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly, \ IsSuperUserOrAppUser -from .utils import check_user_valid, generate_token, get_login_ip, check_otp_code +from .utils import check_user_valid, generate_token, get_login_ip, \ + check_otp_code, set_user_login_failed_count_to_cache, is_block_login from common.mixins import IDInFilterMixin from common.utils import get_logger @@ -149,7 +151,6 @@ class UserOtpAuthApi(APIView): return Response({'msg': '请先进行用户名和密码验证'}, status=401) if not check_otp_code(user.otp_secret_key, otp_code): - # Write login failed log data = { 'username': user.username, 'mfa': int(user.otp_enabled), @@ -159,7 +160,6 @@ class UserOtpAuthApi(APIView): self.write_login_log(request, data) return Response({'msg': 'MFA认证失败'}, status=401) - # Write login success log data = { 'username': user.username, 'mfa': int(user.otp_enabled), @@ -196,12 +196,21 @@ class UserOtpAuthApi(APIView): class UserAuthApi(APIView): permission_classes = (AllowAny,) serializer_class = UserSerializer + key_prefix_limit = "_LOGIN_LIMIT_{}_{}" def post(self, request): user, msg = self.check_user_valid(request) + username = request.data.get('username') + ip = request.data.get('remote_addr', None) + if not ip: + ip = get_login_ip(request) + key_limit = self.key_prefix_limit.format(ip, username) + if is_block_login(key_limit): + msg = _("Log in frequently and try again later") + return Response({'msg': msg}, status=401) + if not user: - # Write login failed log data = { 'username': request.data.get('username', ''), 'mfa': LoginLog.MFA_UNKNOWN, @@ -209,10 +218,11 @@ class UserAuthApi(APIView): 'status': False } self.write_login_log(request, data) + + set_user_login_failed_count_to_cache(key_limit) return Response({'msg': msg}, status=401) if not user.otp_enabled: - # Write login success log data = { 'username': user.username, 'mfa': int(user.otp_enabled), diff --git a/apps/users/templates/users/login.html b/apps/users/templates/users/login.html index 7ea824502..6b55a58bf 100644 --- a/apps/users/templates/users/login.html +++ b/apps/users/templates/users/login.html @@ -46,7 +46,7 @@ {% csrf_token %} - {% if login_limit %} + {% if block_login %}

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

{% elif form.errors %} {% if 'captcha' in form.errors %} diff --git a/apps/users/utils.py b/apps/users/utils.py index fb2a8d93e..bc03eecbd 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -332,3 +332,29 @@ def check_password_rules(password): match_obj = re.match(pattern, password) return bool(match_obj) + + +def set_user_login_failed_count_to_cache(key_limit): + count = cache.get(key_limit) + count = count + 1 if count else 1 + + setting_limit_time = Setting.objects.filter( + name='SECURITY_LOGIN_LIMIT_TIME' + ).first() + limit_time = setting_limit_time.cleaned_value if setting_limit_time \ + else settings.DEFAULT_LOGIN_LIMIT_TIME + + cache.set(key_limit, count, int(limit_time)*60) + + +def is_block_login(key_limit): + count = cache.get(key_limit) + + setting_limit_count = Setting.objects.filter( + name='SECURITY_LOGIN_LIMIT_COUNT' + ).first() + limit_count = setting_limit_count.cleaned_value if setting_limit_count \ + else settings.DEFAULT_LOGIN_LIMIT_COUNT + + if count and count >= limit_count: + return True diff --git a/apps/users/views/login.py b/apps/users/views/login.py index f58ef7b8f..94071924f 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -27,7 +27,8 @@ from common.models import Setting from ..models import User, LoginLog from ..utils import send_reset_password_mail, check_otp_code, get_login_ip, \ redirect_user_first_login_or_index, get_user_or_tmp_user, \ - set_tmp_user_to_cache, get_password_check_rules, check_password_rules + set_tmp_user_to_cache, get_password_check_rules, check_password_rules, \ + is_block_login, set_user_login_failed_count_to_cache from ..tasks import write_login_log_async from .. import forms @@ -63,9 +64,9 @@ class UserLoginView(FormView): # limit login authentication ip = get_login_ip(request) username = self.request.POST.get('username') - count = cache.get(self.key_prefix_limit.format(ip, username)) - if count and count >= 3: - return self.render_to_response(self.get_context_data(login_limit=True)) + key_limit = self.key_prefix_limit.format(ip, username) + if is_block_login(key_limit): + return self.render_to_response(self.get_context_data(block_login=True)) return super().post(request, *args, **kwargs) @@ -87,13 +88,12 @@ class UserLoginView(FormView): } self.write_login_log(data) - # limit user login failed times + # limit user login failed count ip = get_login_ip(self.request) key_limit = self.key_prefix_limit.format(ip, username) - count = cache.get(key_limit) - count = count + 1 if count else 1 - cache.set(key_limit, count, 1800) + set_user_login_failed_count_to_cache(key_limit) + # show captcha cache.set(self.key_prefix_captcha.format(ip), 1, 3600) old_form = form form = self.form_class_captcha(data=form.data) From bd14266abd0168dc23236b45193d5da469cbf07d Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Thu, 5 Jul 2018 16:45:05 +0800 Subject: [PATCH 20/34] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=97=A5=E5=BF=97=E6=A8=A1=E5=9E=8B=E7=B1=BB=E7=9A=84?= =?UTF-8?q?MFA=E5=AD=97=E6=AE=B5=E9=BB=98=E8=AE=A4=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E4=B8=BA-?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/models/authentication.py | 2 +- apps/users/utils.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/users/models/authentication.py b/apps/users/models/authentication.py index 493e4bb59..cb0b7d85f 100644 --- a/apps/users/models/authentication.py +++ b/apps/users/models/authentication.py @@ -72,7 +72,7 @@ class LoginLog(models.Model): ip = models.GenericIPAddressField(verbose_name=_('Login ip')) city = models.CharField(max_length=254, blank=True, null=True, verbose_name=_('Login city')) user_agent = models.CharField(max_length=254, blank=True, null=True, verbose_name=_('User agent')) - mfa = models.SmallIntegerField(default=MFA_DISABLED, choices=MFA_CHOICE, verbose_name=_('MFA')) + mfa = models.SmallIntegerField(default=MFA_UNKNOWN, choices=MFA_CHOICE, verbose_name=_('MFA')) reason = models.SmallIntegerField(default=REASON_NOTHING, choices=REASON_CHOICE, verbose_name=_('Reason')) status = models.BooleanField(max_length=2, default=True, choices=STATUS_CHOICE, verbose_name=_('Status')) datetime = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login')) diff --git a/apps/users/utils.py b/apps/users/utils.py index bc03eecbd..937a90867 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -22,7 +22,6 @@ from common.utils import reverse, get_object_or_none from common.models import Setting from common.forms import SecuritySettingForm from .models import User, LoginLog -# from .tasks import write_login_log_async logger = logging.getLogger('jumpserver') From 5d800fa629010a96268b62bf8948b53350b057bd Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Thu, 5 Jul 2018 17:07:03 +0800 Subject: [PATCH 21/34] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9coco?= =?UTF-8?q?=E7=AB=AF=E7=99=BB=E5=BD=95=E9=99=90=E5=88=B6=E6=AC=A1=E6=95=B0?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/api.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/users/api.py b/apps/users/api.py index cf13b9aea..c23112384 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -199,17 +199,16 @@ class UserAuthApi(APIView): key_prefix_limit = "_LOGIN_LIMIT_{}_{}" def post(self, request): - user, msg = self.check_user_valid(request) - + # limit login username = request.data.get('username') ip = request.data.get('remote_addr', None) - if not ip: - ip = get_login_ip(request) + ip = ip if ip else get_login_ip(request) key_limit = self.key_prefix_limit.format(ip, username) if is_block_login(key_limit): msg = _("Log in frequently and try again later") return Response({'msg': msg}, status=401) + user, msg = self.check_user_valid(request) if not user: data = { 'username': request.data.get('username', ''), From 814e6a7df862a0f6103b5ba29273e486d44bb092 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 6 Jul 2018 10:34:16 +0800 Subject: [PATCH 22/34] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9sshpass=20?= =?UTF-8?q?=E5=BC=95=E5=8F=B7=E5=BC=95=E5=85=A5=E5=AF=86=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E7=89=B9=E6=AE=8A=E5=AD=97=E7=AC=A6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/inventory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ops/inventory.py b/apps/ops/inventory.py index b7d3ef487..7ed89e75c 100644 --- a/apps/ops/inventory.py +++ b/apps/ops/inventory.py @@ -93,7 +93,7 @@ class JMSInventory(BaseInventory): if gateway.password: proxy_command_list.insert( - 0, "sshpass -p {}".format(gateway.password) + 0, "sshpass -p '{}'".format(gateway.password) ) if gateway.private_key: proxy_command_list.append("-i {}".format(gateway.private_key_file)) From 435acafccd608708bf872168fcac1a8378615ae1 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Fri, 6 Jul 2018 13:22:20 +0800 Subject: [PATCH 23/34] =?UTF-8?q?[Update]=20=E5=9C=A8=E7=BA=BF=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=EF=BC=8C=E5=8E=86=E5=8F=B2=E4=BC=9A=E8=AF=9D=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E7=99=BB=E5=BD=95=E6=9D=A5=E6=BA=90=20Web/SSH=20Termi?= =?UTF-8?q?nal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 36708 -> 36764 bytes apps/i18n/zh/LC_MESSAGES/django.po | 76 +++++++++--------- .../templates/terminal/session_list.html | 2 + 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index c9b42d9f4194e85347abe5bcb51aa9c2a1901b5a..78d56177ea093bf518cc027eff07aab406c1a7c5 100644 GIT binary patch delta 12334 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 Dc~FH! delta 12257 zcmY+~2YgT0|HttwA(9Z0gd~I*A&40go7f|Et%|K$ikdB=xHi?IMr*Y-J5V}MEj5cO ztskwu)gHAd|pnw;2V z$El93unbPe8n_vYJC5r-_6kmsijEUNLo?Ju+F({3WcBIh7!0L;GWz3ORR4t-j^AP+ z?!y>7gxTh84Jf_Ydm5mRH2^vozWDs;6c z*c2nNBkKIYRzDimZyIWx1=juXwy8JqyXG{#8&D)x~IRYwi85eXu#QI{U8^CRxS1sE20(au=LssEJOaCc21P z=uK3=Cs-D<@JQOfOF{lN^qZU{OHGT?ehZ>{CZH_F^bvjx_Pt<^eP$!N;ZQ*ot zKI)2=qZYCnHPKeo&i#mbTaKb`*$vcq_pJSy)#s??otGN}_5SClpcCU!E3AsX6{8l= z7z<(>jKw!l{pO%1nvdGK#i()CpeEjdTJU~Mz@wJ?)%Ko|Y?zDroiGX|Fdj8fOVq$! zP&@H5Y9T{WS3DLq@LQ;Uvn-#Fdia*2`h8~YU!um{fCX_E>io;-s^e`6TJb~Ffc|y7 zJlKpx-Lt}|fy$z8K@HT_w?K{471gg7YKLCOe3*{f(f3gkFGlUuiaPASp2E#k=pj3d zo$xB^wW?p&o9GSH0w<#eo{bu49%{lRs4HKCnrI!y;}-P8OXgK9On$@6QIGvEM@3>i zZ^9nfj(iq2#%rh@sZigWs48k94e>i{jhQi|fwzEg)WV}t3v6VzLhW1{>dJeg7CO?U zpaCbL?(r_1#-jK(YJ#s&6Kp^YxC3?GKC3@rUNY~X7V;R?&##f=6v8m{#|p>? zlj~HXkcW!asE6t`3`7@o#ZypQHXq}0HR@S8gj(Pg)C3{iFZIudnz%G-XOmDDPz$qS z1Jn*Q#|XXuT`1@&9gKRvXJ7$bWA4Ww@>{4|@d!2WQ`AlcrF!*|sD3dRh(%EsRL0tC zVK8}p)Ph=KnBM<(6twc*s9&q;sHb)gYQ=Lg7#E=?T#4F&4X7*Mi|ThAHSu}W7GJme zTd4kjp(YM$;_Xld=RZ@;Cx@MXOO0ZAJZh-HF-pxOo}1@IO%l{*8L* z{F{4UID%0Nh(qmINz{c^NA+uuIk1aMK`VX@_0=lf@)=m1d?D(DotTFEFbBrB@JvQ6 zq#kOasaOHKTKjC&d%hG);dhuHFQYEd&D_#^Sb|U!g`lo97iwXJP`9Eq>Pl;wsTe}u z#_}F!KdT>#+VTnJGz=wwA9VqrAoID-dJ5X|?=dItM=jti>fyVIn#gJ8)#pIf$DkHi z7PY`?*4`X-WgSu9{kma2Oh-M0TTl=42@KKue~*IhmA<5FWr1cCYQ^!Wt*vPFO;8I+ zLtSBi9Du`c5MDuz*P*r7-W_#|`k)p(5R2hR4AA?(oPt)m3iVX(R0ICUJc1hVG-`pD zP|wDF)RpFM<4sf;bzVt~!1AbPssZYrcS0>-1cu=xbXAx~AskntZoy{ME!dAb;UreT zD_9a^+Ir7IL$ej?ypE{z`k`)FI_kRsJ{!AqHf74)PgReZrwE;i} NNLVwWWD?b?X|S`nN@W#HYCw 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 J7lwZq`CrhyeTV=6 diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index b1e588650..0cf576ebe 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/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: 2018-07-05 14:58+0800\n" +"POT-Creation-Date: 2018-07-06 13:11+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -480,7 +480,7 @@ msgstr "手动登录" #: assets/views/asset.py:197 assets/views/domain.py:29 #: assets/views/domain.py:45 assets/views/domain.py:61 #: assets/views/domain.py:74 assets/views/domain.py:98 -#: assets/views/domain.py:126 assets/views/domain.py:150 +#: assets/views/domain.py:126 assets/views/domain.py:145 #: assets/views/label.py:26 assets/views/label.py:42 assets/views/label.py:58 #: assets/views/system_user.py:28 assets/views/system_user.py:44 #: assets/views/system_user.py:60 assets/views/system_user.py:74 @@ -685,7 +685,7 @@ msgstr "重置" #: common/templates/common/security_setting.html:71 #: common/templates/common/terminal_setting.html:108 #: perms/templates/perms/asset_permission_create_update.html:70 -#: terminal/templates/terminal/session_list.html:124 +#: terminal/templates/terminal/session_list.html:126 #: terminal/templates/terminal/terminal_update.html:48 #: users/templates/users/_user.html:47 #: users/templates/users/forgot_password.html:44 @@ -847,7 +847,7 @@ msgstr "比例" #: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64 #: ops/templates/ops/task_history.html:65 ops/templates/ops/task_list.html:42 #: perms/templates/perms/asset_permission_list.html:60 -#: terminal/templates/terminal/session_list.html:80 +#: terminal/templates/terminal/session_list.html:81 #: terminal/templates/terminal/terminal_list.html:36 #: users/templates/users/user_group_list.html:15 #: users/templates/users/user_list.html:29 @@ -1189,7 +1189,7 @@ msgstr "网域详情" msgid "Domain gateway list" msgstr "域网关列表" -#: assets/views/domain.py:151 +#: assets/views/domain.py:146 msgid "Update gateway" msgstr "创建网关" @@ -1246,7 +1246,7 @@ msgstr "成功" #: ops/templates/ops/adhoc_history_detail.html:61 #: ops/templates/ops/task_history.html:58 perms/models.py:36 #: perms/templates/perms/asset_permission_detail.html:86 terminal/models.py:137 -#: terminal/templates/terminal/session_list.html:77 +#: terminal/templates/terminal/session_list.html:78 msgid "Date start" msgstr "开始日期" @@ -1433,60 +1433,60 @@ msgid "" "for all users, including administrators)" msgstr "开启后,用户登录必须使用MFA二次认证(对所有用户有效,包括管理员)" -#: common/forms.py:186 +#: common/forms.py:185 msgid "Limit the number of login failures" msgstr "限制登录失败次数" -#: common/forms.py:193 +#: common/forms.py:190 msgid "No logon interval" msgstr "禁止登录时间间隔" -#: common/forms.py:195 +#: common/forms.py:192 msgid "" "Tip :(unit/minute) if the user has failed to log in for a limited number of " "times, no login is allowed during this time interval." msgstr "" "提示:(单位 / 分钟)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录." -#: common/forms.py:201 +#: common/forms.py:198 msgid "Password minimum length" msgstr "密码最小长度 " -#: common/forms.py:208 +#: common/forms.py:205 msgid "Must contain capital letters" msgstr "必须包含大写字母" -#: common/forms.py:210 +#: common/forms.py:207 msgid "" "After opening, the user password changes and resets must contain uppercase " "letters" msgstr "开启后,用户密码修改、重置必须包含大写字母" -#: common/forms.py:216 +#: common/forms.py:213 msgid "Must contain lowercase letters" msgstr "必须包含小写字母" -#: common/forms.py:217 +#: common/forms.py:214 msgid "" "After opening, the user password changes and resets must contain lowercase " "letters" msgstr "开启后,用户密码修改、重置必须包含小写字母" -#: common/forms.py:223 +#: common/forms.py:220 msgid "Must contain numeric characters" msgstr "必须包含数字字符" -#: common/forms.py:224 +#: common/forms.py:221 msgid "" "After opening, the user password changes and resets must contain numeric " "characters" msgstr "开启后,用户密码修改、重置必须包含数字字符" -#: common/forms.py:230 +#: common/forms.py:227 msgid "Must contain special characters" msgstr "必须包含特殊字符" -#: common/forms.py:231 +#: common/forms.py:228 msgid "" "After opening, the user password changes and resets must contain special " "characters" @@ -2177,14 +2177,14 @@ msgstr "线程数" msgid "Boot Time" msgstr "运行时间" -#: terminal/models.py:132 terminal/templates/terminal/session_list.html:102 +#: terminal/models.py:132 terminal/templates/terminal/session_list.html:104 msgid "Replay" msgstr "回放" #: terminal/models.py:133 terminal/templates/terminal/command_list.html:55 #: terminal/templates/terminal/command_list.html:71 #: terminal/templates/terminal/session_detail.html:48 -#: terminal/templates/terminal/session_list.html:76 +#: terminal/templates/terminal/session_list.html:77 msgid "Command" msgstr "命令" @@ -2235,24 +2235,28 @@ msgstr "监控" msgid "Terminate session" msgstr "终止会话" -#: terminal/templates/terminal/session_list.html:79 +#: terminal/templates/terminal/session_list.html:76 +msgid "Login from" +msgstr "登录来源" + +#: terminal/templates/terminal/session_list.html:80 msgid "Duration" msgstr "时长" -#: terminal/templates/terminal/session_list.html:104 +#: terminal/templates/terminal/session_list.html:106 msgid "Monitor" msgstr "监控" -#: terminal/templates/terminal/session_list.html:106 #: terminal/templates/terminal/session_list.html:108 +#: terminal/templates/terminal/session_list.html:110 msgid "Terminate" msgstr "终断" -#: terminal/templates/terminal/session_list.html:120 +#: terminal/templates/terminal/session_list.html:122 msgid "Terminate selected" msgstr "终断所选" -#: terminal/templates/terminal/session_list.html:140 +#: terminal/templates/terminal/session_list.html:142 msgid "Terminate task send, waiting ..." msgstr "终断任务已发送,请等待" @@ -2322,7 +2326,7 @@ msgid "" "You should use your ssh client tools connect terminal: {}

    {}" msgstr "你可以使用ssh客户端工具连接终端" -#: users/api.py:210 users/templates/users/login.html:50 +#: users/api.py:208 users/templates/users/login.html:50 msgid "Log in frequently and try again later" msgstr "登录频繁, 稍后重试" @@ -2697,7 +2701,7 @@ msgid "Can't provide security? Please contact the administrator!" msgstr "如果不能提供MFA验证码,请联系管理员!" #: users/templates/users/reset_password.html:46 -#: users/templates/users/user_detail.html:352 users/utils.py:81 +#: users/templates/users/user_detail.html:352 users/utils.py:80 msgid "Reset password" msgstr "重置密码" @@ -2914,11 +2918,11 @@ msgstr "新的公钥已设置成功,请下载对应的私钥" msgid "Update user" msgstr "更新用户" -#: users/utils.py:42 +#: users/utils.py:41 msgid "Create account successfully" msgstr "创建账户成功" -#: users/utils.py:44 +#: users/utils.py:43 #, python-format msgid "" "\n" @@ -2963,7 +2967,7 @@ msgstr "" "
    \n" " " -#: users/utils.py:83 +#: users/utils.py:82 #, python-format msgid "" "\n" @@ -3007,11 +3011,11 @@ msgstr "" "
    \n" " " -#: users/utils.py:114 +#: users/utils.py:113 msgid "SSH Key Reset" msgstr "重置ssh密钥" -#: users/utils.py:116 +#: users/utils.py:115 #, python-format msgid "" "\n" @@ -3036,19 +3040,19 @@ msgstr "" "
    \n" " " -#: users/utils.py:149 +#: users/utils.py:148 msgid "User not exist" msgstr "用户不存在" -#: users/utils.py:151 +#: users/utils.py:150 msgid "Disabled or expired" msgstr "禁用或失效" -#: users/utils.py:164 +#: users/utils.py:163 msgid "Password or SSH public key invalid" msgstr "密码或密钥不合法" -#: users/utils.py:290 users/utils.py:300 +#: users/utils.py:289 users/utils.py:299 msgid "Bit" msgstr " 位" diff --git a/apps/terminal/templates/terminal/session_list.html b/apps/terminal/templates/terminal/session_list.html index 8cea8e217..33ae09877 100644 --- a/apps/terminal/templates/terminal/session_list.html +++ b/apps/terminal/templates/terminal/session_list.html @@ -73,6 +73,7 @@ {% trans 'System user' %} {% trans 'Remote addr' %} {% trans 'Protocol' %} + {% trans 'Login from' %} {% trans 'Command' %} {% trans 'Date start' %} {# {% trans 'Date last active' %}#} @@ -92,6 +93,7 @@ {{ session.system_user }} {{ session.remote_addr|default:"" }} {{ session.protocol }} + {{ session.get_login_from_display }} {{ session.id | get_session_command_amount }} {{ session.date_start }} From b6cd4a20c59b36d5dbca523a8f05136b2fced2c4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 8 Jul 2018 13:02:43 +0800 Subject: [PATCH 24/34] [Bugfix] Data table xss attack --- apps/static/js/jumpserver.js | 2 +- apps/users/templates/users/user_list.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 0000a3ccf..29fb3e6c6 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -272,7 +272,7 @@ jumpserver.initDataTable = function (options) { $(td).html(''.replace('99991937', cellData)); } }, - {className: 'text-center', targets: '_all'} + {className: 'text-center', render: $.fn.dataTable.render.text(), targets: '_all'} ]; columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs; var select = { diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index 052c5b7c0..27c07c538 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -59,7 +59,7 @@ function initTable() { ele: $('#user_list_table'), columnDefs: [ {targets: 1, createdCell: function (td, cellData, rowData) { - var detail_btn = '' + cellData + ''; + var detail_btn = '' + escape(cellData) + ''; $(td).html(detail_btn.replace("{{ DEFAULT_PK }}", rowData.id)); }}, {targets: 4, createdCell: function (td, cellData) { From 8bfd2be21ff77ec0898bb430153f235a8581ca58 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 10 Jul 2018 15:50:15 +0800 Subject: [PATCH 25/34] =?UTF-8?q?[Update]=20=E6=B8=85=E9=99=A4=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=94=A8=E6=88=B7=E8=AE=A4=E8=AF=81=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=97=B6=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/assets/system_user_detail.html | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/assets/templates/assets/system_user_detail.html b/apps/assets/templates/assets/system_user_detail.html index b68bf2689..f0ed6030c 100644 --- a/apps/assets/templates/assets/system_user_detail.html +++ b/apps/assets/templates/assets/system_user_detail.html @@ -337,10 +337,22 @@ $(document).ready(function () { }); }).on('click', '.btn-clear-auth', function () { var the_url = '{% url "api-assets:system-user-auth-info" pk=system_user.id %}'; - APIUpdateAttr({ - url: the_url, - method: 'DELETE', - success_message: "{% trans 'Clear auth' %}" + " {% trans 'success' %}" + var name = '{{ system_user.name }}'; + swal({ + title: '你确定清除该系统用户的认证信息吗 ?', + text: " [" + name + "] ", + type: "warning", + showCancelButton: true, + cancelButtonText: '取消', + confirmButtonColor: "#ed5565", + confirmButtonText: '确认', + closeOnConfirm: true + }, function () { + APIUpdateAttr({ + url: the_url, + method: 'DELETE', + success_message: "{% trans 'Clear auth' %}" + " {% trans 'success' %}" + }); }); }) From 01185a2d07419206c12792efc5a81b0e6236027a Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 10 Jul 2018 16:03:05 +0800 Subject: [PATCH 26/34] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0gateway=20ap?= =?UTF-8?q?i?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/asset.py | 22 +++++++++++++++++++++- apps/assets/urls/api_urls.py | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py index ff5047ba3..8c1f3d726 100644 --- a/apps/assets/api/asset.py +++ b/apps/assets/api/asset.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # +import random + from rest_framework import generics from rest_framework.response import Response from rest_framework_bulk import BulkModelViewSet @@ -22,7 +24,8 @@ from ..utils import LabelFilter logger = get_logger(__file__) __all__ = [ 'AssetViewSet', 'AssetListUpdateApi', - 'AssetRefreshHardwareApi', 'AssetAdminUserTestApi' + 'AssetRefreshHardwareApi', 'AssetAdminUserTestApi', + 'AssetGatewayApi' ] @@ -106,3 +109,20 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView): asset = get_object_or_404(Asset, pk=asset_id) task = test_asset_connectability_manual.delay(asset) return Response({"task": task.id}) + + +class AssetGatewayApi(generics.RetrieveAPIView): + queryset = Asset.objects.all() + permission_classes = (IsSuperUserOrAppUser,) + + def retrieve(self, request, *args, **kwargs): + asset_id = kwargs.get('pk') + asset = get_object_or_404(Asset, pk=asset_id) + + if asset.domain and \ + asset.domain.gateways.filter(protocol=asset.protocol).exists(): + gateway = random.choice(asset.domain.gateways.filter(protocol=asset.protocol)) + serializer = serializers.GatewayWithAuthSerializer(instance=gateway) + return Response(serializer.data) + else: + return Response({"msg": "Not have gateway"}, status=404) \ No newline at end of file diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index ce622d648..cf55d08ba 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -23,6 +23,8 @@ urlpatterns = [ api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'), url(r'^v1/assets/(?P[0-9a-zA-Z\-]{36})/alive/$', api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'), + url(r'^v1/assets/(?P[0-9a-zA-Z\-]{36})/gateway/$', + api.AssetGatewayApi.as_view(), name='asset-gateway'), url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/nodes/$', api.ReplaceNodesAdminUserApi.as_view(), name='replace-nodes-admin-user'), url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/auth/$', From d3e22a2a90cdfe6a9f971fda0d4d5d83716d28ad Mon Sep 17 00:00:00 2001 From: wojiushixiaobai <296015668@qq.com> Date: Thu, 12 Jul 2018 10:53:39 +0800 Subject: [PATCH 27/34] =?UTF-8?q?=E4=BF=AE=E6=94=B9url=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index df8324761..f0bceec9b 100644 --- a/README.md +++ b/README.md @@ -19,25 +19,25 @@ Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点 ---- ### 功能 - + ![Jumpserver功能](https://jumpserver-release.oss-cn-hangzhou.aliyuncs.com/Jumpserver13.jpg "Jumpserver功能") ### 开始使用 -快速开始文档 [Docker安装](http://docs.jumpserver.org/zh/latest/quickstart.html) +快速开始文档 [Docker安装](http://docs.jumpserver.org/zh/docs/dockerinstall.html) -一步一步安装文档 [详细部署](http://docs.jumpserver.org/zh/latest/step_by_step.html) +一步一步安装文档 [详细部署](http://docs.jumpserver.org/zh/docs/step_by_step.html) 也可以查看我们完整文档包括了使用和开发 [文档](http://docs.jumpserver.org) -### Demo 和 截图 +### Demo 和 截图 我们提供了DEMO和截图可以让你快速了解Jumpserver [DEMO](http://demo.jumpserver.org) [截图](http://docs.jumpserver.org/zh/docs/snapshot.html) -### SDK +### SDK 我们还编写了一些SDK,供你其它系统快速和Jumpserver APi交互, From 9004351ad1f430a9a69e044bc2c5acf9744ea7d3 Mon Sep 17 00:00:00 2001 From: wojiushixiaobai <296015668@qq.com> Date: Thu, 12 Jul 2018 16:47:35 +0800 Subject: [PATCH 28/34] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/make_migrations.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/make_migrations.sh b/utils/make_migrations.sh index fbe3e577c..4fb8fdacf 100755 --- a/utils/make_migrations.sh +++ b/utils/make_migrations.sh @@ -5,4 +5,4 @@ python3 ../apps/manage.py makemigrations python3 ../apps/manage.py migrate -python3 ../apps/manage.py makemigrations –-merge +python3 ../apps/manage.py makemigrations --merge From 1182313c1a6bee4d36edaf9165c56101e2b6448e Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Fri, 13 Jul 2018 19:30:48 +0800 Subject: [PATCH 29/34] =?UTF-8?q?[Update]=20=E5=8F=96=E6=B6=88=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=94=A8=E6=88=B7-=E6=B8=85=E9=99=A4=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E4=BF=A1=E6=81=AF=EF=BC=8C=E5=8F=96=E6=B6=88-?= =?UTF-8?q?=E7=BD=91=E5=85=B3rdp=E5=8D=8F=E8=AE=AE=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7?= =?UTF-8?q?unblock=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/gateway_create_update.html | 15 ++- .../templates/assets/system_user_detail.html | 17 ++- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 36764 -> 36926 bytes apps/i18n/zh/LC_MESSAGES/django.po | 103 ++++++++++-------- apps/users/api.py | 15 ++- apps/users/templates/users/user_detail.html | 35 ++++++ apps/users/urls/api_urls.py | 2 + apps/users/views/login.py | 4 +- 8 files changed, 133 insertions(+), 58 deletions(-) diff --git a/apps/assets/templates/assets/gateway_create_update.html b/apps/assets/templates/assets/gateway_create_update.html index 7c1bf14b2..fea540385 100644 --- a/apps/assets/templates/assets/gateway_create_update.html +++ b/apps/assets/templates/assets/gateway_create_update.html @@ -42,7 +42,7 @@ {% bootstrap_field form.domain layout="horizontal" %} {% block auth %} -

    {% trans 'Auth' %}

    +

    {% trans 'Auth' %}

    {% bootstrap_field form.username layout="horizontal" %} {% bootstrap_field form.password layout="horizontal" %} @@ -72,14 +72,23 @@ var protocol_id = '#' + '{{ form.protocol.id_for_label }}'; var private_key_id = '#' + '{{ form.private_key_file.id_for_label }}'; var port = '#' + '{{ form.port.id_for_label }}'; +var username = '#' + '{{ form.username.id_for_label }}'; +var password = '#' + '{{ form.password.id_for_label }}'; +var auth_title = '#auth_title'; function protocolChange() { if ($(protocol_id + " option:selected").text() === 'rdp') { {#$(port).val(3389);#} - $(private_key_id).closest('.form-group').addClass('hidden') + $(private_key_id).closest('.form-group').addClass('hidden'); + $(username).closest('.form-group').addClass('hidden'); + $(password).closest('.form-group').addClass('hidden'); + $(auth_title).addClass('hidden'); } else { {#$(port).val(22);#} - $(private_key_id).closest('.form-group').removeClass('hidden') + $(private_key_id).closest('.form-group').removeClass('hidden'); + $(username).closest('.form-group').removeClass('hidden'); + $(password).closest('.form-group').removeClass('hidden'); + $(auth_title).removeClass('hidden'); } } diff --git a/apps/assets/templates/assets/system_user_detail.html b/apps/assets/templates/assets/system_user_detail.html index f0ed6030c..dd188bdab 100644 --- a/apps/assets/templates/assets/system_user_detail.html +++ b/apps/assets/templates/assets/system_user_detail.html @@ -152,15 +152,14 @@ - - - {% trans 'Clear auth' %}: - - - - - - +{# #} +{# {% trans 'Clear auth' %}:#} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} {# #} {# {% trans 'Change auth period' %}:#} diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index 78d56177ea093bf518cc027eff07aab406c1a7c5..182ca35ceb4330dca81cc003be9451c796084219 100644 GIT binary patch delta 10828 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 delta 10638 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 v<(|U)q{>Uf2L=T\n" "Language-Team: Jumpserver team\n" @@ -32,7 +32,7 @@ msgstr "" #: assets/forms/asset.py:24 assets/models/asset.py:89 assets/models/user.py:112 #: assets/templates/assets/asset_detail.html:183 #: assets/templates/assets/asset_detail.html:191 -#: assets/templates/assets/system_user_detail.html:179 perms/models.py:33 +#: assets/templates/assets/system_user_detail.html:178 perms/models.py:33 msgid "Nodes" msgstr "节点管理" @@ -101,7 +101,7 @@ msgstr "端口" #: assets/forms/domain.py:14 assets/forms/label.py:13 #: assets/models/asset.py:237 assets/templates/assets/admin_user_list.html:25 #: assets/templates/assets/domain_detail.html:60 -#: assets/templates/assets/domain_list.html:15 +#: assets/templates/assets/domain_list.html:23 #: assets/templates/assets/label_list.html:16 #: assets/templates/assets/system_user_list.html:30 audits/models.py:11 #: audits/templates/audits/ftp_log_list.html:41 @@ -125,7 +125,7 @@ msgstr "资产" #: assets/templates/assets/admin_user_list.html:23 #: assets/templates/assets/domain_detail.html:56 #: assets/templates/assets/domain_gateway_list.html:56 -#: assets/templates/assets/domain_list.html:14 +#: assets/templates/assets/domain_list.html:22 #: assets/templates/assets/label_list.html:14 #: assets/templates/assets/system_user_detail.html:58 #: assets/templates/assets/system_user_list.html:26 common/models.py:26 @@ -356,7 +356,7 @@ msgstr "创建日期" #: assets/templates/assets/asset_detail.html:125 #: assets/templates/assets/domain_detail.html:76 #: assets/templates/assets/domain_gateway_list.html:61 -#: assets/templates/assets/domain_list.html:17 +#: assets/templates/assets/domain_list.html:25 #: assets/templates/assets/system_user_detail.html:104 #: assets/templates/assets/system_user_list.html:34 common/models.py:30 #: ops/models/adhoc.py:42 perms/models.py:40 perms/models.py:83 @@ -756,7 +756,7 @@ msgstr "测试" #: assets/templates/assets/domain_detail.html:24 #: assets/templates/assets/domain_detail.html:103 #: assets/templates/assets/domain_gateway_list.html:85 -#: assets/templates/assets/domain_list.html:42 +#: assets/templates/assets/domain_list.html:50 #: assets/templates/assets/label_list.html:38 #: assets/templates/assets/system_user_detail.html:26 #: assets/templates/assets/system_user_list.html:89 @@ -780,7 +780,7 @@ msgstr "更新" #: assets/templates/assets/domain_detail.html:28 #: assets/templates/assets/domain_detail.html:104 #: assets/templates/assets/domain_gateway_list.html:86 -#: assets/templates/assets/domain_list.html:43 +#: assets/templates/assets/domain_list.html:51 #: assets/templates/assets/label_list.html:39 #: assets/templates/assets/system_user_detail.html:30 #: assets/templates/assets/system_user_list.html:90 @@ -808,12 +808,13 @@ msgstr "选择节点" #: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/asset_detail.html:200 #: assets/templates/assets/asset_list.html:638 -#: assets/templates/assets/system_user_detail.html:196 +#: assets/templates/assets/system_user_detail.html:195 #: assets/templates/assets/system_user_list.html:139 templates/_modal.html:22 #: terminal/templates/terminal/session_detail.html:108 -#: users/templates/users/user_detail.html:366 -#: users/templates/users/user_detail.html:391 -#: users/templates/users/user_detail.html:414 +#: users/templates/users/user_detail.html:374 +#: users/templates/users/user_detail.html:399 +#: users/templates/users/user_detail.html:422 +#: users/templates/users/user_detail.html:458 #: users/templates/users/user_group_create_update.html:32 #: users/templates/users/user_group_list.html:86 #: users/templates/users/user_list.html:200 @@ -841,7 +842,7 @@ msgstr "比例" #: assets/templates/assets/admin_user_list.html:30 #: assets/templates/assets/asset_list.html:91 #: assets/templates/assets/domain_gateway_list.html:62 -#: assets/templates/assets/domain_list.html:18 +#: assets/templates/assets/domain_list.html:26 #: assets/templates/assets/label_list.html:17 #: assets/templates/assets/system_user_list.html:35 #: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64 @@ -905,8 +906,8 @@ msgid "Refresh" msgstr "刷新" #: assets/templates/assets/asset_detail.html:300 -#: users/templates/users/user_detail.html:286 -#: users/templates/users/user_detail.html:313 +#: users/templates/users/user_detail.html:294 +#: users/templates/users/user_detail.html:321 msgid "Update successfully!" msgstr "更新成功" @@ -1002,8 +1003,9 @@ msgstr "存在资产,不能删除" #: assets/templates/assets/asset_list.html:633 #: assets/templates/assets/system_user_list.html:134 -#: users/templates/users/user_detail.html:361 -#: users/templates/users/user_detail.html:386 +#: users/templates/users/user_detail.html:369 +#: users/templates/users/user_detail.html:394 +#: users/templates/users/user_detail.html:453 #: users/templates/users/user_group_list.html:81 #: users/templates/users/user_list.html:195 msgid "Are you sure?" @@ -1043,7 +1045,7 @@ msgstr "您确定删除吗?" #: assets/templates/assets/domain_detail.html:21 #: assets/templates/assets/domain_detail.html:64 #: assets/templates/assets/domain_gateway_list.html:21 -#: assets/templates/assets/domain_list.html:16 +#: assets/templates/assets/domain_list.html:24 msgid "Gateway" msgstr "网关" @@ -1063,7 +1065,7 @@ msgstr "创建网关" msgid "Test connection" msgstr "测试连接" -#: assets/templates/assets/domain_list.html:6 assets/views/domain.py:46 +#: assets/templates/assets/domain_list.html:14 assets/views/domain.py:46 msgid "Create domain" msgstr "创建网域" @@ -1106,20 +1108,15 @@ msgstr "家目录" msgid "Uid" msgstr "Uid" -#: assets/templates/assets/system_user_detail.html:157 -#: assets/templates/assets/system_user_detail.html:343 -msgid "Clear auth" -msgstr "清除认证信息" - -#: assets/templates/assets/system_user_detail.html:160 -msgid "Clear" -msgstr "清除" - -#: assets/templates/assets/system_user_detail.html:187 +#: assets/templates/assets/system_user_detail.html:186 msgid "Add to node" msgstr "添加到节点" -#: assets/templates/assets/system_user_detail.html:343 +#: assets/templates/assets/system_user_detail.html:353 +msgid "Clear auth" +msgstr "清除认证信息" + +#: assets/templates/assets/system_user_detail.html:353 msgid "success" msgstr "成功" @@ -1238,6 +1235,7 @@ msgstr "文件名" #: audits/models.py:15 audits/templates/audits/ftp_log_list.html:77 #: ops/templates/ops/task_list.html:39 users/models/authentication.py:66 +#: users/templates/users/user_detail.html:443 msgid "Success" msgstr "成功" @@ -1854,7 +1852,7 @@ msgstr "选择用户" #: perms/templates/perms/asset_permission_list.html:136 templates/_nav.html:14 #: users/models/group.py:23 users/models/user.py:55 #: users/templates/users/_select_user_modal.html:16 -#: users/templates/users/user_detail.html:192 +#: users/templates/users/user_detail.html:200 #: users/templates/users/user_list.html:26 msgid "User group" msgstr "用户组" @@ -1907,7 +1905,7 @@ msgid "Add node to this permission" msgstr "添加节点" #: perms/templates/perms/asset_permission_asset.html:125 -#: users/templates/users/user_detail.html:209 +#: users/templates/users/user_detail.html:217 msgid "Join" msgstr "加入" @@ -2326,7 +2324,7 @@ msgid "" "You should use your ssh client tools connect terminal: {}

    {}" msgstr "你可以使用ssh客户端工具连接终端" -#: users/api.py:208 users/templates/users/login.html:50 +#: users/api.py:221 users/templates/users/login.html:50 msgid "Log in frequently and try again later" msgstr "登录频繁, 稍后重试" @@ -2405,7 +2403,7 @@ msgstr "" msgid "Paste user id_rsa.pub here." msgstr "复制用户公钥到这里" -#: users/forms.py:73 users/templates/users/user_detail.html:200 +#: users/forms.py:73 users/templates/users/user_detail.html:208 msgid "Join user groups" msgstr "添加到用户组" @@ -2701,7 +2699,7 @@ msgid "Can't provide security? Please contact the administrator!" msgstr "如果不能提供MFA验证码,请联系管理员!" #: users/templates/users/reset_password.html:46 -#: users/templates/users/user_detail.html:352 users/utils.py:80 +#: users/templates/users/user_detail.html:360 users/utils.py:80 msgid "Reset password" msgstr "重置密码" @@ -2773,44 +2771,57 @@ msgstr "发送" msgid "Send reset ssh key mail" msgstr "发送重置密钥邮件" -#: users/templates/users/user_detail.html:295 +#: users/templates/users/user_detail.html:186 +#: users/templates/users/user_detail.html:444 +msgid "Unblock user" +msgstr "解锁用户" + +#: users/templates/users/user_detail.html:189 +msgid "Unblock" +msgstr "解锁" + +#: users/templates/users/user_detail.html:303 msgid "Goto profile page enable MFA" msgstr "请去个人信息页面启用自己的MFA" -#: users/templates/users/user_detail.html:351 +#: users/templates/users/user_detail.html:359 msgid "An e-mail has been sent to the user`s mailbox." msgstr "已发送邮件到用户邮箱" -#: users/templates/users/user_detail.html:362 +#: users/templates/users/user_detail.html:370 msgid "This will reset the user password and send a reset mail" msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱" -#: users/templates/users/user_detail.html:376 +#: users/templates/users/user_detail.html:384 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:377 +#: users/templates/users/user_detail.html:385 msgid "Reset SSH public key" msgstr "重置SSH密钥" -#: users/templates/users/user_detail.html:387 +#: users/templates/users/user_detail.html:395 msgid "This will reset the user public key and send a reset mail" msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱" -#: users/templates/users/user_detail.html:404 +#: users/templates/users/user_detail.html:412 #: users/templates/users/user_profile.html:211 msgid "Successfully updated the SSH public key." msgstr "更新ssh密钥成功" -#: users/templates/users/user_detail.html:405 -#: users/templates/users/user_detail.html:409 +#: users/templates/users/user_detail.html:413 +#: users/templates/users/user_detail.html:417 #: users/templates/users/user_profile.html:212 #: users/templates/users/user_profile.html:217 msgid "User SSH public key update" msgstr "ssh密钥" +#: users/templates/users/user_detail.html:454 +msgid "After unlocking the user, the user can log in normally." +msgstr "解锁用户后,此用户即可正常登录" + #: users/templates/users/user_group_create_update.html:31 msgid "Cancel" msgstr "取消" @@ -3169,5 +3180,11 @@ msgstr "MFA 解绑成功" msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" +#~ msgid "Unblock user successfully. " +#~ msgstr "解锁用户成功" + +#~ msgid "Clear" +#~ msgstr "清除" + #~ msgid "MFA setting" #~ msgstr "MFA 设置" diff --git a/apps/users/api.py b/apps/users/api.py index c23112384..a3a613622 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -95,6 +95,19 @@ class UserUpdatePKApi(generics.UpdateAPIView): user.save() +class UserUnblockPKApi(generics.UpdateAPIView): + queryset = User.objects.all() + permission_classes = (IsSuperUser,) + serializer_class = UserSerializer + key_prefix_limit = "_LOGIN_LIMIT_{}_{}" + + def perform_update(self, serializer): + user = self.get_object() + username = user.username if user else '' + key_limit = self.key_prefix_limit.format(username, '*') + cache.delete_pattern(key_limit) + + class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet): queryset = UserGroup.objects.all() serializer_class = UserGroupSerializer @@ -203,7 +216,7 @@ class UserAuthApi(APIView): username = request.data.get('username') ip = request.data.get('remote_addr', None) ip = ip if ip else get_login_ip(request) - key_limit = self.key_prefix_limit.format(ip, username) + key_limit = self.key_prefix_limit.format(username, ip) if is_block_login(key_limit): msg = _("Log in frequently and try again later") return Response({'msg': msg}, status=401) diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index d40251563..0182cf521 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -182,6 +182,14 @@ + + {% trans 'Unblock user' %} + + + + + +
    @@ -426,6 +434,33 @@ $(document).ready(function() { var the_url = '{% url "api-users:user-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid); var redirect_url = "{% url 'users:user-list' %}"; objectDelete($this, name, the_url, redirect_url); +}).on('click', '#btn-unblock-user', function () { + function doReset() { + {#var the_url = '{% url "api-users:user-reset-password" pk=user_object.id %}';#} + var the_url = '{% url "api-users:user-unblock" pk=user_object.id %}'; + var body = {}; + var success = function() { + var msg = "{% trans "Success" %}"; + swal("{% trans 'Unblock user' %}", msg, "success"); + }; + APIUpdateAttr({ + url: the_url, + body: JSON.stringify(body), + success: success + }); + } + swal({ + title: "{% trans 'Are you sure?' %}", + text: "{% trans "After unlocking the user, the user can log in normally."%}", + type: "warning", + showCancelButton: true, + confirmButtonColor: "#DD6B55", + confirmButtonText: "{% trans 'Confirm' %}", + closeOnConfirm: false + }, function() { + doReset(); + }); + }) {% endblock %} diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py index 683638a4e..017224421 100644 --- a/apps/users/urls/api_urls.py +++ b/apps/users/urls/api_urls.py @@ -29,6 +29,8 @@ urlpatterns = [ api.UserResetPKApi.as_view(), name='user-public-key-reset'), url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/pubkey/update/$', api.UserUpdatePKApi.as_view(), name='user-public-key-update'), + url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/unblock/$', + api.UserUnblockPKApi.as_view(), name='user-unblock'), url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/groups/$', api.UserUpdateGroupApi.as_view(), name='user-update-group'), url(r'^v1/groups/(?P[0-9a-zA-Z\-]{36})/users/$', diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 94071924f..2b86df19a 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -64,7 +64,7 @@ class UserLoginView(FormView): # limit login authentication ip = get_login_ip(request) username = self.request.POST.get('username') - key_limit = self.key_prefix_limit.format(ip, username) + key_limit = self.key_prefix_limit.format(username, ip) if is_block_login(key_limit): return self.render_to_response(self.get_context_data(block_login=True)) @@ -90,7 +90,7 @@ class UserLoginView(FormView): # limit user login failed count ip = get_login_ip(self.request) - key_limit = self.key_prefix_limit.format(ip, username) + key_limit = self.key_prefix_limit.format(username, ip) set_user_login_failed_count_to_cache(key_limit) # show captcha From 696589a3cfc24464fdb08bce36355ed2ae89be3e Mon Sep 17 00:00:00 2001 From: wojiushixiaobai <296015668@qq.com> Date: Mon, 16 Jul 2018 12:13:06 +0800 Subject: [PATCH 30/34] =?UTF-8?q?=E4=BF=AE=E6=94=B9url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/urls/views_urls.py | 1 - apps/perms/urls/views_urls.py | 16 ++++++-------- apps/users/urls/views_urls.py | 40 +++++++++++++++++----------------- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/apps/assets/urls/views_urls.py b/apps/assets/urls/views_urls.py index 418aa63a3..12c351154 100644 --- a/apps/assets/urls/views_urls.py +++ b/apps/assets/urls/views_urls.py @@ -50,4 +50,3 @@ urlpatterns = [ url(r'^domain/(?P[0-9a-zA-Z\-]{36})/gateway/create/$', views.DomainGatewayCreateView.as_view(), name='domain-gateway-create'), url(r'^domain/gateway/(?P[0-9a-zA-Z\-]{36})/update/$', views.DomainGatewayUpdateView.as_view(), name='domain-gateway-update'), ] - diff --git a/apps/perms/urls/views_urls.py b/apps/perms/urls/views_urls.py index 5c3fa3b48..a3cf7ff42 100644 --- a/apps/perms/urls/views_urls.py +++ b/apps/perms/urls/views_urls.py @@ -6,13 +6,11 @@ from .. import views app_name = 'perms' urlpatterns = [ - url(r'^asset-permission$', views.AssetPermissionListView.as_view(), name='asset-permission-list'), - url(r'^asset-permission/create$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'), - url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/update$', views.AssetPermissionUpdateView.as_view(), name='asset-permission-update'), - url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'), - url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/delete$', views.AssetPermissionDeleteView.as_view(), name='asset-permission-delete'), - url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/user$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'), - url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/asset$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'), + url(r'^asset-permission/$', views.AssetPermissionListView.as_view(), name='asset-permission-list'), + url(r'^asset-permission/create/$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'), + url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/update/$', views.AssetPermissionUpdateView.as_view(), name='asset-permission-update'), + url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'), + url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/delete/$', views.AssetPermissionDeleteView.as_view(), name='asset-permission-delete'), + url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/user/$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'), + url(r'^asset-permission/(?P[0-9a-zA-Z\-]{36})/asset/$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'), ] - - diff --git a/apps/users/urls/views_urls.py b/apps/users/urls/views_urls.py index 2aaf4a9ae..8052f1384 100644 --- a/apps/users/urls/views_urls.py +++ b/apps/users/urls/views_urls.py @@ -8,13 +8,13 @@ app_name = 'users' urlpatterns = [ # Login view - url(r'^login$', views.UserLoginView.as_view(), name='login'), - url(r'^logout$', views.UserLogoutView.as_view(), name='logout'), - url(r'^login/otp$', views.UserLoginOtpView.as_view(), name='login-otp'), - url(r'^password/forgot$', views.UserForgotPasswordView.as_view(), name='forgot-password'), - url(r'^password/forgot/sendmail-success$', views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'), - url(r'^password/reset$', views.UserResetPasswordView.as_view(), name='reset-password'), - url(r'^password/reset/success$', views.UserResetPasswordSuccessView.as_view(), name='reset-password-success'), + url(r'^login/$', views.UserLoginView.as_view(), name='login'), + url(r'^logout/$', views.UserLogoutView.as_view(), name='logout'), + url(r'^login/otp/$', views.UserLoginOtpView.as_view(), name='login-otp'), + url(r'^password/forgot/$', views.UserForgotPasswordView.as_view(), name='forgot-password'), + url(r'^password/forgot/sendmail-success/$', views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'), + url(r'^password/reset/$', views.UserResetPasswordView.as_view(), name='reset-password'), + url(r'^password/reset/success/$', views.UserResetPasswordSuccessView.as_view(), name='reset-password-success'), # Profile url(r'^profile/$', views.UserProfileView.as_view(), name='user-profile'), @@ -29,23 +29,23 @@ urlpatterns = [ url(r'^profile/otp/settings-success/$', views.UserOtpSettingsSuccessView.as_view(), name='user-otp-settings-success'), # User view - url(r'^user$', views.UserListView.as_view(), name='user-list'), - url(r'^user/export/', views.UserExportView.as_view(), name='user-export'), + url(r'^user/$', views.UserListView.as_view(), name='user-list'), + url(r'^user/export/$', views.UserExportView.as_view(), name='user-export'), url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'), url(r'^user/import/$', views.UserBulkImportView.as_view(), name='user-import'), - url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'), - url(r'^user/(?P[0-9a-zA-Z\-]{36})/update$', views.UserUpdateView.as_view(), name='user-update'), - url(r'^user/update$', views.UserBulkUpdateView.as_view(), name='user-bulk-update'), - url(r'^user/(?P[0-9a-zA-Z\-]{36})$', views.UserDetailView.as_view(), name='user-detail'), - url(r'^user/(?P[0-9a-zA-Z\-]{36})/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), - url(r'^user/(?P[0-9a-zA-Z\-]{36})/login-history', views.UserDetailView.as_view(), name='user-login-history'), + url(r'^user/create/$', views.UserCreateView.as_view(), name='user-create'), + url(r'^user/(?P[0-9a-zA-Z\-]{36})/update/$', views.UserUpdateView.as_view(), name='user-update'), + url(r'^user/update/$', views.UserBulkUpdateView.as_view(), name='user-bulk-update'), + url(r'^user/(?P[0-9a-zA-Z\-]{36})/$', views.UserDetailView.as_view(), name='user-detail'), + url(r'^user/(?P[0-9a-zA-Z\-]{36})/assets/$', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), + url(r'^user/(?P[0-9a-zA-Z\-]{36})/login-history/$', views.UserDetailView.as_view(), name='user-login-history'), # User group view - url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'), - url(r'^user-group/(?P[0-9a-zA-Z\-]{36})$', views.UserGroupDetailView.as_view(), name='user-group-detail'), - url(r'^user-group/create$', views.UserGroupCreateView.as_view(), name='user-group-create'), - url(r'^user-group/(?P[0-9a-zA-Z\-]{36})/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'), - url(r'^user-group/(?P[0-9a-zA-Z\-]{36})/assets', views.UserGroupGrantedAssetView.as_view(), name='user-group-granted-asset'), + url(r'^user-group/$', views.UserGroupListView.as_view(), name='user-group-list'), + url(r'^user-group/(?P[0-9a-zA-Z\-]{36})/$', views.UserGroupDetailView.as_view(), name='user-group-detail'), + url(r'^user-group/create/$', views.UserGroupCreateView.as_view(), name='user-group-create'), + url(r'^user-group/(?P[0-9a-zA-Z\-]{36})/update/$', views.UserGroupUpdateView.as_view(), name='user-group-update'), + url(r'^user-group/(?P[0-9a-zA-Z\-]{36})/assets/$', views.UserGroupGrantedAssetView.as_view(), name='user-group-granted-asset'), # Login log url(r'^login-log/$', views.LoginLogListView.as_view(), name='login-log-list'), From 812078331e75a2efe5bcec95ae4334c494ba6255 Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 16 Jul 2018 12:13:13 +0800 Subject: [PATCH 31/34] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0=E8=A7=A3?= =?UTF-8?q?=E9=99=A4=E7=94=A8=E6=88=B7=E7=99=BB=E5=BD=95=E9=99=90=E5=88=B6?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 36926 -> 36944 bytes apps/i18n/zh/LC_MESSAGES/django.po | 8 ++++---- apps/users/api.py | 7 ++++++- apps/users/templates/users/user_detail.html | 17 ++++++++++++----- apps/users/utils.py | 17 ++++++++++++++++- apps/users/views/login.py | 4 +++- apps/users/views/user.py | 10 ++++++++-- 7 files changed, 49 insertions(+), 14 deletions(-) diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index 182ca35ceb4330dca81cc003be9451c796084219..50e22bf55f237bf8d2d1e9ddf1c548523ff9daf9 100644 GIT binary patch delta 4306 zcmYk;dvwor9LMo*yX7)A!;TWUEy<;noESBCDW{Z{ZmQ#6Q!djee91N|bIGw~6)74W zX{$+{h1y9smFOTH$w?-3RYo_f=WE~BAN}?CzTcnE=lywqKKp)uY+nsu14y!$W61CA;RO1aE3W8W{k2-lr+dcF{x*det_$KUu znT{8Ed^ze$pL6~d>`7eV_#|rMQ;y>v_Mgh8AVa|gsCi)l4?5Aab_1$`H|$pXp2v6E zFENREM^Ps_ZEI2M>rII^NV4rv`&{j~Ke8?iZu7vsHp|XJb(n8U>?(crztC}+-GWWX zZ^KY;|K~iY&?!%-#bo06>Cx4uqBd@Wn%B{B51WoE*dO($80q|I)ckRng3~;n=kcY} z`TMJIu@kFM3)b1!>~{MJ>ZAu8A4jdPar}?t`q_N@ z1tj!KScG*qg6jAbYCLX6bh0M4C907$RN<>p8}&hb??W&K@3UjD8}WEsgc-y;LO;Ym z!mkpE!8jfZQH6g;HSm|?^K*F7h#RBEH`p@lN4&-HANF5VqX{#k1Er$Ybwbq*ukb@( z4-9iW!trR-O^idGaHd`0@g=B2MfOEoX18Js#y>`Fe8lkyWL+4<%!(!?p*Fe{FT`}z zf=tu~_t*zeKe;)M=h?;f8N1$YM%ArCy%T#;KgIuIGW!Ryk4A5MGt}FghWf5AM=k8@ z@tZM?_O^;-u6mmD`OX(( zbMj@*zmIBUAF6>Pw$}Lz=W_q*yyM(xVmDNQbjKsGHSuWF@4_6^{O7Um>TS8Lv^!86 z??N5mAZp#usF$|}wZ1mwK@;oINNdy!p}2<|>xU{Z)cL#YM32uv6`JihA9X{E9haik zZ$j07*ZIS!eZpgY_#Jh{!Mw<1RG~Jg&U@hk%s?&NVfUcMzd^09#-3P%n%8B1)IcxP z=XWEjZVs|843=ii=ig$=0P1DLd9oL1!D6e z8`vhO@fN7BBn{&*-T4gEy5Xps8|nN6RQ+tniyc3c$Ng(!1qu0q{mK)6w11-hCzFsL zy+qwmjoe|!+v(Vt{5;g3WGN=#^R^VVZj-Il=c2@iPVDu>Zyg`EHQ1JUF$<%D9Z)Cg zj9Nbsb$}75U(&I7G0s4Jc173@H>19eFR?2&4hy1Rraq{GqfmvWqE3*Dskj1%;zk^V zr%`Wr?Lo5%=Jy48lU==pRa;%SA zJ^lfzvCr%w`x9#2IaDLbi=uh0Q2DN?e1;t!83uPcF~Mfr`PhjCOB}z2D)heN1GXB~ z&~K(&LV$|1Ds#xFuRvt9*Q&i#o_B&LeUr`&xE{VpQqT)8FLRUB* zY%@{y#$zj-f~va&Rqt8U`d2a3#O*xj9ry_A8bMv%e$>K4_N4P?Z7phk{L*NC5_Tp| z#je;N_0R4URGmUwf_gbim-4l$z;(-_0@qi3+`fEBvyye+uh?*`;LYz#-mczObfRQg eb-|X3`42X_xOvIC;Q0ErCz$a6p;b@55dR-#B4M-u delta 4288 zcmX}udvwor9LMo*yG1sWOIgbl5o)H+qIGw&NIIRQGfIv+-A5&>wpo2Gj4ea#bdicP zp&e(*tdvqmbU5lDCr2gSj+m~d+$!t&+UHe&JwEUE=ks}gF2C>hyKP^buzqpEs?n#JD~zHPti>#>!&KaYop2YX;X%~6W46VnObFxC>{<4FOlN$q;~|c3#W%kp%xtEc(^S<6}a1Rq2ppqBOjwSQtkZ9sD+l= zH=SQ^H`z}xo%N%gPW*tHc+eijOyZkw8>%N&Zu#jj{DdgRKbC$@xz=Si5fo!JL0|WFLD30 z$^8C0QI!+Vq9(jxU$d+22dI;N;&=~gev{+h9sh&<$R`!CdCWnLy9{-sgHf;f9Yx%~ z7Azx?hn3iRBd8tkL-ij+oh+si?yHQ5*Qx@n85Nal#X!f03=l!Q|^4|7;JVHhKbepwo)Oyl$wv@p*1s zlY1?m8esApm`>Qj6eJFtFqw6#Ie((v16px*1AsEHS$ z-i9IA3vYEi1^W=sMs2VWJK+XwJ*o3wJ3eR+*<;v6?|%!8uGnQtu)iH@$Dn+zH|P7{S#xIFrh4*unQ{gjao3zUT1Hy<5A-#q8_fPs0HSs zHuOAd{u1ZwF_XB_@eaGY%*ZETCtFR0C zTIb(E)!BmDz%JYDeEX-kf9*W;sW7kysz8q8e9R&qiTW-~LXBU5tygcC+IqVdweSYi z!M361eUEy0n^5zcV;UOxH)dg5e~|ikW0#-`T;cppcC7m!Koxq#aVhGC${jC6&0m75 zzsC8UsCDBn-Pn)1;y-NC)KI81YUkNF4RcWw*V;{}{?AbJzrcamgc^5FdDuWU>h-$@ zwZTGUUK~vijp!LWA2snsy9{-L2FD+xK14h1UfX2n~f=Y{|C{~ zf>+ra>}b@&_c$)LGn}9A_%+mms~vAZ-N-i7Nq6D>_?zR=T%+=nP(OE6MMD#6P#by4 zuCR@EgWYEL*e1M^c}HyS^ic0nRJ{^R#tPKU&BhDyd5kq+BMt3n2P!^@+R#yZ(k9Oc z{i&$8BmTktp?2ZqhUb{-{fh$mN$5zb7gm_l?G7Ugo(Fjzbdr&7R#xrmh4#QV59}l3O z?*5hGgqNZ=ay9aAV>AL^!1dS}3#vl@1WX|=!DNi5(nzFH;f^X)p=YrjzJhJB&i!jp z8(U|$+OJR>{|Pn!r1R~b3HeM^zQ4UZ7)QgLxYgceAI82+D093FwLpX8kL^y>hQ38@ z^mls#b<(8S!FIMIY9krgQSX0m8Y+;7ZSZO78jJc(??S%P(PT`;X4`_ApIXho zZ+JFpqXnos52E^u>~z%IQ=?e#e;o}Cd>2)Cquqfjv=>$2kn^X^32}Q=p)AL_b_lB8 zDC~ygP<6{t^(s;I7GbP`D{1H%cne!Mg1WkmsEJ$cZs+&epHbrvJN^eRAWoVap6&st zKfB{lbxQ3_)Wcabm$y{~vgd^b2Q_TzxoT*en)3ZMD}J2yc0+YxTKDvt^60?w+NS@L JZ&WQw{uiW`Uzz{_ diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index 09b453262..9e3766748 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/zh/LC_MESSAGES/django.po @@ -2774,11 +2774,11 @@ msgstr "发送重置密钥邮件" #: users/templates/users/user_detail.html:186 #: users/templates/users/user_detail.html:444 msgid "Unblock user" -msgstr "解锁用户" +msgstr "解除登录限制" #: users/templates/users/user_detail.html:189 msgid "Unblock" -msgstr "解锁" +msgstr "解除" #: users/templates/users/user_detail.html:303 msgid "Goto profile page enable MFA" @@ -2820,7 +2820,7 @@ msgstr "ssh密钥" #: users/templates/users/user_detail.html:454 msgid "After unlocking the user, the user can log in normally." -msgstr "解锁用户后,此用户即可正常登录" +msgstr "解除用户登录限制后,此用户即可正常登录" #: users/templates/users/user_group_create_update.html:31 msgid "Cancel" @@ -3181,7 +3181,7 @@ msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" #~ msgid "Unblock user successfully. " -#~ msgstr "解锁用户成功" +#~ msgstr "解除登录限制成功" #~ msgid "Clear" #~ msgstr "清除" diff --git a/apps/users/api.py b/apps/users/api.py index a3a613622..840b8c913 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -100,12 +100,15 @@ class UserUnblockPKApi(generics.UpdateAPIView): permission_classes = (IsSuperUser,) serializer_class = UserSerializer key_prefix_limit = "_LOGIN_LIMIT_{}_{}" + key_prefix_block = "_LOGIN_BLOCK_{}" def perform_update(self, serializer): user = self.get_object() username = user.username if user else '' key_limit = self.key_prefix_limit.format(username, '*') + key_block = self.key_prefix_block.format(username) cache.delete_pattern(key_limit) + cache.delete(key_block) class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet): @@ -210,6 +213,7 @@ class UserAuthApi(APIView): permission_classes = (AllowAny,) serializer_class = UserSerializer key_prefix_limit = "_LOGIN_LIMIT_{}_{}" + key_prefix_block = "_LOGIN_BLOCK_{}" def post(self, request): # limit login @@ -217,6 +221,7 @@ class UserAuthApi(APIView): ip = request.data.get('remote_addr', None) ip = ip if ip else get_login_ip(request) key_limit = self.key_prefix_limit.format(username, ip) + key_block = self.key_prefix_block.format(username) if is_block_login(key_limit): msg = _("Log in frequently and try again later") return Response({'msg': msg}, status=401) @@ -231,7 +236,7 @@ class UserAuthApi(APIView): } self.write_login_log(request, data) - set_user_login_failed_count_to_cache(key_limit) + set_user_login_failed_count_to_cache(key_limit, key_block) return Response({'msg': msg}, status=401) if not user.otp_enabled: diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 0182cf521..a5adb31d2 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -182,7 +182,7 @@ - + {% trans 'Unblock user' %} @@ -283,7 +283,7 @@ $(document).ready(function() { .on('select2:unselect', function(evt) { var data = evt.params.data; delete jumpserver.nodes_selected[data.id]; - }) + }); }) .on('click', '#is_active', function() { var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}"; @@ -301,7 +301,7 @@ $(document).ready(function() { .on('click', '#force_enable_otp', function() { {% if request.user == user_object %} toastr.error("{% trans 'Goto profile page enable MFA' %}"); - return + return; {% endif %} var the_url = "{% url 'api-users:user-detail' pk=user_object.id %}"; @@ -441,7 +441,15 @@ $(document).ready(function() { var body = {}; var success = function() { var msg = "{% trans "Success" %}"; - swal("{% trans 'Unblock user' %}", msg, "success"); + {#swal("{% trans 'Unblock user' %}", msg, "success");#} + swal({ + title: "{% trans 'Unblock user' %}", + text: msg, + type: "success" + }, function() { + location.reload() + } + ); }; APIUpdateAttr({ url: the_url, @@ -460,7 +468,6 @@ $(document).ready(function() { }, function() { doReset(); }); - }) {% endblock %} diff --git a/apps/users/utils.py b/apps/users/utils.py index 937a90867..7cbaa75f0 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -333,7 +333,7 @@ def check_password_rules(password): return bool(match_obj) -def set_user_login_failed_count_to_cache(key_limit): +def set_user_login_failed_count_to_cache(key_limit, key_block): count = cache.get(key_limit) count = count + 1 if count else 1 @@ -343,6 +343,15 @@ def set_user_login_failed_count_to_cache(key_limit): limit_time = setting_limit_time.cleaned_value if setting_limit_time \ else settings.DEFAULT_LOGIN_LIMIT_TIME + setting_limit_count = Setting.objects.filter( + name='SECURITY_LOGIN_LIMIT_COUNT' + ).first() + limit_count = setting_limit_count.cleaned_value if setting_limit_count \ + else settings.DEFAULT_LOGIN_LIMIT_COUNT + + if count >= limit_count: + cache.set(key_block, 1, int(limit_time)*60) + cache.set(key_limit, count, int(limit_time)*60) @@ -357,3 +366,9 @@ def is_block_login(key_limit): if count and count >= limit_count: return True + + +def is_need_unblock(key_block): + if not cache.get(key_block): + return False + return True diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 2b86df19a..fc7caf305 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -51,6 +51,7 @@ class UserLoginView(FormView): redirect_field_name = 'next' key_prefix_captcha = "_LOGIN_INVALID_{}" key_prefix_limit = "_LOGIN_LIMIT_{}_{}" + key_prefix_block = "_LOGIN_BLOCK_{}" def get(self, request, *args, **kwargs): if request.user.is_staff: @@ -91,7 +92,8 @@ class UserLoginView(FormView): # limit user login failed count ip = get_login_ip(self.request) key_limit = self.key_prefix_limit.format(username, ip) - set_user_login_failed_count_to_cache(key_limit) + key_block = self.key_prefix_block.format(username) + set_user_login_failed_count_to_cache(key_limit, key_block) # show captcha cache.set(self.key_prefix_captcha.format(ip), 1, 3600) diff --git a/apps/users/views/user.py b/apps/users/views/user.py index 094d2ced2..56d551efa 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -36,7 +36,9 @@ from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen from common.models import Setting from .. import forms from ..models import User, UserGroup -from ..utils import AdminUserRequiredMixin, generate_otp_uri, check_otp_code, get_user_or_tmp_user, get_password_check_rules, check_password_rules +from ..utils import AdminUserRequiredMixin, generate_otp_uri, check_otp_code, \ + get_user_or_tmp_user, get_password_check_rules, check_password_rules, \ + is_need_unblock from ..signals import post_user_create from ..tasks import write_login_log_async @@ -168,13 +170,17 @@ class UserDetailView(AdminUserRequiredMixin, DetailView): model = User template_name = 'users/user_detail.html' context_object_name = "user_object" + key_prefix_block = "_LOGIN_BLOCK_{}" def get_context_data(self, **kwargs): + user = self.get_object() + key_block = self.key_prefix_block.format(user.username) groups = UserGroup.objects.exclude(id__in=self.object.groups.all()) context = { 'app': _('Users'), 'action': _('User detail'), - 'groups': groups + 'groups': groups, + 'unblock': is_need_unblock(key_block), } kwargs.update(context) return super().get_context_data(**kwargs) From 09e636495eca1a840b35cb72be2145e0b6995c4c Mon Sep 17 00:00:00 2001 From: BaiJiangJie Date: Mon, 16 Jul 2018 12:22:32 +0800 Subject: [PATCH 32/34] =?UTF-8?q?[Update]=20=E6=A8=A1=E7=89=88=E5=88=A0?= =?UTF-8?q?=E9=99=A4unblock=E7=9A=84id=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/templates/users/user_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index a5adb31d2..b664d917f 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -182,7 +182,7 @@ - + {% trans 'Unblock user' %} From ff3f74abe68dd35ae43e0de18df9600d7c6cce44 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 17 Jul 2018 12:06:47 +0800 Subject: [PATCH 33/34] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/system_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 7a4e3aadc..a295f245c 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -53,7 +53,7 @@ class SystemUserAuthSerializer(AuthSerializer): model = SystemUser fields = [ "id", "name", "username", "protocol", - "password", "private_key", + "login_mode", "password", "private_key", ] From 8883e0090fbc7317ad6647dd38e71b5941f5e75b Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 17 Jul 2018 17:12:28 +0800 Subject: [PATCH 34/34] =?UTF-8?q?[Update]=20=E6=9B=B4=E6=94=B9=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/__init__.py | 2 +- apps/templates/_footer.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/__init__.py b/apps/__init__.py index 7e96164aa..be40e1dd2 100644 --- a/apps/__init__.py +++ b/apps/__init__.py @@ -2,4 +2,4 @@ # -*- coding: utf-8 -*- # -__version__ = "1.3.2" +__version__ = "1.3.3" diff --git a/apps/templates/_footer.html b/apps/templates/_footer.html index c78f0da33..70e0c8dcd 100644 --- a/apps/templates/_footer.html +++ b/apps/templates/_footer.html @@ -1,6 +1,6 @@