Skip to content

Commit

Permalink
feat(bkuser): adjust local data source config model (#1652)
Browse files Browse the repository at this point in the history
  • Loading branch information
nannan00 authored Apr 17, 2024
1 parent da15421 commit 191ef06
Show file tree
Hide file tree
Showing 26 changed files with 109 additions and 90 deletions.
2 changes: 1 addition & 1 deletion src/bk-user/bkuser/apis/web/data_source/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ def validate(self, attrs):
raise ValidationError(_("指定数据源不存在"))

plugin_config = data_source.get_plugin_cfg()
if not plugin_config.enable_account_password_login:
if not plugin_config.enable_password:
raise ValidationError(_("无法使用该数据源生成随机密码"))

attrs["password_rule"] = plugin_config.password_rule.to_rule()
Expand Down
6 changes: 3 additions & 3 deletions src/bk-user/bkuser/apis/web/data_source_organization/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,9 @@ def put(self, request, *args, **kwargs):
data_source = user.data_source
plugin_config = data_source.get_plugin_cfg()

if not (data_source.is_local and plugin_config.enable_account_password_login):
if not (data_source.is_local and plugin_config.enable_password):
raise error_codes.DATA_SOURCE_OPERATION_UNSUPPORTED.f(
_("仅可以重置 已经启用账密登录功能 的 本地数据源 的用户密码")
_("仅可以重置 已经启用密码功能 的 本地数据源 的用户密码")
)

slz = DataSourceUserPasswordResetInputSLZ(
Expand All @@ -292,7 +292,7 @@ def put(self, request, *args, **kwargs):
DataSourceUserHandler.update_password(
data_source_user=user,
password=raw_password,
valid_days=plugin_config.password_rule.valid_time,
valid_days=plugin_config.password_expire.valid_time,
operator=request.user.username,
)

Expand Down
2 changes: 1 addition & 1 deletion src/bk-user/bkuser/apis/web/password/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def post(self, request, *args, **kwargs):
DataSourceUserHandler.update_password(
data_source_user=data_source_user,
password=password,
valid_days=plugin_cfg.password_rule.valid_time,
valid_days=plugin_cfg.password_expire.valid_time,
operator="AnonymousByResetToken",
)
# 成功修改完用户密码后,需要及时禁用 Token
Expand Down
8 changes: 4 additions & 4 deletions src/bk-user/bkuser/apis/web/personal_center/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ def get(self, request, *args, **kwargs):

feature_flags = {
PersonalCenterFeatureFlag.CAN_CHANGE_PASSWORD: bool(
data_source.is_local and data_source.plugin_config.get("enable_account_password_login", False)
data_source.is_local and data_source.plugin_config.get("enable_password", False)
)
}
return Response(TenantUserFeatureFlagOutputSLZ(feature_flags).data)
Expand All @@ -279,9 +279,9 @@ def put(self, request, *args, **kwargs):
data_source = data_source_user.data_source
plugin_config = data_source.get_plugin_cfg()

if not (data_source.is_local and plugin_config.enable_account_password_login):
if not (data_source.is_local and plugin_config.enable_password):
raise error_codes.DATA_SOURCE_OPERATION_UNSUPPORTED.f(
_("仅可以重置 已经启用账密登录功能 的 本地数据源 的用户密码")
_("仅可以重置 已经启用密码功能 的 本地数据源 的用户密码")
)

slz = TenantUserPasswordUpdateInputSLZ(
Expand All @@ -298,7 +298,7 @@ def put(self, request, *args, **kwargs):
DataSourceUserHandler.update_password(
data_source_user=data_source_user,
password=new_password,
valid_days=plugin_config.password_rule.valid_time,
valid_days=plugin_config.password_expire.valid_time,
operator=request.user.username,
)

Expand Down
4 changes: 2 additions & 2 deletions src/bk-user/bkuser/apis/web/platform_management/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ def put(self, request, *args, **kwargs):
plugin_config = data_source.get_plugin_cfg()
assert isinstance(plugin_config, LocalDataSourcePluginConfig)
assert plugin_config.password_initial is not None
assert plugin_config.password_rule is not None
assert plugin_config.password_expire is not None

# 输入参数校验
slz = TenantBuiltinManagerUpdateInputSLZ(
Expand All @@ -329,7 +329,7 @@ def put(self, request, *args, **kwargs):
DataSourceUserHandler.update_password(
data_source_user=user,
password=fixed_password,
valid_days=plugin_config.password_rule.valid_time,
valid_days=plugin_config.password_expire.valid_time,
operator=request.user.username,
)

Expand Down
4 changes: 2 additions & 2 deletions src/bk-user/bkuser/apis/web/tenant_info/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ def validate_name(self, name: str) -> str:

class TenantBuiltinManagerRetrieveOutputSLZ(serializers.Serializer):
username = serializers.CharField(help_text="用户名")
enable_account_password_login = serializers.BooleanField(help_text="是否启用账密登录")
enable_login = serializers.BooleanField(help_text="是否启用账密登录")


class TenantBuiltinManagerUpdateInputSLZ(serializers.Serializer):
username = serializers.CharField(
help_text="用户名", validators=[validate_data_source_user_username], required=False, allow_blank=True
)
enable_account_password_login = serializers.BooleanField(help_text="是否启用账密登录", required=False)
enable_login = serializers.BooleanField(help_text="是否启用账密登录", required=False)


class TenantBuiltinManagerPasswordUpdateInputSLZ(serializers.Serializer):
Expand Down
20 changes: 14 additions & 6 deletions src/bk-user/bkuser/apis/web/tenant_info/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""
from django.db import transaction
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from drf_yasg.utils import swagger_auto_schema
from rest_framework import generics, status
from rest_framework.permissions import IsAuthenticated
Expand All @@ -22,6 +23,7 @@
from bkuser.apps.permission.permissions import perm_class
from bkuser.apps.tenant.models import Tenant, TenantManager, TenantUser
from bkuser.biz.data_source_organization import DataSourceUserHandler
from bkuser.common.error_codes import error_codes
from bkuser.common.views import ExcludePatchAPIViewMixin, ExcludePutAPIViewMixin
from bkuser.plugins.local.models import LocalDataSourcePluginConfig

Expand Down Expand Up @@ -91,7 +93,7 @@ def get(self, request, *args, **kwargs):
TenantBuiltinManagerRetrieveOutputSLZ(
{
"username": user.username,
"enable_account_password_login": data_source.plugin_config["enable_account_password_login"],
"enable_login": data_source.plugin_config["enable_password"],
}
).data
)
Expand All @@ -107,6 +109,12 @@ def patch(self, request, *args, **kwargs):
slz.is_valid(raise_exception=True)
data = slz.validated_data

# 若当前内置的租户管理员是唯一的管理员,则无法禁用
if not TenantManager.objects.filter(
tenant_id=self.get_current_tenant_id(), tenant_user__data_source__type=DataSourceTypeEnum.REAL
).exists():
raise error_codes.VALIDATION_ERROR.f(_("不存在实名管理员,无法禁用内置管理员账号登录"))

# 内建数据源 & 用户
data_source, user = self.get_builtin_data_source_and_user()

Expand All @@ -125,9 +133,9 @@ def patch(self, request, *args, **kwargs):
LocalDataSourceIdentityInfo.objects.filter(user=user).update(username=new_username)

# 更新是否启用登录
enable = data.get("enable_account_password_login")
if enable is not None and plugin_config.enable_account_password_login != enable:
plugin_config.enable_account_password_login = enable
enable = data.get("enable_login")
if enable is not None and plugin_config.enable_password != enable:
plugin_config.enable_password = enable
data_source.set_plugin_cfg(plugin_config)

return Response(status=status.HTTP_204_NO_CONTENT)
Expand All @@ -151,7 +159,7 @@ def put(self, request, *args, **kwargs):
# 数据源配置
plugin_config = data_source.get_plugin_cfg()
assert isinstance(plugin_config, LocalDataSourcePluginConfig)
assert plugin_config.password_rule is not None
assert plugin_config.password_expire is not None

# 数据校验
slz = TenantBuiltinManagerPasswordUpdateInputSLZ(
Expand All @@ -164,7 +172,7 @@ def put(self, request, *args, **kwargs):
DataSourceUserHandler.update_password(
data_source_user=user,
password=raw_password,
valid_days=plugin_config.password_rule.valid_time,
valid_days=plugin_config.password_expire.valid_time,
operator=request.user.username,
)

Expand Down
2 changes: 1 addition & 1 deletion src/bk-user/bkuser/apis/web/virtual_user/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def get_current_virtual_data_source(self):
type=DataSourceTypeEnum.VIRTUAL,
defaults={
"plugin": DataSourcePlugin.objects.get(id=DataSourcePluginEnum.LOCAL),
"plugin_config": LocalDataSourcePluginConfig(enable_account_password_login=False),
"plugin_config": LocalDataSourcePluginConfig(enable_password=False),
},
)

Expand Down
8 changes: 4 additions & 4 deletions src/bk-user/bkuser/apps/data_source/initializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def __init__(self, data_source: DataSource):
self.plugin_cfg = data_source.get_plugin_cfg()
assert isinstance(self.plugin_cfg, LocalDataSourcePluginConfig)

if not self.plugin_cfg.enable_account_password_login:
if not self.plugin_cfg.enable_password:
return

self.password_provider = PasswordProvider(
Expand Down Expand Up @@ -90,8 +90,8 @@ def _can_skip(self) -> bool:
if not self.data_source.is_local:
return True

# 是本地数据源,但是没开启账密登录的,不需要初始化
if not self.plugin_cfg.enable_account_password_login: # type: ignore
# 是本地数据源,但是没开启密码功能的,不需要初始化
if not self.plugin_cfg.enable_password: # type: ignore
return True

return False
Expand Down Expand Up @@ -121,7 +121,7 @@ def _init_users_identity_info(self, users: List[DataSourceUser]) -> Dict[int, st

def _get_password_expired_at(self) -> datetime.datetime:
"""获取密码过期的具体时间"""
valid_time: int = self.plugin_cfg.password_rule.valid_time # type: ignore
valid_time: int = self.plugin_cfg.password_expire.valid_time # type: ignore
# 有效时间 -1 表示永远有效
if valid_time < 0:
return PERMANENT_TIME
Expand Down
6 changes: 3 additions & 3 deletions src/bk-user/bkuser/apps/idp/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def _update_local_idp_of_tenant(data_source: DataSource):
"""
更新租户的本地账密登录认证源
对于每个租户,如果有本地数据源,则必须有本地账密认证源
该函数主要是根据本地数据源(status和enable_account_password_login)的变化更新租户的本地账密认证源配置和状态
该函数主要是根据本地数据源(status 和 enable_password )的变化更新租户的本地账密认证源配置和状态
"""
# 非本地数据源不需要默认认证源
if not data_source.is_local:
Expand All @@ -45,11 +45,11 @@ def _update_local_idp_of_tenant(data_source: DataSource):
owner_tenant_id=data_source.owner_tenant_id, plugin=plugin, defaults={"name": _("本地账密")}
)

# 判断数据源 status和enable_account_password_login ,确定是否使用账密登录
# 判断数据源 status 和 enable_password ,确定是否使用账密登录
plugin_cfg = data_source.get_plugin_cfg()
assert isinstance(plugin_cfg, LocalDataSourcePluginConfig)

enable_login = plugin_cfg.enable_account_password_login
enable_login = plugin_cfg.enable_password

# 根据数据源是否使用账密登录,修改认证源配置
idp_plugin_cfg: LocalIdpPluginConfig = idp.get_plugin_cfg()
Expand Down
4 changes: 2 additions & 2 deletions src/bk-user/bkuser/apps/notification/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def build_and_run_notify_password_expiring_users_task():
logger.info("data source's owner tenant %s not enabled, skip notify...", data_source.id)
continue

if data_source.plugin_config.get("enable_account_password_login", False):
if data_source.plugin_config.get("enable_password", False):
notify_password_expiring_users.delay(data_source.id)


Expand Down Expand Up @@ -122,7 +122,7 @@ def build_and_run_notify_password_expired_users_task():
logger.info("data source's owner tenant %s not enabled, skip notify...", data_source.id)
continue

if data_source.plugin_config.get("enable_account_password_login", False):
if data_source.plugin_config.get("enable_password", False):
notify_password_expired_users.delay(data_source.id)


Expand Down
2 changes: 1 addition & 1 deletion src/bk-user/bkuser/apps/sync/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def sync_identity_infos_and_notify_after_sync_tenant(sender, data_source: DataSo
@receiver(post_save, sender=DataSource)
def sync_identity_infos_and_notify_after_modify_data_source(sender, instance: DataSource, created: bool, **kwargs):
"""
数据源更新后,需要检查是否是本地数据源,若是本地数据源且启用账密登录
数据源更新后,需要检查是否是本地数据源,若是本地数据源且启用密码功能
则需要对没有账密信息的用户,进行密码的初始化 & 发送通知,批量创建数据源用户同理
"""
if created:
Expand Down
13 changes: 8 additions & 5 deletions src/bk-user/bkuser/plugins/local/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
)
from bkuser.plugins.local.models import (
LocalDataSourcePluginConfig,
LoginLimitConfig,
NotificationConfig,
NotificationTemplate,
PasswordExpireConfig,
Expand All @@ -24,7 +25,7 @@

# 本地数据源插件默认配置
DEFAULT_PLUGIN_CONFIG = LocalDataSourcePluginConfig(
enable_account_password_login=True,
enable_password=False,
password_rule=PasswordRuleConfig(
min_length=12,
contain_lowercase=True,
Expand All @@ -36,12 +37,8 @@
not_continuous_letter=False,
not_continuous_digit=False,
not_repeated_symbol=False,
valid_time=90,
max_retries=3,
lock_time=60 * 60,
),
password_initial=PasswordInitialConfig(
force_change_at_first_login=True,
cannot_use_previous_password=True,
reserved_previous_password_count=3,
generate_method=PasswordGenerateMethod.RANDOM,
Expand Down Expand Up @@ -129,6 +126,7 @@
),
),
password_expire=PasswordExpireConfig(
valid_time=90,
remind_before_expire=[1, 7, 15],
notification=NotificationConfig(
enabled_methods=[NotificationMethod.EMAIL],
Expand Down Expand Up @@ -200,4 +198,9 @@
],
),
),
login_limit=LoginLimitConfig(
force_change_at_first_login=True,
max_retries=3,
lock_time=60 * 60,
),
)
36 changes: 21 additions & 15 deletions src/bk-user/bkuser/plugins/local/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,6 @@ class PasswordRuleConfig(BaseModel):
# 不允许重复字母,数字,特殊字符
not_repeated_symbol: bool

# 密码有效期(单位:天)
valid_time: int = Field(ge=NEVER_EXPIRE_TIME, le=MAX_PASSWORD_VALID_TIME)
# 密码试错次数
max_retries: int = Field(ge=0, le=PASSWORD_MAX_RETRIES)
# 锁定时间(单位:秒)
lock_time: int = Field(ge=NEVER_EXPIRE_TIME, le=MAX_LOCK_TIME)

def to_rule(self) -> PasswordRule:
"""转换成密码工具可用的规则"""
return PasswordRule(
Expand Down Expand Up @@ -120,8 +113,6 @@ class NotificationConfig(BaseModel):
class PasswordInitialConfig(BaseModel):
"""初始密码设置"""

# 首次登录后强制修改密码
force_change_at_first_login: bool
# 修改密码时候不能使用之前的密码
cannot_use_previous_password: bool
# 之前的 N 个密码不能被本次修改使用,仅当 cannot_use_previous_password 为 True 时有效
Expand All @@ -135,14 +126,27 @@ class PasswordInitialConfig(BaseModel):


class PasswordExpireConfig(BaseModel):
"""密码到期相关配置"""
"""密码有效期相关配置"""

# 密码有效期(单位:天)
valid_time: int = Field(ge=NEVER_EXPIRE_TIME, le=MAX_PASSWORD_VALID_TIME)
# 在密码到期多久前提醒,单位:天,多个值表示多次提醒
remind_before_expire: List[int]
# 通知相关配置
notification: NotificationConfig


class LoginLimitConfig(BaseModel):
"""登录限制配置"""

# 首次登录后强制修改密码
force_change_at_first_login: bool
# 密码试错次数
max_retries: int = Field(ge=0, le=PASSWORD_MAX_RETRIES)
# 锁定时间(单位:秒)
lock_time: int = Field(ge=NEVER_EXPIRE_TIME, le=MAX_LOCK_TIME)


class LocalDataSourcePluginConfig(BasePluginConfig):
"""本地数据源插件配置"""

Expand All @@ -151,23 +155,25 @@ class LocalDataSourcePluginConfig(BasePluginConfig):
"password_initial.fixed_password",
]

# 是否允许使用账密登录
enable_account_password_login: bool
# 是否启用密码,即用户是否添加密码数据
enable_password: bool
# 密码生成规则
password_rule: Optional[PasswordRuleConfig] = None
# 密码初始化/修改规则
password_initial: Optional[PasswordInitialConfig] = None
# 密码到期规则
password_expire: Optional[PasswordExpireConfig] = None
# 登录限制
login_limit: Optional[LoginLimitConfig] = None

@model_validator(mode="after")
def validate_attrs(self) -> "LocalDataSourcePluginConfig":
"""插件配置合法性检查"""
# 如果没有开启账密登录,则不需要检查配置
if not self.enable_account_password_login:
# 如果没有开启密码,则不需要检查配置
if not self.enable_password:
return self

# 若启用账密登录,则各字段都需要配置上
# 若启用密码功能,则各字段都需要配置上
if not (self.password_rule and self.password_initial and self.password_expire):
raise ValueError(_("密码生成规则、初始密码设置、密码到期设置均不能为空"))

Expand Down
Loading

0 comments on commit 191ef06

Please sign in to comment.