Compare commits

...

13 Commits
v3 ... v2.10

Author SHA1 Message Date
xinwen
c5ac45b734 fix: 将 es 的 doc_type 默认值改为 _doc 2021-08-11 16:24:50 +08:00
xinwen
53dd874b23 fix: 修复索引不存在时报错 2021-08-11 14:49:47 +08:00
xinwen
0f0f0b6e4f fix: 无效的 es 报 500 2021-08-10 18:42:24 +08:00
xinwen
1942fc5a51 fix: 修复 es 命令存储过滤不准确 2021-08-10 17:14:46 +08:00
xinwen
8f63b38a76 fix: 其他组织中创建的用户不要添加到默认组织了 2021-06-04 04:29:49 -05:00
Bai
457784bf0d fix: 修复组织批量删除的问题(翻译) 2021-06-03 11:36:03 +08:00
Bai
3e1883ceda fix: 修复组织批量删除的问题 2021-06-03 11:36:03 +08:00
Michael Bai
d183e3ddd0 fix: 修复创建/更新用户时密码策略相关的问题 2021-05-23 21:56:16 -05:00
Michael Bai
911ed45d6a fix: 修复parser没有处理int类型数据的问题 2021-05-23 21:53:49 -05:00
ibuler
344c291583 fix(assets): 修复网关信息没有密码的bug 2021-05-21 15:16:55 +08:00
Bai
162e5b204f fix: 修改cloud翻译 2021-05-20 22:27:53 -05:00
xinwen
e0c3998c45 fix: 修复 default 组织用户数量统计错误 2021-05-21 10:36:06 +08:00
ibuler
b912031cc2 fix: 修复周期监测任务配置的bug 2021-05-21 10:34:45 +08:00
13 changed files with 147 additions and 108 deletions

View File

@@ -59,7 +59,7 @@ class GatewaySerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
'created_by', 'comment',
]
fields_fk = ['domain']
fields = fields_small + fields_fk
fields = fields_small + fields_fk
extra_kwargs = {
'password': {'write_only': True, 'validators': [NoSpecialChars()]},
'private_key': {"write_only": True},
@@ -78,12 +78,12 @@ class GatewaySerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
class GatewayWithAuthSerializer(GatewaySerializer):
def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info)
fields.extend(
['password', 'private_key']
)
return fields
class Meta(GatewaySerializer.Meta):
extra_kwargs = {
'password': {'write_only': False, 'validators': [NoSpecialChars()]},
'private_key': {"write_only": False},
'public_key': {"write_only": False},
}
class DomainWithGatewaySerializer(BulkOrgResourceModelSerializer):

View File

@@ -94,7 +94,7 @@ class BaseFileParser(BaseParser):
new_row_data = {}
serializer_fields = self.serializer_fields
for k, v in row_data.items():
if isinstance(v, list) or isinstance(v, dict) or isinstance(v, str) and k.strip() and v.strip():
if type(v) in [list, dict, int] or (isinstance(v, str) and k.strip() and v.strip()):
# 解决类似disk_info为字符串的'{}'的问题
if not isinstance(v, str) and isinstance(serializer_fields[k], serializers.CharField):
v = str(v)

View File

@@ -279,7 +279,7 @@ class Config(dict):
'WINDOWS_SSH_DEFAULT_SHELL': 'cmd',
'FLOWER_URL': "127.0.0.1:5555",
'DEFAULT_ORG_SHOW_ALL_USERS': True,
'PERIOD_TASK_ENABLE': True,
'PERIOD_TASK_ENABLED': True,
'FORCE_SCRIPT_NAME': '',
'LOGIN_CONFIRM_ENABLE': False,
'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False,

