Skip to content

Commit

Permalink
feat: add data source plugin default config api (#1225)
Browse files Browse the repository at this point in the history
  • Loading branch information
narasux authored Sep 7, 2023
1 parent 21ebb3a commit 75c8ac7
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 6 deletions.
4 changes: 4 additions & 0 deletions src/bk-user/bkuser/apis/web/data_source/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ class DataSourcePluginOutputSLZ(serializers.Serializer):
logo = serializers.CharField(help_text="数据源插件 Logo")


class DataSourcePluginDefaultConfigOutputSLZ(serializers.Serializer):
config = serializers.JSONField(help_text="数据源插件默认配置")


class DataSourceRetrieveOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="数据源 ID")
name = serializers.CharField(help_text="数据源名称")
Expand Down
6 changes: 6 additions & 0 deletions src/bk-user/bkuser/apis/web/data_source/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
urlpatterns = [
# 数据源插件列表
path("plugins/", views.DataSourcePluginListApi.as_view(), name="data_source_plugin.list"),
# 数据源插件默认配置
path(
"plugins/<str:id>/default-config/",
views.DataSourcePluginDefaultConfigApi.as_view(),
name="data_source_plugin.default_config",
),
# 数据源创建/获取列表
path("", views.DataSourceListCreateApi.as_view(), name="data_source.list_create"),
# 数据源更新/获取
Expand Down
21 changes: 20 additions & 1 deletion src/bk-user/bkuser/apis/web/data_source/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from bkuser.apis.web.data_source.serializers import (
DataSourceCreateInputSLZ,
DataSourceCreateOutputSLZ,
DataSourcePluginDefaultConfigOutputSLZ,
DataSourcePluginOutputSLZ,
DataSourceRetrieveOutputSLZ,
DataSourceSearchInputSLZ,
Expand All @@ -29,6 +30,7 @@
from bkuser.apps.data_source.models import DataSource, DataSourcePlugin
from bkuser.apps.data_source.plugins.constants import DATA_SOURCE_PLUGIN_CONFIG_SCHEMA_MAP
from bkuser.apps.data_source.signals import post_create_data_source, post_update_data_source
from bkuser.biz.data_source_plugin import DefaultPluginConfigProvider
from bkuser.common.error_codes import error_codes
from bkuser.common.views import ExcludePatchAPIViewMixin, ExcludePutAPIViewMixin

Expand All @@ -39,14 +41,31 @@ class DataSourcePluginListApi(generics.ListAPIView):
serializer_class = DataSourcePluginOutputSLZ

@swagger_auto_schema(
tags=["data_source"],
tags=["data_source_plugin"],
operation_description="数据源插件列表",
responses={status.HTTP_200_OK: DataSourcePluginOutputSLZ(many=True)},
)
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)


class DataSourcePluginDefaultConfigApi(generics.RetrieveAPIView):
@swagger_auto_schema(
tags=["data_source_plugin"],
operation_description="数据源插件默认配置",
responses={
status.HTTP_200_OK: DataSourcePluginDefaultConfigOutputSLZ(),
**DATA_SOURCE_PLUGIN_CONFIG_SCHEMA_MAP,
},
)
def get(self, request, *args, **kwargs):
config = DefaultPluginConfigProvider().get(kwargs["id"])
if not config:
raise error_codes.DATA_SOURCE_PLUGIN_NOT_DEFAULT_CONFIG

return Response(DataSourcePluginDefaultConfigOutputSLZ(instance={"config": config.model_dump()}).data)


class DataSourceListCreateApi(CurrentUserTenantMixin, generics.ListCreateAPIView):
pagination_class = None
serializer_class = DataSourceSearchOutputSLZ
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
ONE_DAY_SECONDS = 24 * 60 * 60

# 密码可选最长有效期:10年
MAX_PASSWORD_VALID_TIME = 10 * 365 * ONE_DAY_SECONDS
MAX_PASSWORD_VALID_TIME = 10 * 365

# 可选最长锁定时间:10年
MAX_LOCK_TIME = 10 * 365 * ONE_DAY_SECONDS
Expand Down
4 changes: 2 additions & 2 deletions src/bk-user/bkuser/apps/data_source/plugins/local/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ 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)
Expand Down Expand Up @@ -140,7 +140,7 @@ class PasswordInitialConfig(BaseModel):
class PasswordExpireConfig(BaseModel):
"""密码到期相关配置"""

# 在密码到期多久前提醒,单位:,多个值表示多次提醒
# 在密码到期多久前提醒,单位:,多个值表示多次提醒
remind_before_expire: List[int]
# 通知相关配置
notification: NotificationConfig
Expand Down
200 changes: 200 additions & 0 deletions src/bk-user/bkuser/biz/data_source_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from typing import Optional

