Merge branch 'v2.0' of github.com:jumpserver/jumpserver into v2.0

pull/4272/head
ibuler 2020-07-08 15:48:05 +08:00
commit 0dd4c8adc2
15 changed files with 87 additions and 19 deletions

View File

@ -24,23 +24,20 @@ JumpServer 采纳分布式架构,支持多机房跨区域部署,支持横向
## 版本说明 ## 版本说明
从 2.0 开始 JumpServer 版本号规则将发生变更,如下所示 自 v2.0.0 发布后, JumpServer 版本号命名将变更为v大版本.功能版本.Bug修复版本。比如
大版本.功能版本.bug修复版本
``` ```
如 2.0.1 修复bug 将是 2.0.2 v2.0.1 是 v2.0.0 之后的Bug修复版本
如 2.0.2 添加新功能后 将会是 2.1.0 v2.1.0 是 v2.0.0 之后的功能版本。
``` ```
并且 JumpServer 以后也会像其它优秀开源项目一样,同时维护多个版本,目前计划同时维护 3 个版本 像其它优秀开源项目一样JumpServer 每个月会发布一个功能版本,并同时维护 3 个功能版本。比如:
``` ```
如 2.1, 2.2, 2.3 版本我们会同时维护, 发布 2.4 版本后2.1 停止维护 在 v2.4 发布前,我们会同时维护 v2.1、v2.2、v2.3
在 v2.4 发布后,我们会同时维护 v2.2、v2.3、v2.4v2.1 会停止维护。
``` ```
JumpServer 每个月会发布一个功能版本, 修复版本视情况而定
## 功能列表 ## 功能列表
<table> <table>

View File

@ -18,5 +18,5 @@ class GatheredUserViewSet(OrgModelViewSet):
permission_classes = [IsOrgAdmin] permission_classes = [IsOrgAdmin]
extra_filter_backends = [AssetRelatedByNodeFilterBackend] 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'] search_fields = ['username', 'asset__ip', 'asset__hostname']

View File

@ -28,7 +28,7 @@ class SystemUserViewSet(OrgBulkModelViewSet):
System user api set, for add,delete,update,list,retrieve resource System user api set, for add,delete,update,list,retrieve resource
""" """
model = SystemUser model = SystemUser
filter_fields = ("name", "username") filter_fields = ("name", "username", "protocol")
search_fields = filter_fields search_fields = filter_fields
serializer_class = serializers.SystemUserSerializer serializer_class = serializers.SystemUserSerializer
serializer_classes = { serializer_classes = {

View File

@ -27,6 +27,7 @@ class FTPLogViewSet(CreateModelMixin,
] ]
filter_fields = ['user', 'asset', 'system_user', 'filename'] filter_fields = ['user', 'asset', 'system_user', 'filename']
search_fields = filter_fields search_fields = filter_fields
ordering = ['-date_start']
class UserLoginLogViewSet(ListModelMixin, CommonGenericViewSet): class UserLoginLogViewSet(ListModelMixin, CommonGenericViewSet):

View File

@ -30,7 +30,7 @@ class JMSCSVRender(BaseRenderer):
@staticmethod @staticmethod
def _gen_table(data, fields): 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] yield ['*{}'.format(f.label) if f.required else f.label for f in fields]
for item in data: for item in data:

View File

@ -10,3 +10,7 @@ class HttpResponseTemporaryRedirect(HttpResponse):
def __init__(self, redirect_to): def __init__(self, redirect_to):
HttpResponse.__init__(self) HttpResponse.__init__(self)
self['Location'] = iri_to_uri(redirect_to) 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")

View File

@ -259,7 +259,8 @@ class Config(dict):
'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False, 'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False,
'ORG_CHANGE_TO_URL': '', 'ORG_CHANGE_TO_URL': '',
'LANGUAGE_CODE': 'zh', '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): def compatible_auth_openid_of_key(self):

View File

@ -86,7 +86,11 @@ TASK_LOG_KEEP_DAYS = CONFIG.TASK_LOG_KEEP_DAYS
ORG_CHANGE_TO_URL = CONFIG.ORG_CHANGE_TO_URL ORG_CHANGE_TO_URL = CONFIG.ORG_CHANGE_TO_URL
WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD WINDOWS_SKIP_ALL_MANUAL_PASSWORD = CONFIG.WINDOWS_SKIP_ALL_MANUAL_PASSWORD
AUTH_EXPIRED_SECONDS = 60 * 5
# XPACK # XPACK
XPACK_LICENSE_IS_VALID = DYNAMIC.XPACK_LICENSE_IS_VALID XPACK_LICENSE_IS_VALID = DYNAMIC.XPACK_LICENSE_IS_VALID
LOGO_URLS = DYNAMIC.LOGO_URLS LOGO_URLS = DYNAMIC.LOGO_URLS
CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED = CONFIG.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED

Binary file not shown.

View File

@ -3755,7 +3755,7 @@ msgstr "腾讯云"
#: xpack/plugins/cloud/serializers.py:53 #: xpack/plugins/cloud/serializers.py:53
msgid "History count" msgid "History count"
msgstr "用户数量" msgstr "执行次数"
#: xpack/plugins/cloud/serializers.py:54 #: xpack/plugins/cloud/serializers.py:54
msgid "Instance count" msgid "Instance count"

View File

