Compare commits

...

16 Commits
v4.8 ... v3.1.2

Author SHA1 Message Date
fit2bot
2e967abe17 feat: Update v3.1.2 2023-04-10 18:20:35 +08:00
jiangweidong
e4f117e753 fix 2023-04-10 17:36:06 +08:00
jiangweidong
adfe4faf98 fix: 手机号可以为空及验证逻辑修改 2023-04-10 17:36:06 +08:00
Bai
d8bf45c760 fix: 修复手机号字段问题 2023-04-10 14:35:54 +08:00
jiangweidong
2245c13017 feat: 支持手机号选择区号 2023-04-10 10:30:17 +08:00
老广
06a24b1a04 Merge pull request #10120 from jumpserver/pr@v3.1@fix_ldapuserimport_v3.1
fix: 修复 LDAP 导入用户时指定其他组织,还会导入到 Default 组织的问题
2023-04-03 17:10:20 +08:00
Bai
339d872d37 fix: 修复 LDAP 导入用户时指定其他组织,还会导入到 Default 组织的问题 2023-04-03 16:52:52 +08:00
老广
012da07999 Merge pull request #10024 from jumpserver/pr@v3.1@asset_accounts_secret_type_default
perf: 设置资产账号的默认值,方便导入
2023-03-22 14:31:32 +08:00
ibuler
d6c22dd485 perf: 设置资产账号的默认值,方便导入 2023-03-21 04:44:51 +00:00
老广
d1e5559a7b Merge pull request #10004 from jumpserver/pr@v3.1@perf_import_export
perf: 优化导入导出
2023-03-20 09:58:39 +08:00
ibuler
5fddd0b02b perf: fix warning 2023-03-20 09:53:59 +08:00
ibuler
11232871a2 pref: 修改 import export 2023-03-17 18:53:21 +08:00
ibuler
3f6c3c3dbb pref: 去掉 i18n 变更 2023-03-17 18:51:58 +08:00
ibuler
675516f6da perf: 优化导入导出 2023-03-17 10:45:45 +00:00
fit2bot
234554060a perf: 批量推送账号 分批处理 (#10001)
Co-authored-by: feng <1304903146@qq.com>
2023-03-17 17:10:23 +08:00
ibuler
afc7232bd9 perf: 优化 Ansible 账号选择 2023-03-16 19:09:08 +08:00
12 changed files with 62 additions and 37 deletions

1
GITSHA Normal file
View File

@@ -0,0 +1 @@
e4f117e7536ae14ca750e290e2ed1e7ac6fa1663

View File

@@ -81,7 +81,7 @@ class AccountAssetSerializer(serializers.ModelSerializer):
def to_internal_value(self, data):
if isinstance(data, dict):
i = data.get('id')
i = data.get('id') or data.get('pk')
else:
i = data

View File

@@ -71,7 +71,8 @@ class AssetAccountSerializer(
template = serializers.BooleanField(
default=False, label=_("Template"), write_only=True
)
name = serializers.CharField(max_length=128, required=True, label=_("Name"))
name = serializers.CharField(max_length=128, required=False, label=_("Name"))
secret_type = serializers.CharField(max_length=64, default='password', label=_("Secret type"))
class Meta:
model = Account

View File

@@ -111,7 +111,7 @@ class BaseFileParser(BaseParser):
return {'pk': obj_id, 'name': obj_name}
def parse_value(self, field, value):
if value is '-':
if value == '-' and field and field.allow_null:
return None
elif hasattr(field, 'to_file_internal_value'):
value = field.to_file_internal_value(value)

View File

@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
#
import phonenumbers
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
@@ -17,6 +18,7 @@ __all__ = [
"BitChoicesField",
"TreeChoicesField",
"LabeledMultipleChoiceField",
"PhoneField",
]
@@ -201,3 +203,11 @@ class BitChoicesField(TreeChoicesField):
value = self.to_internal_value(data)
self.run_validators(value)
return value
class PhoneField(serializers.CharField):
def to_representation(self, value):
if value:
phone = phonenumbers.parse(value, 'CN')
value = {'code': '+%s' % phone.country_code, 'phone': phone.national_number}
return value

View File

@@ -2,12 +2,15 @@
#
import re
import phonenumbers
from django.core.validators import RegexValidator
from django.utils.translation import ugettext_lazy as _
from rest_framework.validators import (
UniqueTogetherValidator, ValidationError
)
from rest_framework import serializers
from phonenumbers.phonenumberutil import NumberParseException
from common.utils.strings import no_special_chars
@@ -42,9 +45,14 @@ class NoSpecialChars:
class PhoneValidator:
pattern = re.compile(r"^1[3456789]\d{9}$")
message = _('The mobile phone number format is incorrect')
def __call__(self, value):
if not self.pattern.match(value):
try:
phone = phonenumbers.parse(value, 'CN')
valid = phonenumbers.is_valid_number(phone)
except NumberParseException:
valid = False
if not valid:
raise serializers.ValidationError(self.message)

View File

@@ -139,12 +139,12 @@ class JMSInventory:
self.make_ssh_account_vars(host, asset, account, automation, protocols, platform, gateway)
return host
def get_asset_accounts(self, asset):
from assets.const import Connectivity
accounts = asset.accounts.filter(is_active=True).order_by('-privileged', '-date_updated')
accounts_connectivity_ok = list(accounts.filter(connectivity=Connectivity.OK))
accounts_connectivity_no = list(accounts.exclude(connectivity=Connectivity.OK))
return accounts_connectivity_ok + accounts_connectivity_no
def get_asset_sorted_accounts(self, asset):
accounts = list(asset.accounts.filter(is_active=True))
connectivity_score = {'ok': 2, '-': 1, 'err': 0}
sort_key = lambda x: (x.privileged, connectivity_score.get(x.connectivity, 0), x.date_updated)
accounts_sorted = sorted(accounts, key=sort_key, reverse=True)
return accounts_sorted
@staticmethod
def get_account_prefer(account_prefer):
@@ -163,28 +163,23 @@ class JMSInventory:
return account
def select_account(self, asset):
accounts = self.get_asset_accounts(asset)
if not accounts or self.account_policy == 'skip':
accounts = self.get_asset_sorted_accounts(asset)
if not accounts:
return None
account_selected = None
# 首先找到特权账号
privileged_accounts = list(filter(lambda account: account.privileged, accounts))
refer_account = self.get_refer_account(accounts)
if refer_account:
return refer_account
# 不同类型的账号选择,优先使用提供的名称
refer_privileged_account = self.get_refer_account(privileged_accounts)
if self.account_policy in ['privileged_only', 'privileged_first']:
first_privileged = privileged_accounts[0] if privileged_accounts else None
account_selected = refer_privileged_account or first_privileged
# 此策略不管是否匹配到账号都需强制返回
if self.account_policy == 'privileged_only':
account_selected = accounts[0]
if self.account_policy == 'skip':
return None
elif self.account_policy == 'privileged_first':
return account_selected
if not account_selected:
account_selected = self.get_refer_account(accounts)
return account_selected or accounts[0]
elif self.account_policy == 'privileged_only' and account_selected.privileged:
return account_selected
else:
return None
def generate(self, path_dir):
hosts = []

View File

@@ -46,7 +46,7 @@ class JMSPermedInventory(JMSInventory):
self.user = user
self.assets_accounts_mapper = self.get_assets_accounts_mapper()
def get_asset_accounts(self, asset):
def get_asset_sorted_accounts(self, asset):
return self.assets_accounts_mapper.get(asset.id, [])
def get_assets_accounts_mapper(self):

View File

@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#
from django.db.models import Q, QuerySet
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
@@ -119,7 +118,10 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer):
return
assets = self.get_all_assets(nodes, assets)
accounts = self.create_accounts(assets)
push_accounts_to_assets_task.delay([str(account.id) for account in accounts])
account_ids = [str(account.id) for account in accounts]
slice_count = 20
for i in range(0, len(account_ids), slice_count):
push_accounts_to_assets_task.delay(account_ids[i:i + slice_count])
def validate_accounts(self, usernames: list[str]):
template_ids = []

View File

@@ -1,9 +1,9 @@
# coding: utf-8
#
#
from celery import shared_task
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.db import transaction
from common.utils import get_logger
from ops.celery.decorator import after_app_ready_start
from ops.celery.utils import (
@@ -22,6 +22,7 @@ def sync_ldap_user():
@shared_task(verbose_name=_('Import ldap user'))
@transaction.atomic
def import_ldap_user():
logger.info("Start import ldap user task")
util_server = LDAPServerUtil()

View File

@@ -1,12 +1,16 @@
# -*- coding: utf-8 -*-
#
import phonenumbers
from functools import partial
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.serializers import CommonBulkSerializerMixin
from common.serializers.fields import EncryptedField, ObjectRelatedField, LabeledChoiceField
from common.serializers.fields import (
EncryptedField, ObjectRelatedField, LabeledChoiceField, PhoneField
)
from common.utils import pretty_string, get_logger
from common.validators import PhoneValidator
from rbac.builtin import BuiltinRole
@@ -101,6 +105,9 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer
label=_("Password"), required=False, allow_blank=True,
allow_null=True, max_length=1024,
)
phone = PhoneField(
validators=[PhoneValidator()], required=False, allow_blank=True, allow_null=True, label=_("Phone")
)
custom_m2m_fields = {
"system_roles": [BuiltinRole.system_user],
"org_roles": [BuiltinRole.org_user],
@@ -167,7 +174,6 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer
"created_by": {"read_only": True, "allow_blank": True},
"role": {"default": "User"},
"is_otp_secret_key_bound": {"label": _("Is OTP bound")},
"phone": {"validators": [PhoneValidator()]},
'mfa_level': {'label': _("MFA level")},
}

View File

@@ -29,6 +29,7 @@ pycparser==2.21
cryptography==38.0.4
pycryptodome==3.15.0
pycryptodomex==3.15.0
phonenumbers==8.13.8
gmssl==3.2.1
itsdangerous==1.1.0
pyotp==2.6.0