from pydantic import BaseModel

from bkuser.apps.data_source.constants import DataSourcePluginEnum
from bkuser.apps.data_source.plugins.local.constants import (
NotificationMethod,
NotificationScene,
PasswordGenerateMethod,
)
from bkuser.apps.data_source.plugins.local.models import (
LocalDataSourcePluginConfig,
NotificationConfig,
NotificationTemplate,
PasswordExpireConfig,
PasswordInitialConfig,
PasswordRuleConfig,
)


class DefaultPluginConfigProvider:
"""默认插件配置提供者"""

def get(self, plugin_id: str) -> Optional[BaseModel]:
"""获取指定插件类型的默认插件配置"""
if plugin_id == DataSourcePluginEnum.LOCAL:
return self._get_default_local_plugin_config()

return None

def _get_default_local_plugin_config(self) -> BaseModel:
return LocalDataSourcePluginConfig(
enable_account_password_login=True,
password_rule=PasswordRuleConfig(
min_length=12,
contain_lowercase=True,
contain_uppercase=True,
contain_digit=True,
contain_punctuation=True,
not_continuous_count=0,
not_keyboard_order=False,
not_continuous_letter=False,
not_continuous_digit=False,
not_repeated_symbol=False,
valid_time=30,
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,
fixed_password=None,
notification=NotificationConfig(
enabled_methods=[NotificationMethod.EMAIL],
templates=[
NotificationTemplate(
method=NotificationMethod.EMAIL,
scene=NotificationScene.USER_INITIALIZE,
title="蓝鲸智云 - 您的账户已经成功创建!",
sender="蓝鲸智云",
content=(
"您好:\n"
+ "您的蓝鲸智云帐户已经成功创建,以下是您的帐户信息\n"
+ " 登录帐户:{username},初始登录密码:{password}\n"
+ "为了保障帐户安全,建议您尽快登录平台修改密码:{url}\n"
+ "此邮件为系统自动发送,请勿回复。"
),
content_html=(
"<p>您好:</p>"
+ "<p>您的蓝鲸智云帐户已经成功创建,以下是您的帐户信息</p>"
+ "<p>登录帐户:{username},初始登录密码:{password}</p>"
+ "<p>为了保障帐户安全,建议您尽快登录平台修改密码:{url}</p>"
+ "<p>此邮件为系统自动发送,请勿回复。</p>"
),
),
NotificationTemplate(
method=NotificationMethod.EMAIL,
scene=NotificationScene.RESET_PASSWORD,
title="蓝鲸智云 - 登录密码重置",
sender="蓝鲸智云",
content=(
"您好:\n"
+ "我们收到了您重置密码的申请,请点击下方链接进行密码重置:{url}\n"
+ "该链接有效时间为 3 小时,过期后请重新点击密码重置链接:{reset_url}\n"
+ "此邮件为系统自动发送,请勿回复。"
),
content_html=(
"<p>您好:</p>"
+ "<p>我们收到了您重置密码的申请,请点击下方链接进行密码重置:{url} </p>"
+ "<p>该链接有效时间为 3 小时,过期后请重新点击密码重置链接:{reset_url}</p>"
+ "<p>此邮件为系统自动发送,请勿回复。</p>"
),
),
NotificationTemplate(
method=NotificationMethod.SMS,
scene=NotificationScene.USER_INITIALIZE,
title=None,
sender="蓝鲸智云",
content=(
"您好:\n"
+ "您的蓝鲸智云帐户已经成功创建,以下是您的帐户信息\n"
+ " 登录帐户:{username},初始登录密码:{password}\n"
+ "为了保障帐户安全,建议您尽快登录平台修改密码:{url}\n"
+ "此邮件为系统自动发送,请勿回复。"
),
content_html=None,
),
NotificationTemplate(
method=NotificationMethod.SMS,
scene=NotificationScene.RESET_PASSWORD,
title=None,
sender="蓝鲸智云",
content=(
"您好:\n"
+ "我们收到了您重置密码的申请,请点击下方链接进行密码重置:{url}\n"
+ "该链接有效时间为 3 小时,过期后请重新点击密码重置链接:{reset_url}\n"
+ "此邮件为系统自动发送,请勿回复。"
),
content_html=None,
),
],
),
),
password_expire=PasswordExpireConfig(
remind_before_expire=[1, 7, 15],
notification=NotificationConfig(
enabled_methods=[NotificationMethod.EMAIL],
templates=[
NotificationTemplate(
method=NotificationMethod.EMAIL,
scene=NotificationScene.PASSWORD_EXPIRING,
title="蓝鲸智云 - 密码即将到期提醒",
sender="蓝鲸智云",
content=(
"{username},您好:\n"
+ "您的蓝鲸智云平台密码将于 {expired_at} 天后过期,为避免影响使用,请尽快登陆平台修改密码。\n" # noqa: E501
+ "此邮件为系统自动发送,请勿回复。"
),
content_html=(
"<p>{username},您好:</p>"
+ "<p>您的蓝鲸智云平台密码将于 {expired_at} 天后过期,为避免影响使用,请尽快登陆平台修改密码。</p>" # noqa: E501
+ "<p>此邮件为系统自动发送,请勿回复。</p>"
),
),
NotificationTemplate(
method=NotificationMethod.EMAIL,
scene=NotificationScene.PASSWORD_EXPIRED,
title="蓝鲸智云 - 密码已过期提醒",
sender="蓝鲸智云",
content=(
"{username},您好:\n"
+ "您的蓝鲸智云平台密码已过期,为避免影响正常使用,请尽快登陆平台修改密码。\n" # noqa: E501
+ "此邮件为系统自动发送,请勿回复。"
),
content_html=(
"<p>{username},您好:</p>"
+ "<p>您的蓝鲸智云平台密码已过期,为避免影响正常使用,请尽快登陆平台修改密码。</p>"
+ "<p>此邮件为系统自动发送,请勿回复。</p>"
),
),
NotificationTemplate(
method=NotificationMethod.SMS,
scene=NotificationScene.PASSWORD_EXPIRING,
title=None,
sender="蓝鲸智云",
content=(
"{username},您好:\n"
+ "您的蓝鲸智云平台密码将于 {expired_at} 天后过期,为避免影响使用,请尽快登陆平台修改密码。\n" # noqa: E501
+ "此邮件为系统自动发送,请勿回复。"
),
content_html=None,
),
NotificationTemplate(
method=NotificationMethod.SMS,
scene=NotificationScene.PASSWORD_EXPIRED,
title=None,
sender="蓝鲸智云",
content=(
"{username},您好:\n"
+ "您的蓝鲸智云平台密码已过期,为避免影响使用,请尽快登陆平台修改密码。\n" # noqa: E501
+ "此邮件为系统自动发送,请勿回复。"
),
content_html=None,
),
],
),
),
)
4 changes: 4 additions & 0 deletions src/bk-user/bkuser/common/error_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ class ErrorCodes:

