Skip to content

Commit

Permalink
Merge branch 'ft_tenant' of https://github.com/Canway-shiisa/bk-user
Browse files Browse the repository at this point in the history
…into ft_tenant_list_ds_user

# Conflicts:
#	src/bk-user/bkuser/apis/web/urls.py
  • Loading branch information
Canway-shiisa committed Aug 23, 2023
2 parents 851c740 + 608114a commit 5a714f1
Show file tree
Hide file tree
Showing 81 changed files with 3,986 additions and 895 deletions.
1 change: 1 addition & 0 deletions src/bk-user/bkuser/apis/web/basic/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@

class CurrentUserRetrieveOutputSLZ(serializers.Serializer):
username = serializers.CharField(help_text="用户名")
tenant_id = serializers.CharField(help_text="租户 ID")
1 change: 1 addition & 0 deletions src/bk-user/bkuser/apis/web/basic/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def get(self, request, *args, **kwargs):
current_user = request.user
info = {
"username": current_user.username,
"tenant_id": current_user.get_property("tenant_id"),
}

return Response(CurrentUserRetrieveOutputSLZ(instance=info).data)
10 changes: 10 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -*- 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.
"""
46 changes: 46 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# -*- 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.
"""
import logging

from django.conf import settings
from drf_yasg.utils import swagger_serializer_method
from rest_framework import serializers

from bkuser.apps.tenant.models import Tenant

logger = logging.getLogger(__name__)


class TenantDepartmentOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="租户部门ID")
name = serializers.CharField(help_text="部门名称")
has_children = serializers.BooleanField(help_text="是否有子部门")


class TenantListOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="租户ID")
name = serializers.CharField(help_text="租户名称")
logo = serializers.SerializerMethodField(help_text="租户 Logo")
departments = serializers.SerializerMethodField(help_text="租户下每个数据源的根组织")

def get_logo(self, instance: Tenant) -> str:
return instance.logo or settings.DEFAULT_TENANT_LOGO

@swagger_serializer_method(serializer_or_field=TenantDepartmentOutputSLZ(many=True))
def get_departments(self, instance: Tenant):
departments = self.context["tenant_root_departments_map"].get(instance.id) or []
return [item.model_dump(include={"id", "name", "has_children"}) for item in departments]


class TenantDepartmentChildrenListOutputSLZ(serializers.Serializer):
id = serializers.IntegerField(help_text="租户部门ID")
name = serializers.CharField(help_text="部门名称")
has_children = serializers.BooleanField(help_text="是否有子部门")
24 changes: 24 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- 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 django.urls import path

from . import views