Binary file not shown.

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-05-20 14:56+0800\n"
"POT-Creation-Date: 2021-06-03 11:34+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@@ -99,7 +99,7 @@ msgstr "动作"
#: terminal/backends/command/models.py:18
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:38
#: tickets/models/comment.py:17 users/models/user.py:176
#: users/models/user.py:738 users/models/user.py:764
#: users/models/user.py:740 users/models/user.py:766
#: users/serializers/group.py:20
#: users/templates/users/user_asset_permission.html:38
#: users/templates/users/user_asset_permission.html:64
@@ -184,7 +184,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
#: users/templates/users/_select_user_modal.html:14
#: xpack/plugins/change_auth_plan/models.py:47
#: xpack/plugins/change_auth_plan/models.py:278
#: xpack/plugins/cloud/serializers.py:71
#: xpack/plugins/cloud/serializers.py:65
msgid "Username"
msgstr "用户名"
@@ -285,7 +285,7 @@ msgid "Cluster"
msgstr "集群"
#: applications/serializers/attrs/application_category/db.py:11
#: ops/models/adhoc.py:146 xpack/plugins/cloud/serializers.py:69
#: ops/models/adhoc.py:146 xpack/plugins/cloud/serializers.py:63
msgid "Host"
msgstr "主机"
@@ -295,7 +295,7 @@ msgstr "主机"
#: applications/serializers/attrs/application_type/oracle.py:11
#: applications/serializers/attrs/application_type/pgsql.py:11
#: assets/models/asset.py:188 assets/models/domain.py:53
#: xpack/plugins/cloud/serializers.py:70
#: xpack/plugins/cloud/serializers.py:64
msgid "Port"
msgstr "端口"
@@ -325,7 +325,7 @@ msgstr "目标URL"
#: xpack/plugins/change_auth_plan/models.py:68
#: xpack/plugins/change_auth_plan/models.py:190
#: xpack/plugins/change_auth_plan/models.py:285
#: xpack/plugins/cloud/serializers.py:73
#: xpack/plugins/cloud/serializers.py:67
msgid "Password"
msgstr "密码"
@@ -407,7 +407,7 @@ msgstr "激活"
#: assets/models/asset.py:196 assets/models/cluster.py:19
#: assets/models/user.py:66 templates/_nav.html:44
#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers.py:166
#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers.py:160
msgid "Admin user"
msgstr "管理用户"
@@ -497,7 +497,7 @@ msgstr "创建者"
#: assets/models/label.py:25 common/db/models.py:72 common/mixins/models.py:50
#: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:25
#: orgs/models.py:420 perms/models/base.py:56 users/models/group.py:18
#: users/models/user.py:765 xpack/plugins/cloud/models.py:107
#: users/models/user.py:767 xpack/plugins/cloud/models.py:107
msgid "Date created"
msgstr "创建日期"
@@ -569,7 +569,7 @@ msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:14
#: users/models/user.py:750
#: users/models/user.py:752
msgid "System"
msgstr "系统"
@@ -678,7 +678,7 @@ msgstr "ssh私钥"
#: users/templates/users/user_asset_permission.html:41
#: users/templates/users/user_asset_permission.html:73
#: users/templates/users/user_asset_permission.html:158
#: xpack/plugins/cloud/models.py:89 xpack/plugins/cloud/serializers.py:167
#: xpack/plugins/cloud/models.py:89 xpack/plugins/cloud/serializers.py:161
msgid "Node"
msgstr "节点"
@@ -1978,13 +1978,13 @@ msgstr "更新任务内容: {}"
msgid "Disk used more than 80%: {} => {}"
msgstr "磁盘使用率超过 80%: {} => {}"
#: orgs/api.py:79
msgid "Have {} exists, Please delete"
msgstr "{} 存在数据, 请先删除"
#: orgs/api.py:77
msgid "The current organization ({}) cannot be deleted"
msgstr "当前组织 ({}) 不能被删除"
#: orgs/api.py:83
msgid "The current organization cannot be deleted"
msgstr "当前组织不能被删除"
#: orgs/api.py:85
msgid "The organization have resource ({}) cannot be deleted"
msgstr "组织有资源 ({}) 不能被删除"
#: orgs/mixins/models.py:45 orgs/mixins/serializers.py:25 orgs/models.py:36
#: orgs/models.py:417 orgs/serializers.py:108
@@ -2100,8 +2100,8 @@ msgid ""
msgstr "应用列表中包含与授权类型不同的应用。({})"
#: perms/serializers/asset/permission.py:45
#: perms/serializers/asset/permission.py:69 users/serializers/user.py:34
#: users/serializers/user.py:82
#: perms/serializers/asset/permission.py:69 users/serializers/user.py:33
#: users/serializers/user.py:81
msgid "Is expired"
msgstr "是否过期"
@@ -2121,7 +2121,7 @@ msgstr "资产名称"
msgid "System users name"
msgstr "系统用户名称"
#: perms/serializers/asset/permission.py:70 users/serializers/user.py:81
#: perms/serializers/asset/permission.py:70 users/serializers/user.py:80
msgid "Is valid"
msgstr "账户是否有效"
@@ -3897,11 +3897,15 @@ msgstr "用户来源"
msgid "Date password last updated"
msgstr "最后更新密码日期"
#: users/models/user.py:746
#: users/models/user.py:603
msgid "Need update password"
msgstr "需要更新密码"
#: users/models/user.py:748
msgid "Administrator"
msgstr "管理员"
#: users/models/user.py:749
#: users/models/user.py:751
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
@@ -3909,7 +3913,7 @@ msgstr "Administrator是初始的超级管理员"
msgid "The old password is incorrect"
msgstr "旧密码错误"
#: users/serializers/profile.py:36 users/serializers/user.py:125
#: users/serializers/profile.py:36 users/serializers/user.py:126
msgid "Password does not match security rules"
msgstr "密码不满足安全规则"
@@ -3921,76 +3925,76 @@ msgstr "新密码不能是最近 {} 次的密码"
msgid "The newly set password is inconsistent"
msgstr "两次密码不一致"
#: users/serializers/profile.py:119 users/serializers/user.py:80
#: users/serializers/profile.py:119 users/serializers/user.py:79
msgid "Is first login"
msgstr "首次登录"
#: users/serializers/user.py:20
#: users/serializers/user.py:22
msgid "Reset link will be generated and sent to the user"
msgstr "生成重置密码链接,通过邮件发送给用户"
#: users/serializers/user.py:21
#: users/serializers/user.py:23
msgid "Set password"
msgstr "设置密码"
#: users/serializers/user.py:28 xpack/plugins/change_auth_plan/models.py:61
#: users/serializers/user.py:27 xpack/plugins/change_auth_plan/models.py:61
#: xpack/plugins/change_auth_plan/serializers.py:30
msgid "Password strategy"
msgstr "密码策略"
#: users/serializers/user.py:30
#: users/serializers/user.py:29
msgid "MFA enabled"
msgstr "是否开启多因子认证"
#: users/serializers/user.py:31
#: users/serializers/user.py:30
msgid "MFA force enabled"
msgstr "强制启用多因子认证"
#: users/serializers/user.py:32
#: users/serializers/user.py:31
msgid "MFA level for display"
msgstr "多因子认证等级(显示名称)"
#: users/serializers/user.py:33
#: users/serializers/user.py:32
msgid "Login blocked"
msgstr "登录被阻塞"
#: users/serializers/user.py:35
#: users/serializers/user.py:34
msgid "Can update"
msgstr "是否可更新"
#: users/serializers/user.py:36
#: users/serializers/user.py:35
msgid "Can delete"
msgstr "是否可删除"
#: users/serializers/user.py:39 users/serializers/user.py:87
#: users/serializers/user.py:38 users/serializers/user.py:86
msgid "Organization role name"
msgstr "组织角色名称"
#: users/serializers/user.py:83
#: users/serializers/user.py:82
msgid "Avatar url"
msgstr "头像路径"
#: users/serializers/user.py:85
#: users/serializers/user.py:84
msgid "Groups name"
msgstr "用户组名"
#: users/serializers/user.py:86
#: users/serializers/user.py:85
msgid "Source name"
msgstr "用户来源名"
#: users/serializers/user.py:88
#: users/serializers/user.py:87
msgid "Super role name"
msgstr "超级角色名称"
#: users/serializers/user.py:89
#: users/serializers/user.py:88
msgid "Total role name"
msgstr "汇总角色名称"
#: users/serializers/user.py:113
#: users/serializers/user.py:112
msgid "Role limit to {}"
msgstr "角色只能为 {}"
#: users/serializers/user.py:210
#: users/serializers/user.py:211
msgid "name not unique"
msgstr "名称重复"
@@ -3999,7 +4003,7 @@ msgid "Security token validation"
msgstr "安全令牌验证"
#: users/templates/users/_base_otp.html:14 xpack/plugins/cloud/models.py:78
#: xpack/plugins/cloud/serializers.py:165
#: xpack/plugins/cloud/serializers.py:159
msgid "Account"
msgstr "账户"
@@ -4740,7 +4744,7 @@ msgstr "云服务商"
msgid "Cloud account"
msgstr "云账号"
#: xpack/plugins/cloud/models.py:81 xpack/plugins/cloud/serializers.py:146
#: xpack/plugins/cloud/models.py:81 xpack/plugins/cloud/serializers.py:140
msgid "Regions"
msgstr "地域"
@@ -4748,7 +4752,7 @@ msgstr "地域"
msgid "Hostname strategy"
msgstr "主机名策略"
#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers.py:169
#: xpack/plugins/cloud/models.py:95 xpack/plugins/cloud/serializers.py:163
msgid "Always update"
msgstr "总是更新"
@@ -4940,19 +4944,19 @@ msgstr ""
msgid "Subscription ID"
msgstr ""
#: xpack/plugins/cloud/serializers.py:55
msgid "{} is required"
msgstr "{} 字段是必填项"
#: xpack/plugins/cloud/serializers.py:49
msgid "This field is required"
msgstr "这个字段是必填项"
#: xpack/plugins/cloud/serializers.py:144
#: xpack/plugins/cloud/serializers.py:138
msgid "History count"
msgstr "执行次数"
#: xpack/plugins/cloud/serializers.py:145
#: xpack/plugins/cloud/serializers.py:139
msgid "Instance count"
msgstr "实例个数"
#: xpack/plugins/cloud/serializers.py:168
#: xpack/plugins/cloud/serializers.py:162
#: xpack/plugins/gathered_user/serializers.py:20
msgid "Periodic display"
msgstr "定时执行"
@@ -5045,6 +5049,12 @@ msgstr "旗舰版"
msgid "Community edition"
msgstr "社区版"
#~ msgid "Have {} exists, Please delete"
#~ msgstr "{} 存在数据, 请先删除"
#~ msgid "{} is required"
#~ msgstr "{} 字段是必填项"
#~ msgid "AppSecret is required"
#~ msgstr "AppSecret 是必须的"