@ -351,7 +351,7 @@ class AssetPermissionUtil(AssetPermissionUtilCacheMixin):
self.add_favorite_node_if_need(user_tree) self.add_favorite_node_if_need(user_tree)
self.set_user_tree_to_cache_if_need(user_tree) self.set_user_tree_to_cache_if_need(user_tree)
self.set_user_tree_to_local(user_tree) self.set_user_tree_to_local(user_tree)
print(user_tree) # print(user_tree)
return user_tree return user_tree
# Todo: 是否可以获取多个资产的系统用户 # Todo: 是否可以获取多个资产的系统用户

View File

@ -275,6 +275,7 @@ class PublicSettingApi(generics.RetrieveAPIView):
"LOGIN_CONFIRM_ENABLE": settings.LOGIN_CONFIRM_ENABLE, "LOGIN_CONFIRM_ENABLE": settings.LOGIN_CONFIRM_ENABLE,
"SECURITY_VIEW_AUTH_NEED_MFA": settings.SECURITY_VIEW_AUTH_NEED_MFA, "SECURITY_VIEW_AUTH_NEED_MFA": settings.SECURITY_VIEW_AUTH_NEED_MFA,
"SECURITY_MFA_VERIFY_TTL": settings.SECURITY_MFA_VERIFY_TTL, "SECURITY_MFA_VERIFY_TTL": settings.SECURITY_MFA_VERIFY_TTL,
"SECURITY_COMMAND_EXECUTION": settings.SECURITY_COMMAND_EXECUTION,
"LOGO_URLS": settings.LOGO_URLS, "LOGO_URLS": settings.LOGO_URLS,
"PASSWORD_RULE": { "PASSWORD_RULE": {
'SECURITY_PASSWORD_MIN_LENGTH': settings.SECURITY_PASSWORD_MIN_LENGTH, 'SECURITY_PASSWORD_MIN_LENGTH': settings.SECURITY_PASSWORD_MIN_LENGTH,

View File

@ -5,8 +5,8 @@ import re
import pyotp import pyotp
import base64 import base64
import logging import logging
import time
from django.http import Http404
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.core.cache import cache from django.core.cache import cache
@ -333,3 +333,15 @@ def get_source_choices():
if settings.AUTH_CAS: if settings.AUTH_CAS:
choices.append((User.SOURCE_CAS, choices_all[User.SOURCE_CAS])) choices.append((User.SOURCE_CAS, choices_all[User.SOURCE_CAS]))
return choices 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')

View File

@ -1,4 +1,5 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
import time
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -6,13 +7,17 @@ from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.contrib.auth import logout as auth_logout from django.contrib.auth import logout as auth_logout
from django.conf import settings from django.conf import settings
from django.http.response import HttpResponseForbidden
from common.utils import get_logger from authentication.mixins import AuthMixin
from users.models import User
from common.utils import get_logger, get_object_or_none
from common.permissions import IsValidUser from common.permissions import IsValidUser
from ... import forms from ... import forms
from .password import UserVerifyPasswordView from .password import UserVerifyPasswordView
from ...utils import ( from ...utils import (
generate_otp_uri, check_otp_code, get_user_or_pre_auth_user, generate_otp_uri, check_otp_code, get_user_or_pre_auth_user,
is_auth_password_time_valid, is_auth_otp_time_valid
) )
__all__ = [ __all__ = [
@ -46,11 +51,50 @@ class UserOtpEnableInstallAppView(TemplateView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class UserOtpEnableBindView(TemplateView, FormView): class UserOtpEnableBindView(AuthMixin, TemplateView, FormView):
template_name = 'users/user_otp_enable_bind.html' template_name = 'users/user_otp_enable_bind.html'
form_class = forms.UserCheckOtpCodeForm form_class = forms.UserCheckOtpCodeForm
success_url = reverse_lazy('authentication:user-otp-settings-success') success_url = reverse_lazy('authentication:user-otp-settings-success')
def get(self, request, *args, **kwargs):
if self._check_can_bind():
return super().get(request, *args, **kwargs)
return HttpResponseForbidden()
def post(self, request, *args, **kwargs):
if self._check_can_bind():
return super().post(request, *args, **kwargs)
return HttpResponseForbidden()
def _check_authenticated_user_can_bind(self):
user = self.request.user
session = self.request.session
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)
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:
return self._check_unauthenticated_user_can_bind()
def form_valid(self, form): def form_valid(self, form):
otp_code = form.cleaned_data.get('otp_code') otp_code = form.cleaned_data.get('otp_code')
otp_secret_key = self.request.session.get('otp_secret_key', '') otp_secret_key = self.request.session.get('otp_secret_key', '')
@ -116,6 +160,7 @@ class UserOtpUpdateView(FormView):
valid = user.check_mfa(otp_code) valid = user.check_mfa(otp_code)
if valid: if valid:
self.request.session['auth_opt_expired_at'] = time.time() + settings.AUTH_EXPIRED_SECONDS
return super().form_valid(form) return super().form_valid(form)
else: else:
error = _('MFA code invalid, or ntp sync server time') error = _('MFA code invalid, or ntp sync server time')

View File

@ -1,8 +1,10 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
import time
from django.conf import settings
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from django.shortcuts import redirect 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.utils.translation import ugettext as _
from django.views.generic.edit import UpdateView, FormView from django.views.generic.edit import UpdateView, FormView
from django.contrib.auth import logout as auth_logout from django.contrib.auth import logout as auth_logout
@ -76,6 +78,7 @@ class UserVerifyPasswordView(FormView):
user.save() user.save()
self.request.session['user_id'] = str(user.id) self.request.session['user_id'] = str(user.id)
self.request.session['auth_password'] = 1 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()) return redirect(self.get_success_url())
def get_success_url(self): def get_success_url(self):