urlpatterns = [
# 租户
path("tenants/", views.TenantListApi.as_view(), name="organization.tenant.list"),
path("tenants/<str:id>/", views.TenantRetrieveUpdateApi.as_view(), name="organization.tenant.retrieve_update"),
path(
"departments/<int:id>/children/",
views.TenantDepartmentChildrenListApi.as_view(),
name="organization.children.list",
),
]
110 changes: 110 additions & 0 deletions src/bk-user/bkuser/apis/web/organization/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# -*- 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.
"""
import logging

from drf_yasg.utils import swagger_auto_schema
from rest_framework import generics, status
from rest_framework.response import Response

from bkuser.apis.web.organization.serializers import TenantDepartmentChildrenListOutputSLZ, TenantListOutputSLZ
from bkuser.apis.web.tenant.serializers import TenantRetrieveOutputSLZ, TenantUpdateInputSLZ
from bkuser.apps.tenant.models import Tenant
from bkuser.biz.tenant import (
TenantDepartmentHandler,
TenantEditableBaseInfo,
TenantFeatureFlag,
TenantHandler,
)
from bkuser.common.error_codes import error_codes
from bkuser.common.views import ExcludePatchAPIViewMixin

logger = logging.getLogger(__name__)


class TenantListApi(generics.ListAPIView):
pagination_class = None
queryset = Tenant.objects.all()
serializer_class = TenantListOutputSLZ

def _get_tenant_id(self) -> str:
return self.request.user.get_property("tenant_id")

def get_serializer_context(self):
tenant_ids = list(self.queryset.values_list("id", flat=True))
tenant_root_departments_map = TenantDepartmentHandler.get_tenant_root_department_map_by_tenant_id(
tenant_ids, self._get_tenant_id()
)
return {"tenant_root_departments_map": tenant_root_departments_map}

@swagger_auto_schema(
operation_description="租户列表",
responses={status.HTTP_200_OK: TenantListOutputSLZ(many=True)},
)
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)


class TenantRetrieveUpdateApi(ExcludePatchAPIViewMixin, generics.RetrieveUpdateAPIView):
queryset = Tenant.objects.all()
pagination_class = None
serializer_class = TenantRetrieveOutputSLZ
lookup_url_kwarg = "id"

def _get_tenant_id(self) -> str:
return self.request.user.get_property("tenant_id")

def get_serializer_context(self):
return {"current_tenant_id": self._get_tenant_id()}

@swagger_auto_schema(
operation_description="单个租户详情",
responses={status.HTTP_200_OK: TenantRetrieveOutputSLZ()},
)
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)

@swagger_auto_schema(
operation_description="更新租户",
request_body=TenantUpdateInputSLZ(),
responses={status.HTTP_200_OK: ""},
)
def put(self, request, *args, **kwargs):
slz = TenantUpdateInputSLZ(data=request.data)
slz.is_valid(raise_exception=True)
data = slz.validated_data

instance = self.get_object()
# NOTE 因协同数据源而展示的租户,非当前租户, 无权限做更新操作
if self._get_tenant_id() != instance.id:
raise error_codes.NO_PERMISSION

should_updated_info = TenantEditableBaseInfo(
name=data["name"], logo=data["logo"] or "", feature_flags=TenantFeatureFlag(**data["feature_flags"])
)

TenantHandler.update_with_managers(instance.id, should_updated_info, data["manager_ids"])
return Response()


class TenantDepartmentChildrenListApi(generics.ListAPIView):
pagination_class = None
serializer_class = TenantDepartmentChildrenListOutputSLZ

@swagger_auto_schema(
operation_description="租户部门的二级子部门列表",
responses={status.HTTP_200_OK: TenantDepartmentChildrenListOutputSLZ(many=True)},
)
def get(self, request, *args, **kwargs):
tenant_department_id = self.kwargs["id"]
# 拉取子部门信息列表
tenant_department_children = TenantDepartmentHandler.get_tenant_department_children_by_id(tenant_department_id)
data = [item.model_dump(include={"id", "name", "has_children"}) for item in tenant_department_children]
return Response(TenantDepartmentChildrenListOutputSLZ(data, many=True).data)
32 changes: 15 additions & 17 deletions src/bk-user/bkuser/apis/web/tenant/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from bkuser.apps.data_source.models import DataSourceUser
from bkuser.apps.tenant.models import Tenant, TenantUser
from bkuser.biz.data_source import DataSourceSimpleInfo
from bkuser.biz.tenant import TenantUserWithInheritedInfo
from bkuser.biz.tenant import TenantHandler, TenantUserWithInheritedInfo
from bkuser.biz.validators import validate_tenant_id


Expand Down Expand Up @@ -54,13 +54,13 @@ class TenantSearchInputSLZ(serializers.Serializer):
name = serializers.CharField(required=False, help_text="租户名", allow_blank=True)


class TenantSearchManagerOutputSchema(serializers.Serializer):
class TenantSearchManagerOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="用户 ID")
username = serializers.CharField(help_text="用户名")
full_name = serializers.CharField(help_text="姓名")


class TenantSearchDataSourceOutputSchema(serializers.Serializer):
class TenantSearchDataSourceOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="数据源 ID")
name = serializers.CharField(help_text="数据源名称")

Expand All @@ -79,7 +79,7 @@ def get_logo(self, obj: Tenant) -> str:
def get_created_at(self, obj: Tenant) -> str:
return obj.created_at_display

@swagger_serializer_method(serializer_or_field=TenantSearchManagerOutputSchema(many=True))
@swagger_serializer_method(serializer_or_field=TenantSearchManagerOutputSLZ(many=True))
def get_managers(self, obj: Tenant) -> List[Dict]:
tenant_manager_map: Dict[str, List[TenantUserWithInheritedInfo]] = self.context["tenant_manager_map"]
managers = tenant_manager_map.get(obj.id) or []
Expand All @@ -91,7 +91,7 @@ def get_managers(self, obj: Tenant) -> List[Dict]:
for i in managers
]

@swagger_serializer_method(serializer_or_field=TenantSearchDataSourceOutputSchema(many=True))
@swagger_serializer_method(serializer_or_field=TenantSearchDataSourceOutputSLZ(many=True))
def get_data_sources(self, obj: Tenant) -> List[Dict]:
data_source_map: Dict[str, List[DataSourceSimpleInfo]] = self.context["data_source_map"]
data_sources = data_source_map.get(obj.id) or []
Expand All @@ -100,12 +100,12 @@ def get_data_sources(self, obj: Tenant) -> List[Dict]:

class TenantUpdateInputSLZ(serializers.Serializer):
name = serializers.CharField(help_text="租户名称")
logo = serializers.CharField(help_text="租户 Logo", required=False)
logo = serializers.CharField(help_text="租户 Logo", required=False, default=settings.DEFAULT_TENANT_LOGO)
manager_ids = serializers.ListField(child=serializers.CharField(), help_text="租户用户 ID 列表", allow_empty=False)
feature_flags = TenantFeatureFlagSLZ(help_text="租户特性集")


class TenantRetrieveManagerOutputSchema(serializers.Serializer):
class TenantRetrieveManagerOutputSLZ(serializers.Serializer):
id = serializers.CharField(help_text="用户 ID")
username = serializers.CharField(help_text="租户用户名")
full_name = serializers.CharField(help_text="用户姓名")
Expand All @@ -123,18 +123,16 @@ class TenantRetrieveOutputSLZ(serializers.Serializer):
feature_flags = TenantFeatureFlagSLZ(help_text="租户特性集")
managers = serializers.SerializerMethodField()

@swagger_serializer_method(serializer_or_field=TenantRetrieveManagerOutputSchema(many=True))
@swagger_serializer_method(serializer_or_field=TenantRetrieveManagerOutputSLZ(many=True))
def get_managers(self, obj: Tenant) -> List[Dict]:
tenant_manager_map: Dict[str, List[TenantUserWithInheritedInfo]] = self.context["tenant_manager_map"]
managers = tenant_manager_map.get(obj.id) or []
# 根据当前登录的租户用户,获取租户ID
# NOTE 因协同数据源而展示的租户,不返回管理员
if obj.id != self.context["current_tenant_id"]:
return []
managers = TenantHandler.retrieve_tenant_managers(obj.id)
return [
{
"id": i.id,
**i.data_source_user.model_dump(
include={"username", "full_name", "email", "phone", "phone_country_code"}
),
}
for i in managers
{"id": manager.id, **manager.data_source_user.model_dump(include={"username", "full_name"})}
for manager in managers
]

def get_logo(self, obj: Tenant) -> str:
Expand Down
1 change: 1 addition & 0 deletions src/bk-user/bkuser/apis/web/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
# 租户
path("tenants/", include("bkuser.apis.web.tenant.urls")),
path("data-sources/", include("bkuser.apis.web.data_source.urls")),
path("tenant-organization/", include("bkuser.apis.web.organization.urls")),
]
5 changes: 5 additions & 0 deletions src/bk-user/bkuser/auth/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import logging
import traceback

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import BaseBackend
from django.db import IntegrityError
Expand Down Expand Up @@ -43,6 +44,10 @@ def authenticate(self, request=None, bk_token=None):
user.set_property(key="language", value=user_info.get("language", ""))
user.set_property(key="time_zone", value=user_info.get("time_zone", ""))

# FIXME: 新版登录后删除该MOCK逻辑
tenant_id = settings.MOCK_USER_TENANTS.get(username) or settings.MOCK_USER_DEFAULT_TENANT
user.set_property(key="tenant_id", value=tenant_id)

return user

except IntegrityError:
Expand Down
29 changes: 28 additions & 1 deletion src/bk-user/bkuser/biz/data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@

from pydantic import BaseModel

from bkuser.apps.data_source.models import DataSource
from bkuser.apps.data_source.models import DataSource, DataSourceDepartment, DataSourceDepartmentRelation


class DataSourceDepartmentInfoWithChildren(BaseModel):
id: int
name: str
children_ids: List[int]


class DataSourceSimpleInfo(BaseModel):
Expand All @@ -38,3 +44,24 @@ def get_data_source_map_by_owner(
data[i.owner_tenant_id].append(DataSourceSimpleInfo(id=i.id, name=i.name))

return data


class DataSourceDepartmentHandler:
@staticmethod
def get_department_info_map_by_id(department_ids: List[int]) -> Dict[int, DataSourceDepartmentInfoWithChildren]:
"""
获取部门基础信息
"""
departments = DataSourceDepartment.objects.filter(id__in=department_ids)
departments_map: Dict = {}
for item in departments:
departments_map[item.id] = DataSourceDepartmentInfoWithChildren(
id=item.id,
name=item.name,
children_ids=list(
DataSourceDepartmentRelation.objects.get(department=item)
.get_children()
.values_list("department_id", flat=True)
),
)
return departments_map
Loading

0 comments on commit 5a714f1

Please sign in to comment.