View File

@@ -48,7 +48,6 @@ class OrgViewSet(BulkModelViewSet):
queryset = Organization.objects.all()
serializer_class = OrgSerializer
permission_classes = (IsSuperUserOrAppUser,)
org = None
def get_serializer_class(self):
mapper = {
@@ -58,32 +57,36 @@ class OrgViewSet(BulkModelViewSet):
return mapper.get(self.action, super().get_serializer_class())
@tmp_to_root_org()
def get_data_from_model(self, model):
def get_data_from_model(self, org, model):
if model == User:
data = model.objects.filter(
orgs__id=self.org.id,
m2m_org_members__role__in=[ROLE.USER, ROLE.ADMIN, ROLE.AUDITOR]
orgs__id=org.id, m2m_org_members__role__in=[ROLE.USER, ROLE.ADMIN, ROLE.AUDITOR]
)
elif model == Node:
# 节点不能手动删除,所以排除检查
data = model.objects.filter(org_id=self.org.id).exclude(parent_key='', key__regex=r'^[0-9]+$')
# 节点不能手动删除,所以排除检查
data = model.objects.filter(org_id=org.id).exclude(parent_key='', key__regex=r'^[0-9]+$')
else:
data = model.objects.filter(org_id=self.org.id)
data = model.objects.filter(org_id=org.id)
return data
def destroy(self, request, *args, **kwargs):
self.org = self.get_object()
def allow_bulk_destroy(self, qs, filtered):
return False
def perform_destroy(self, instance):
if str(current_org) == str(instance):
msg = _('The current organization ({}) cannot be deleted'.format(current_org))
raise PermissionDenied(detail=msg)
for model in org_related_models:
data = self.get_data_from_model(model)
if data:
msg = _('Have {} exists, Please delete').format(model._meta.verbose_name)
return Response(data={'error': msg}, status=status.HTTP_403_FORBIDDEN)
else:
if str(current_org) == str(self.org):
msg = _('The current organization cannot be deleted')
return Response(data={'error': msg}, status=status.HTTP_403_FORBIDDEN)
self.org.delete()
return Response({'msg': True}, status=status.HTTP_200_OK)
data = self.get_data_from_model(instance, model)
if not data:
continue
msg = _(
'The organization have resource ({}) cannot be deleted'
).format(model._meta.verbose_name)
raise PermissionDenied(detail=msg)
super().perform_destroy(instance)
class OrgMemberRelationBulkViewSet(JMSBulkRelationModelViewSet):

View File

@@ -78,12 +78,12 @@ class OrgResourceStatisticsCache(OrgRelatedCache):
return self.org
def compute_users_amount(self):
if self.org.is_root():
users_amount = User.objects.exclude(role='App').count()
else:
users_amount = OrganizationMember.objects.values(
'user_id'
).filter(org_id=self.org.id).distinct().count()
users = User.objects.exclude(role='App')
if not self.org.is_root():
users = users.filter(m2m_org_members__org_id=self.org.id)
users_amount = users.values('id').distinct().count()
return users_amount
def compute_assets_amount(self):

View File

@@ -167,10 +167,3 @@ def on_org_user_changed(action, instance, reverse, pk_set, **kwargs):
leaved_users = set(pk_set) - set(org.members.filter(id__in=user_pk_set).values_list('id', flat=True))
_clear_users_from_org(org, leaved_users)
@receiver(post_save, sender=User)
def on_user_create_refresh_cache(sender, instance, created, **kwargs):
if created:
default_org = Organization.default()
default_org.members.add(instance)

View File

@@ -11,7 +11,7 @@ from django.utils.translation import gettext_lazy as _
from django.db.models import QuerySet as DJQuerySet
from elasticsearch import Elasticsearch
from elasticsearch.helpers import bulk
from elasticsearch.exceptions import RequestError
from elasticsearch.exceptions import RequestError, NotFoundError
from common.utils.common import lazyproperty
from common.utils import get_logger
@@ -33,12 +33,43 @@ class CommandStore():
kwargs = config.get("OTHER", {})
self.index = config.get("INDEX") or 'jumpserver'
self.doc_type = config.get("DOC_TYPE") or 'command_store'
self.exact_fields = {}
self.match_fields = {}
ignore_verify_certs = kwargs.pop('IGNORE_VERIFY_CERTS', False)
if ignore_verify_certs:
kwargs['verify_certs'] = None
self.es = Elasticsearch(hosts=hosts, max_retries=0, **kwargs)
self.exact_fields = set()
self.match_fields = {'input', 'risk_level', 'user', 'asset', 'system_user'}
may_exact_fields = {'session', 'org_id'}
if self.is_new_index_type():
self.exact_fields.update(may_exact_fields)
self.doc_type = '_doc'
else:
self.match_fields.update(may_exact_fields)
def is_new_index_type(self):
if not self.ping(timeout=3):
return False
try:
# 获取索引信息,如果没有定义,直接返回
data = self.es.indices.get_mapping(self.index)
except NotFoundError:
return False
try:
# 检测索引是不是新的类型
properties = data[self.index]['mappings']['properties']
if properties['session']['type'] == 'keyword' \
and properties['org_id']['type'] == 'keyword':
return True
except KeyError:
return False
def pre_use_check(self):
if not self.ping(timeout=3):
raise InvalidElasticsearch
@@ -110,15 +141,14 @@ class CommandStore():
except Exception:
return False
@staticmethod
def get_query_body(**kwargs):
def get_query_body(self, **kwargs):
new_kwargs = {}
for k, v in kwargs.items():
new_kwargs[k] = str(v) if isinstance(v, UUID) else v
kwargs = new_kwargs
exact_fields = {}
match_fields = {'session', 'input', 'org_id', 'risk_level', 'user', 'asset', 'system_user'}
exact_fields = self.exact_fields
match_fields = self.match_fields
match = {}
exact = {}

View File

@@ -180,7 +180,7 @@ class CommandStorageTypeESSerializer(serializers.Serializer):
INDEX = serializers.CharField(
max_length=1024, default='jumpserver', label=_('Index'), allow_null=True
)
DOC_TYPE = ReadableHiddenField(default='command', label=_('Doc type'), allow_null=True)
DOC_TYPE = ReadableHiddenField(default='_doc', label=_('Doc type'), allow_null=True)
IGNORE_VERIFY_CERTS = serializers.BooleanField(
default=False, label=_('Ignore Certificate Verification'),
source='OTHER.IGNORE_VERIFY_CERTS', allow_null=True,

View File

@@ -599,7 +599,9 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
auto_now_add=True, blank=True, null=True,
verbose_name=_('Date password last updated')
)
need_update_password = models.BooleanField(default=False)
need_update_password = models.BooleanField(
default=False, verbose_name=_('Need update password')
)
wecom_id = models.CharField(null=True, default=None, unique=True, max_length=128)
dingtalk_id = models.CharField(null=True, default=None, unique=True, max_length=128)

View File

@@ -2,6 +2,7 @@
#
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from django.db.models import TextChoices
from rest_framework import serializers
from common.mixins import CommonBulkSerializerMixin
@@ -17,15 +18,13 @@ __all__ = [
class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
EMAIL_SET_PASSWORD = _('Reset link will be generated and sent to the user')
CUSTOM_PASSWORD = _('Set password')
PASSWORD_STRATEGY_CHOICES = (
(0, EMAIL_SET_PASSWORD),
(1, CUSTOM_PASSWORD)
)
class PasswordStrategy(TextChoices):
email = 'email', _('Reset link will be generated and sent to the user')
custom = 'custom', _('Set password')
password_strategy = serializers.ChoiceField(
choices=PASSWORD_STRATEGY_CHOICES, required=False,
label=_('Password strategy'), write_only=True, default=0
choices=PasswordStrategy.choices, default=PasswordStrategy.email, required=False,
write_only=True, label=_('Password strategy')
)
mfa_enabled = serializers.BooleanField(read_only=True, label=_('MFA enabled'))
mfa_force_enabled = serializers.BooleanField(read_only=True, label=_('MFA force enabled'))
@@ -117,9 +116,11 @@ class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
def validate_password(self, password):
from ..utils import check_password_rules
password_strategy = self.initial_data.get('password_strategy')
if password_strategy == '0':
if self.instance is None and password_strategy != self.PasswordStrategy.custom:
# 创建用户,使用邮件设置密码
return
if password_strategy is None and not password:
if self.instance and not password:
# 更新用户, 未设置密码
return
if not check_password_rules(password):
msg = _('Password does not match security rules')

View File

@@ -122,7 +122,7 @@ REDIS_PORT: 6379
# USER_LOGIN_SINGLE_MACHINE_ENABLED: False
#
# 启用定时任务
# PERIOD_TASK_ENABLE: True
# PERIOD_TASK_ENABLED: True
#
# 启用二次复合认证配置
# LOGIN_CONFIRM_ENABLE: False