From 1d640eccf667f69c9daf4002a442d4789fa9d6e0 Mon Sep 17 00:00:00 2001 From: xinwen Date: Fri, 19 Jun 2020 18:28:43 +0800 Subject: [PATCH 01/20] [Fix] `/opt/jumpserver/apps/jumpserver/views/index.py redirect(assets:user-asset-list)` (#4121) --- apps/jumpserver/views/index.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/apps/jumpserver/views/index.py b/apps/jumpserver/views/index.py index 19380466f..5050d72c0 100644 --- a/apps/jumpserver/views/index.py +++ b/apps/jumpserver/views/index.py @@ -1,5 +1,4 @@ from django.views.generic import TemplateView -from django.utils.translation import ugettext_lazy as _ from django.shortcuts import redirect from common.permissions import PermissionsMixin, IsValidUser @@ -12,17 +11,3 @@ class IndexView(PermissionsMixin, TemplateView): def get(self, request, *args, **kwargs): return redirect('/ui/') - - def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated: - return self.handle_no_permission() - if request.user.is_common_user: - return redirect('assets:user-asset-list') - return super(IndexView, self).dispatch(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context.update({ - 'app': _("Dashboard"), - }) - return context From 29666cc8d365474dddcdfc7397cf83be1ad0a3e9 Mon Sep 17 00:00:00 2001 From: Bai Date: Fri, 19 Jun 2020 20:41:51 +0800 Subject: [PATCH 02/20] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0=E6=94=B9?= =?UTF-8?q?=E5=AF=86=E8=AE=A1=E5=88=92=E5=AE=89=E5=85=A8=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/conf.py | 3 ++- apps/jumpserver/settings/custom.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 5da97cfb7..da6aafebe 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -256,7 +256,8 @@ class Config(dict): 'FORCE_SCRIPT_NAME': '', 'LOGIN_CONFIRM_ENABLE': False, 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False, - 'ORG_CHANGE_TO_URL': '' + 'ORG_CHANGE_TO_URL': '', + 'CHANGE_AUTH_PLAN_ENABLE_SECURE_MODE': True } def compatible_auth_openid_of_key(self): diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index a55693c55..3d3ce4400 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -85,3 +85,5 @@ LOGIN_LOG_KEEP_DAYS = DYNAMIC.LOGIN_LOG_KEEP_DAYS TASK_LOG_KEEP_DAYS = CONFIG.TASK_LOG_KEEP_DAYS ORG_CHANGE_TO_URL = CONFIG.ORG_CHANGE_TO_URL WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD + +CHANGE_AUTH_PLAN_ENABLE_SECURE_MODE = CONFIG.CHANGE_AUTH_PLAN_ENABLE_SECURE_MODE From fcb0aefe3ca6aa7d93c6192c0ccd15ac371ff691 Mon Sep 17 00:00:00 2001 From: Michael Bai Date: Sat, 20 Jun 2020 15:47:39 +0800 Subject: [PATCH 03/20] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E6=94=B9=E5=AF=86?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E5=AE=89=E5=85=A8=E6=A8=A1=E5=BC=8F=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B9=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/conf.py | 2 +- apps/jumpserver/settings/custom.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index da6aafebe..ef1439255 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -257,7 +257,7 @@ class Config(dict): 'LOGIN_CONFIRM_ENABLE': False, 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False, 'ORG_CHANGE_TO_URL': '', - 'CHANGE_AUTH_PLAN_ENABLE_SECURE_MODE': True + 'CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED': True } def compatible_auth_openid_of_key(self): diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 3d3ce4400..a2f302acc 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -86,4 +86,6 @@ TASK_LOG_KEEP_DAYS = CONFIG.TASK_LOG_KEEP_DAYS ORG_CHANGE_TO_URL = CONFIG.ORG_CHANGE_TO_URL WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD -CHANGE_AUTH_PLAN_ENABLE_SECURE_MODE = CONFIG.CHANGE_AUTH_PLAN_ENABLE_SECURE_MODE +CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED = CONFIG.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED + + From f1e5c7c2bb6dccdbc1060418ffbed752aba5b8fe Mon Sep 17 00:00:00 2001 From: Michael Bai Date: Sat, 20 Jun 2020 16:18:58 +0800 Subject: [PATCH 04/20] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=94=B9=E5=AF=86?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E5=AE=89=E5=85=A8=E6=A8=A1=E5=BC=8F=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/conf.py | 3 ++- apps/jumpserver/settings/custom.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 13f25d492..2b4a5b65f 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -259,7 +259,8 @@ class Config(dict): 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False, 'ORG_CHANGE_TO_URL': '', 'LANGUAGE_CODE': 'zh', - 'TIME_ZONE': 'Asia/Shanghai' + 'TIME_ZONE': 'Asia/Shanghai', + 'CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED': True } def compatible_auth_openid_of_key(self): diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 0be476d96..efcf2f4cc 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -90,3 +90,5 @@ WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD XPACK_LICENSE_IS_VALID = DYNAMIC.XPACK_LICENSE_IS_VALID LOGO_URLS = DYNAMIC.LOGO_URLS + +CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED = CONFIG.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED From ff747f9e4248b631f2c2365318aaff3a0fcecafe Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 22 Jun 2020 12:06:42 +0800 Subject: [PATCH 05/20] =?UTF-8?q?feat:=20readme=20=E6=B7=BB=E5=8A=A0docker?= =?UTF-8?q?=20pull?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 07e6f6c91..96a9fbd47 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Python3](https://img.shields.io/badge/python-3.6-green.svg?style=plastic)](https://www.python.org/) [![Django](https://img.shields.io/badge/django-2.2-brightgreen.svg?style=plastic)](https://www.djangoproject.com/) +[![Docker Pulls](https://img.shields.io/docker/pulls/jumpserver/jms_all.svg)](https://hub.docker.com/u/jumpserver) JumpServer 是全球首款开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 规范的运维安全审计系统。 From 005573b53b9ea843e2d1183744dedb9e3aa5bd06 Mon Sep 17 00:00:00 2001 From: xinwen Date: Fri, 19 Jun 2020 17:43:15 +0800 Subject: [PATCH 06/20] =?UTF-8?q?[Fix]=20=E9=87=8D=E6=96=B0=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=20MFA=20=E7=9A=84=E6=BC=8F=E6=B4=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/views/profile/otp.py | 57 ++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/apps/users/views/profile/otp.py b/apps/users/views/profile/otp.py index 83918114e..e5724d506 100644 --- a/apps/users/views/profile/otp.py +++ b/apps/users/views/profile/otp.py @@ -1,4 +1,5 @@ # ~*~ coding: utf-8 ~*~ +import time from django.urls import reverse_lazy, reverse from django.utils.translation import ugettext as _ @@ -6,8 +7,12 @@ from django.views.generic.base import TemplateView from django.views.generic.edit import FormView from django.contrib.auth import logout as auth_logout from django.conf import settings +from django.shortcuts import redirect +from authentication.mixins import AuthMixin +from users.models import User from common.utils import get_logger +from common.utils import get_object_or_none from common.permissions import IsValidUser from ... import forms from .password import UserVerifyPasswordView @@ -46,11 +51,59 @@ class UserOtpEnableInstallAppView(TemplateView): return super().get_context_data(**kwargs) -class UserOtpEnableBindView(TemplateView, FormView): +class UserOtpEnableBindView(AuthMixin, TemplateView, FormView): template_name = 'users/user_otp_enable_bind.html' form_class = forms.UserCheckOtpCodeForm success_url = reverse_lazy('users:user-otp-settings-success') + def get(self, request, *args, **kwargs): + return self._check_can_bind() or super().get(request, *args, **kwargs) + + def post(self, request, *args, **kwargs): + return self._check_can_bind() or super().post(request, *args, **kwargs) + + def _check_can_bind(self): + """ + :return: + - `None` 表示验证成功 + - `Response` 验证失败,调用函数需直接返回该 `Response` + """ + + request = self.request + request_user = request.user + session_user = None + + if not self.request.session.is_empty(): + user_id = self.request.session.get('user_id') + session_user = get_object_or_none(User, pk=user_id) + + auth_password = request.session.get('auth_password') + if request_user.is_authenticated: + # 用户已登录,在 `mfa_enabled` 启用,而且 `otp_secret_key` 不为空的情况,跳转到 + # otp 认证界面 + if request_user.mfa_enabled and request_user.otp_secret_key: + logger.warn(f'OPT_BIND-> authenticated ' + f'request_user.username={request_user.username}, ' + f'request_user.mfa_enabled={request_user.mfa_enabled}, ' + f'request_user.otp_secret_key={request_user.otp_secret_key}') + return redirect(reverse('authentication:user-otp-update')) + return None + elif session_user: + # 未登录,但是验证过了密码,如果是 `reset` 流程,需要 `mfa_enabled` 启用,`otp_secret_key` 为空 + if not all((auth_password, session_user.mfa_enabled, not session_user.otp_secret_key)): + logger.warn(f'OPT_BIND-> auth_password ' + f'session_user.username={session_user.username}, ' + f'auth_password={auth_password}, ' + f'session_user.mfa_enabled={session_user.mfa_enabled}, ' + f'session_user.otp_secret_key={session_user.otp_secret_key}') + return redirect(reverse('authentication:login')) + return None + else: + # 未登录,没有验证过密码,直接跳转到登录界面 + logger.warn(f'OPT_BIND-> anonymous ' + f'REMOTE_ADDR={request.META.get("HTTP_X_FORWARDED_HOST") or request.META.get("REMOTE_ADDR")}') + return redirect(reverse('authentication:login')) + def form_valid(self, form): otp_code = form.cleaned_data.get('otp_code') otp_secret_key = self.request.session.get('otp_secret_key', '') @@ -116,6 +169,8 @@ class UserOtpUpdateView(FormView): valid = user.check_mfa(otp_code) if valid: + user.otp_secret_key = '' + user.save() return super().form_valid(form) else: error = _('MFA code invalid, or ntp sync server time') From 3e993fd0447e5ea304ced83bc68bcad429050dac Mon Sep 17 00:00:00 2001 From: xinwen Date: Mon, 22 Jun 2020 17:04:07 +0800 Subject: [PATCH 07/20] =?UTF-8?q?[Update]=20=E8=B0=83=E6=95=B4`MFA`?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E7=AD=96=E7=95=A5=20V2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/http.py | 4 ++ apps/jumpserver/settings/custom.py | 2 +- apps/users/utils.py | 14 +++++- apps/users/views/profile/otp.py | 72 ++++++++++++---------------- apps/users/views/profile/password.py | 5 +- 5 files changed, 53 insertions(+), 44 deletions(-) diff --git a/apps/common/http.py b/apps/common/http.py index df6b9a78f..f3a743045 100644 --- a/apps/common/http.py +++ b/apps/common/http.py @@ -10,3 +10,7 @@ class HttpResponseTemporaryRedirect(HttpResponse): def __init__(self, redirect_to): HttpResponse.__init__(self) self['Location'] = iri_to_uri(redirect_to) + + +def get_remote_addr(request): + return request.META.get("HTTP_X_FORWARDED_HOST") or request.META.get("REMOTE_ADDR") diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index a2f302acc..2c187b60d 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -88,4 +88,4 @@ WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED = CONFIG.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED - +AUTH_EXPIRED_SECONDS = 60 * 5 diff --git a/apps/users/utils.py b/apps/users/utils.py index 0729115b6..40dee261b 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -5,8 +5,8 @@ import re import pyotp import base64 import logging +import time -from django.http import Http404 from django.conf import settings from django.utils.translation import ugettext as _ from django.core.cache import cache @@ -333,3 +333,15 @@ def get_source_choices(): if settings.AUTH_CAS: choices.append((User.SOURCE_CAS, choices_all[User.SOURCE_CAS])) return choices + + +def is_auth_time_valid(session, key): + return True if session.get(key, 0) > time.time() else False + + +def is_auth_password_time_valid(session): + return is_auth_time_valid(session, 'auth_password_expired_at') + + +def is_auth_otp_time_valid(session): + return is_auth_time_valid(session, 'auth_opt_expired_at') diff --git a/apps/users/views/profile/otp.py b/apps/users/views/profile/otp.py index e5724d506..41a6302f1 100644 --- a/apps/users/views/profile/otp.py +++ b/apps/users/views/profile/otp.py @@ -7,17 +7,17 @@ from django.views.generic.base import TemplateView from django.views.generic.edit import FormView from django.contrib.auth import logout as auth_logout from django.conf import settings -from django.shortcuts import redirect +from django.http.response import HttpResponseForbidden from authentication.mixins import AuthMixin from users.models import User -from common.utils import get_logger -from common.utils import get_object_or_none +from common.utils import get_logger, get_object_or_none from common.permissions import IsValidUser from ... import forms from .password import UserVerifyPasswordView from ...utils import ( generate_otp_uri, check_otp_code, get_user_or_pre_auth_user, + is_auth_password_time_valid, is_auth_otp_time_valid ) __all__ = [ @@ -57,52 +57,43 @@ class UserOtpEnableBindView(AuthMixin, TemplateView, FormView): success_url = reverse_lazy('users:user-otp-settings-success') def get(self, request, *args, **kwargs): - return self._check_can_bind() or super().get(request, *args, **kwargs) + if self._check_can_bind(): + return super().get(request, *args, **kwargs) + return HttpResponseForbidden() def post(self, request, *args, **kwargs): - return self._check_can_bind() or super().post(request, *args, **kwargs) + if self._check_can_bind(): + return super().post(request, *args, **kwargs) + return HttpResponseForbidden() - def _check_can_bind(self): - """ - :return: - - `None` 表示验证成功 - - `Response` 验证失败,调用函数需直接返回该 `Response` - """ + def _check_authenticated_user_can_bind(self): + user = self.request.user + session = self.request.session - request = self.request - request_user = request.user + if not user.mfa_enabled: + return is_auth_password_time_valid(session) + + if not user.otp_secret_key: + return is_auth_password_time_valid(session) + + return is_auth_otp_time_valid(session) + + def _check_unauthenticated_user_can_bind(self): session_user = None - if not self.request.session.is_empty(): user_id = self.request.session.get('user_id') session_user = get_object_or_none(User, pk=user_id) - auth_password = request.session.get('auth_password') - if request_user.is_authenticated: - # 用户已登录,在 `mfa_enabled` 启用,而且 `otp_secret_key` 不为空的情况,跳转到 - # otp 认证界面 - if request_user.mfa_enabled and request_user.otp_secret_key: - logger.warn(f'OPT_BIND-> authenticated ' - f'request_user.username={request_user.username}, ' - f'request_user.mfa_enabled={request_user.mfa_enabled}, ' - f'request_user.otp_secret_key={request_user.otp_secret_key}') - return redirect(reverse('authentication:user-otp-update')) - return None - elif session_user: - # 未登录,但是验证过了密码,如果是 `reset` 流程,需要 `mfa_enabled` 启用,`otp_secret_key` 为空 - if not all((auth_password, session_user.mfa_enabled, not session_user.otp_secret_key)): - logger.warn(f'OPT_BIND-> auth_password ' - f'session_user.username={session_user.username}, ' - f'auth_password={auth_password}, ' - f'session_user.mfa_enabled={session_user.mfa_enabled}, ' - f'session_user.otp_secret_key={session_user.otp_secret_key}') - return redirect(reverse('authentication:login')) - return None + if session_user: + if all((is_auth_password_time_valid(self.request.session), session_user.mfa_enabled, not session_user.otp_secret_key)): + return True + return False + + def _check_can_bind(self): + if self.request.user.is_authenticated: + return self._check_authenticated_user_can_bind() else: - # 未登录,没有验证过密码,直接跳转到登录界面 - logger.warn(f'OPT_BIND-> anonymous ' - f'REMOTE_ADDR={request.META.get("HTTP_X_FORWARDED_HOST") or request.META.get("REMOTE_ADDR")}') - return redirect(reverse('authentication:login')) + return self._check_unauthenticated_user_can_bind() def form_valid(self, form): otp_code = form.cleaned_data.get('otp_code') @@ -169,8 +160,7 @@ class UserOtpUpdateView(FormView): valid = user.check_mfa(otp_code) if valid: - user.otp_secret_key = '' - user.save() + self.request.session['auth_opt_expired_at'] = time.time() + settings.AUTH_EXPIRED_SECONDS return super().form_valid(form) else: error = _('MFA code invalid, or ntp sync server time') diff --git a/apps/users/views/profile/password.py b/apps/users/views/profile/password.py index bb7caa9a1..1fbbd64a7 100644 --- a/apps/users/views/profile/password.py +++ b/apps/users/views/profile/password.py @@ -1,8 +1,10 @@ # ~*~ coding: utf-8 ~*~ +import time +from django.conf import settings from django.contrib.auth import authenticate from django.shortcuts import redirect -from django.urls import reverse_lazy, reverse +from django.urls import reverse_lazy from django.utils.translation import ugettext as _ from django.views.generic.edit import UpdateView, FormView from django.contrib.auth import logout as auth_logout @@ -76,6 +78,7 @@ class UserVerifyPasswordView(FormView): user.save() self.request.session['user_id'] = str(user.id) self.request.session['auth_password'] = 1 + self.request.session['auth_password_expired_at'] = time.time() + settings.AUTH_EXPIRED_SECONDS return redirect(self.get_success_url()) def get_success_url(self): From cf6aba1f38aa6ec5ce18fd4155280313311c522d Mon Sep 17 00:00:00 2001 From: Bai Date: Sun, 28 Jun 2020 10:43:47 +0800 Subject: [PATCH 08/20] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9csv=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=EF=BC=8C=E6=9C=80=E5=A4=A7=E9=99=90=E5=88=B6=E6=9D=A1?= =?UTF-8?q?=E7=9B=AE=E6=95=B0=E4=BB=8E100->10000=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/drf/renders/csv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/common/drf/renders/csv.py b/apps/common/drf/renders/csv.py index 0d90af359..435e3d4a6 100644 --- a/apps/common/drf/renders/csv.py +++ b/apps/common/drf/renders/csv.py @@ -30,7 +30,7 @@ class JMSCSVRender(BaseRenderer): @staticmethod def _gen_table(data, fields): - data = data[:100] + data = data[:10000] yield ['*{}'.format(f.label) if f.required else f.label for f in fields] for item in data: From d43709f584d9971135dc8cbf735d65e42d43345c Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 28 Jun 2020 15:17:15 +0800 Subject: [PATCH 09/20] =?UTF-8?q?docs(github):=20=E4=BF=AE=E6=94=B9=20gith?= =?UTF-8?q?ub=20issue=20=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更改版本号说明,1.4及之前不再提供支持 --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index c88d7639d..5c40fded3 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ ##### 使用版本 -[请提供你使用的Jumpserver版本 1.x.x 注: 0.3.x不再提供支持] +[请提供你使用的JumpServer版本 如 2.0.1 注: 1.4及以下版本不再提供支持] ##### 问题复现步骤 1. [步骤1] From 3077d11483c0416853d438171fe82531ef335ffd Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 7 Jul 2020 13:02:47 +0800 Subject: [PATCH 10/20] =?UTF-8?q?ci(release&build):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20github=20workflows,=20=E8=87=AA=E5=8A=A8=E6=9E=84=E5=BB=BA?= =?UTF-8?q?=20release?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 utils/build.sh 脚本,构建后放到 release 目录中 - 当 push tags时,自动创建 Release Draft - 自动生成 Release Change Log - 自动构建包,上传到 Release Assets --- .github/release-config.yml | 44 +++++++++++++++++++++++++++ .github/workflows/release-drafter.yml | 38 +++++++++++++++++++++++ .gitignore | 4 ++- build.sh | 11 ------- utils/build.sh | 22 ++++++++++++++ utils/build_docker.sh | 12 ++++++++ 6 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 .github/release-config.yml create mode 100644 .github/workflows/release-drafter.yml delete mode 100644 build.sh create mode 100644 utils/build.sh create mode 100644 utils/build_docker.sh diff --git a/.github/release-config.yml b/.github/release-config.yml new file mode 100644 index 000000000..859704707 --- /dev/null +++ b/.github/release-config.yml @@ -0,0 +1,44 @@ +name-template: 'Release v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '🌱 新功能 Features' + labels: + - 'feature' + - 'enhancement' + - 'feat' + - '新功能' + - title: '🚀 性能优化 Optimization' + labels: + - 'perf' + - 'opt' + - 'refactor' + - 'Optimization' + - '优化' + - title: '🐛 Bug修复 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 其它 Maintenance' + labels: + - 'chore' + - 'docs' +exclude-labels: + - 'no' + - '无需处理' + - 'wontfix' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## 版本变化 What’s Changed + $CHANGES \ No newline at end of file diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 000000000..5d6394e8e --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,38 @@ +on: + push: + # Sequence of patterns matched against refs/tags + tags: + - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + +name: Create Release And Upload assets + +jobs: + create-realese: + name: Create Release + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Create Release + id: create_release + uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + config-name: release-config.yml + + build-and-release: + needs: create-realese + name: Build and Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build it and upload + uses: ibuler/action-build-upload@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + upload_url: ${{ needs.create-realese.outputs.upload_url }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9d65d375a..cb931287b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,6 @@ docs/_build/ xpack logs/* ### Vagrant ### -.vagrant/ \ No newline at end of file +.vagrant/ +release/* +releashe diff --git a/build.sh b/build.sh deleted file mode 100644 index 9350c1616..000000000 --- a/build.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# - -version=$1 -if [ -z "$version" ];then - echo "Usage: sh build version" - exit -fi - - -docker build -t jumpserver/jumpserver:$version . diff --git a/utils/build.sh b/utils/build.sh new file mode 100644 index 000000000..e499edf40 --- /dev/null +++ b/utils/build.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# 该build基于registry.fit2cloud.com/public/python:3 +utils_dir=$(pwd) +project_dir=$(dirname "$utils_dir") +release_dir=${project_dir}/release + +# 编译翻译文件 +cd "${project_dir}/apps" && python manage.py compilemessages + +# 打包 +cd "${project_dir}" || exit 3 +rm -rf "${release_dir:?}/*" +to_dir="${release_dir}/jumpserver" +mkdir -p "${to_dir}" +git archive --format tar HEAD | tar x -C "${to_dir}" + +# 修改版本号文件 +if [[ -n ${VERSION} ]]; then + sed -i "" "s@VERSION = .*@VERSION = \"${VERSION}\"@g" "${release_dir}/jumpserver/apps/jumpserver/const.py" +fi + diff --git a/utils/build_docker.sh b/utils/build_docker.sh new file mode 100644 index 000000000..62dede6ed --- /dev/null +++ b/utils/build_docker.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# +utils_dir=$(dirname "$0") +project_dir=$(dirname "${utils_dir}") + +version=$1 +if [ -z "$version" ]; then + echo "Usage: sh build version" + exit +fi + +cd "${project_dir}" && docker build -t "jumpserver/jumpserver:${version}" . From 18174e286795c8cae02fe2fca07efd3f6997f9ed Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 7 Jul 2020 13:25:34 +0800 Subject: [PATCH 11/20] =?UTF-8?q?fix(build):=20=E4=BF=AE=E7=A8=BF=E6=9E=84?= =?UTF-8?q?=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/build.sh b/utils/build.sh index e499edf40..d77e30f23 100644 --- a/utils/build.sh +++ b/utils/build.sh @@ -5,8 +5,8 @@ utils_dir=$(pwd) project_dir=$(dirname "$utils_dir") release_dir=${project_dir}/release -# 编译翻译文件 -cd "${project_dir}/apps" && python manage.py compilemessages +# 安装依赖包 +yum -y install git # 打包 cd "${project_dir}" || exit 3 From 43741dc9b2140936b218e7c57b8afb74bda92344 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 7 Jul 2020 13:36:48 +0800 Subject: [PATCH 12/20] =?UTF-8?q?ci(build):=20=E4=BF=AE=E6=94=B9=20workflo?= =?UTF-8?q?w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改使用action jumpserver/action-build-upload-assets --- .github/release-config.yml | 2 +- .github/workflows/release-drafter.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/release-config.yml b/.github/release-config.yml index 859704707..1a75bdf6f 100644 --- a/.github/release-config.yml +++ b/.github/release-config.yml @@ -1,4 +1,4 @@ -name-template: 'Release v$RESOLVED_VERSION' +name-template: 'v$RESOLVED_VERSION' tag-template: 'v$RESOLVED_VERSION' categories: - title: '🌱 新功能 Features' diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 5d6394e8e..415725a8d 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -30,7 +30,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build it and upload - uses: ibuler/action-build-upload@master + uses: jumpserver/action-build-upload-assets@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 6b16aa6bc059b32002921e5dc708c750c1dfb48c Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 7 Jul 2020 13:51:23 +0800 Subject: [PATCH 13/20] =?UTF-8?q?ci(build):=20=E4=BF=AE=E6=94=B9=20?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sed 在不同系统下表现不同 --- utils/build.sh | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/utils/build.sh b/utils/build.sh index d77e30f23..1cf6e9de8 100644 --- a/utils/build.sh +++ b/utils/build.sh @@ -6,7 +6,7 @@ project_dir=$(dirname "$utils_dir") release_dir=${project_dir}/release # 安装依赖包 -yum -y install git +command -v git || yum -y install git # 打包 cd "${project_dir}" || exit 3 @@ -15,8 +15,14 @@ to_dir="${release_dir}/jumpserver" mkdir -p "${to_dir}" git archive --format tar HEAD | tar x -C "${to_dir}" -# 修改版本号文件 -if [[ -n ${VERSION} ]]; then - sed -i "" "s@VERSION = .*@VERSION = \"${VERSION}\"@g" "${release_dir}/jumpserver/apps/jumpserver/const.py" +if [[ $(uname) == 'Darwin' ]];then + alias sedi="sed -i ''" +else + alias sedi='sed -i' +fi + +# 修改版本号文件 +if [[ -n ${VERSION} ]]; then + sedi "s@VERSION = .*@VERSION = \"${VERSION}\"@g" "${to_dir}/apps/jumpserver/const.py" fi From 23ef185b7e23baf95662e0a78480daf92d610b6f Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 7 Jul 2020 14:29:37 +0800 Subject: [PATCH 14/20] =?UTF-8?q?fix(build):=20=E4=BF=AE=E6=94=B9=E8=B0=83?= =?UTF-8?q?=E7=94=A8action=20jumpserver/action-build-upload-asset=E7=9A=84?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release-drafter.yml | 1 - utils/build.sh | 0 2 files changed, 1 deletion(-) mode change 100644 => 100755 utils/build.sh diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 415725a8d..a920dd7d3 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -34,5 +34,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - github_token: ${{ secrets.GITHUB_TOKEN }} upload_url: ${{ needs.create-realese.outputs.upload_url }} \ No newline at end of file diff --git a/utils/build.sh b/utils/build.sh old mode 100644 new mode 100755 From ce7edc1612338c9b57f8766046b4175b969662ae Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 7 Jul 2020 18:41:26 +0800 Subject: [PATCH 15/20] =?UTF-8?q?ci(release):=20=E4=BF=AE=E6=94=B9=20relea?= =?UTF-8?q?se=20=E4=BD=BF=E7=94=A8=E7=9A=84tag=EF=BC=8C=E8=80=8C=E4=B8=8D?= =?UTF-8?q?=E6=98=AF=E8=87=AA=E5=8A=A8=E7=94=9F=E6=88=90=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release-drafter.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index a920dd7d3..e50aae839 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -15,6 +15,13 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 + - name: Get version + id: get_version + run: | + TAG=$(basename ${GITHUB_REF}) + VERSION=${TAG/v/} + echo "::set-output name=TAG::$TAG" + echo "::set-output name=VERSION::$VERSION" - name: Create Release id: create_release uses: release-drafter/release-drafter@v5 @@ -22,6 +29,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: config-name: release-config.yml + version: ${{ steps.get_version.outputs.TAG }} + tag: ${{ steps.get_version.outputs.TAG }} build-and-release: needs: create-realese From 10f99be100c2a67c435a7b895d0f3ec1185c4df9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 16 Jun 2020 20:11:50 +0800 Subject: [PATCH 16/20] =?UTF-8?q?=E6=B7=BB=E5=8A=A0example=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/example_api.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 utils/example_api.py diff --git a/utils/example_api.py b/utils/example_api.py new file mode 100644 index 000000000..6bc86c665 --- /dev/null +++ b/utils/example_api.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +import requests + +# 私有token页面上目前不允许创建,只能后台生成,见 https://docs.jumpserver.org/zh/master/dev/rest_api/ +private_token = '10659d70a223235b8f76d45a3023eca1147488d7' + + +def do_request(url, data=None, method='get', params=None, org_id=''): + authorization = 'Token {}'.format(private_token) + headers = {'Authorization': authorization, 'Content-Type': 'application/json'} + if org_id: + headers['X-JMS-ORG'] = org_id + resp = requests.request(method=method, url=url, data=data, params=params, headers=headers) + return resp + + +def get_assets_list(): + url = 'http://localhost:8080/api/v1/assets/assets/?limit=10' + resp = do_request(url) + print(resp.status_code) + print(resp.json()) + print(resp) + + +if __name__ == '__main__': + get_assets_list() \ No newline at end of file From c3eafbee8c1ae983ed02b3dbfced43eb9c25cc57 Mon Sep 17 00:00:00 2001 From: xinwen Date: Tue, 30 Jun 2020 14:15:35 +0800 Subject: [PATCH 17/20] =?UTF-8?q?[Fix]=20X-Pack/=E4=BA=91=E7=AE=A1?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=20i18n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/zh/LC_MESSAGES/django.mo | Bin 53928 -> 53928 bytes apps/locale/zh/LC_MESSAGES/django.po | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 1fa128c6a49cb95568a8074b96bb6c24e150b1f2..1c50f6958b93d213f70c18bc07a73b77a0614c06 100644 GIT binary patch delta 27 lcmV+$0ObFur30v?1F*NP4CaZa=%I|}tfA(Wu(QLh Date: Tue, 23 Jun 2020 18:24:45 +0800 Subject: [PATCH 18/20] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9=20ftp=20=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E6=8C=89=E5=BC=80=E5=A7=8B=E6=97=A5=E6=9C=9F=E6=8E=92?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/audits/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/audits/api.py b/apps/audits/api.py index 9d6d0339e..7397b3596 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -27,6 +27,7 @@ class FTPLogViewSet(CreateModelMixin, ] filter_fields = ['user', 'asset', 'system_user', 'filename'] search_fields = filter_fields + ordering = ['-date_start'] class UserLoginLogViewSet(ListModelMixin, CommonGenericViewSet): From 17a76994dc2c75c1daed9521c1d9cf170821913b Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 1 Jul 2020 14:54:42 +0800 Subject: [PATCH 19/20] =?UTF-8?q?[Update]=20=E7=B3=BB=E7=BB=9F=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B7=BB=E5=8A=A0=E8=BF=87=E6=BB=A4=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/system_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index dc134ee97..ff3f81f36 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -28,7 +28,7 @@ class SystemUserViewSet(OrgBulkModelViewSet): System user api set, for add,delete,update,list,retrieve resource """ model = SystemUser - filter_fields = ("name", "username") + filter_fields = ("name", "username", "protocol") search_fields = filter_fields serializer_class = serializers.SystemUserSerializer serializer_classes = { From 6f5a92c21f7e8f643a8ae41a4f072447b7902238 Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 1 Jul 2020 17:21:00 +0800 Subject: [PATCH 20/20] =?UTF-8?q?[Update]=20assets/gathered=5Fuser=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=BF=87=E6=BB=A4=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/gathered_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/api/gathered_user.py b/apps/assets/api/gathered_user.py index e024fedb1..896dab7e1 100644 --- a/apps/assets/api/gathered_user.py +++ b/apps/assets/api/gathered_user.py @@ -18,5 +18,5 @@ class GatheredUserViewSet(OrgModelViewSet): permission_classes = [IsOrgAdmin] extra_filter_backends = [AssetRelatedByNodeFilterBackend] - filter_fields = ['asset', 'username', 'present', 'asset__ip', 'asset__hostname'] + filter_fields = ['asset', 'username', 'present', 'asset__ip', 'asset__hostname', 'asset_id'] search_fields = ['username', 'asset__ip', 'asset__hostname']