# 调用外部系统API
REMOTE_REQUEST_ERROR = ErrorCode(_("调用外部系统API异常"))

# 数据源插件
DATA_SOURCE_PLUGIN_NOT_DEFAULT_CONFIG = ErrorCode(_("当前数据源插件未提供默认配置"))

# 数据源
DATA_SOURCE_OPERATION_UNSUPPORTED = ErrorCode(_("数据源不支持该操作"))
DATA_SOURCE_NOT_EXIST = ErrorCode(_("数据源不存在"))
Expand Down
14 changes: 12 additions & 2 deletions src/bk-user/tests/apis/web/data_source/test_data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def local_ds_plugin_config() -> Dict[str, Any]:
"not_continuous_letter": True,
"not_continuous_digit": True,
"not_repeated_symbol": True,
"valid_time": 86400,
"valid_time": 7,
"max_retries": 3,
"lock_time": 3600,
},
Expand Down Expand Up @@ -87,7 +87,7 @@ def local_ds_plugin_config() -> Dict[str, Any]:
},
},
"password_expire": {
"remind_before_expire": [3600, 7200],
"remind_before_expire": [1, 7],
"notification": {
"enabled_methods": [NotificationMethod.EMAIL, NotificationMethod.SMS],
"templates": [
Expand Down Expand Up @@ -153,6 +153,16 @@ def test_list(self, api_client):
assert DataSourcePluginEnum.LOCAL in [d["id"] for d in resp.data]


class TestDataSourcePluginDefaultConfigApi:
def test_retrieve(self, api_client):
resp = api_client.get(reverse("data_source_plugin.default_config", args=[DataSourcePluginEnum.LOCAL.value]))
assert resp.status_code == status.HTTP_200_OK

def test_retrieve_not_exists(self, api_client):
resp = api_client.get(reverse("data_source_plugin.default_config", args=["not_exists"]))
assert resp.status_code == status.HTTP_400_BAD_REQUEST


class TestDataSourceCreateApi:
def test_create_local_data_source(self, api_client, local_ds_plugin_config):
resp = api_client.post(
Expand Down

0 comments on commit 75c8ac7

Please sign in to comment.