From a9d619a4848946cc695d08499e291077be7e7e6d Mon Sep 17 00:00:00 2001 From: Blues Yu Date: Sun, 23 Jan 2022 19:21:10 +0800 Subject: [PATCH 001/188] feat: support in query in v3 apis(only profiles) --- src/api/bkuser_core/profiles/v3/filters.py | 77 ++++++++++++++++--- .../bkuser_core/profiles/v3/serializers.py | 15 +++- src/api/bkuser_core/profiles/v3/views.py | 4 +- .../tests/apis/v3/profiles/test_list.py | 18 +++++ 4 files changed, 99 insertions(+), 15 deletions(-) diff --git a/src/api/bkuser_core/profiles/v3/filters.py b/src/api/bkuser_core/profiles/v3/filters.py index ca28e5caa..aa712e76f 100644 --- a/src/api/bkuser_core/profiles/v3/filters.py +++ b/src/api/bkuser_core/profiles/v3/filters.py @@ -7,21 +7,76 @@ 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.db.models import ManyToManyField, ManyToManyRel, ManyToOneRel +from typing import Generator + +from django.db.models import CharField, DateTimeField, FloatField, IntegerField, TextField from rest_framework import filters +class FieldFilter: + def extract_fields(self, queryset) -> Generator[str, None, None]: + raise NotImplementedError + + def get_key(self, key: str) -> str: + raise NotImplementedError + + def gen_query(self, queryset, params: dict) -> dict: + return { + self.get_key(key): value for key, value in params.items() if key in list(self.extract_fields(queryset)) + } + + +class M2MFieldFilter(FieldFilter): + def extract_fields(self, queryset) -> Generator[str, None, None]: + # TODO: maybe we can use `queryset.model._meta.get_field(key).m2m_field_name` + return (x for x in ["departments", "leader"]) + + def get_key(self, key: str) -> str: + return f"{key}__in" + + +class IContainFieldFilter(FieldFilter): + def extract_fields(self, queryset) -> Generator[str, None, None]: + available_fields = queryset.model._meta.get_fields() + for f in available_fields: + if isinstance(f, (CharField, TextField, DateTimeField)): + yield f.name + + def get_key(self, key) -> str: + return f"{key}__icontains" + + +class PlainFieldFilter(FieldFilter): + def extract_fields(self, queryset) -> Generator[str, None, None]: + available_fields = queryset.model._meta.get_fields() + for f in available_fields: + if isinstance(f, (IntegerField, FloatField)): + yield f.name + + def get_key(self, key) -> str: + return key + + +class InFieldFilter(FieldFilter): + def extract_fields(self, queryset) -> Generator[str, None, None]: + # TODO: get from serializer? + return (x for x in ["username__in", "staff_status__in", "status__in"]) + + def get_key(self, key: str) -> str: + return key + + class MultipleFieldFilter(filters.SearchFilter): """多字段过滤器, 同时支持标准和非标准过滤""" - def filter_by_params(self, params: dict, queryset, view): - """非标准 filter""" - plain_fields = [ - f.name - for f in queryset.model._meta.get_fields() - if not isinstance(f, (ManyToOneRel, ManyToManyRel, ManyToManyField)) - ] - plain_query_params = {key: value for key, value in params.items() if key in plain_fields} - m2m_query_params = {f"{key}__in": value for key, value in params.items() if key in view.supported_m2m_fields} + field_filters: list = [M2MFieldFilter, IContainFieldFilter, PlainFieldFilter, InFieldFilter] + + def filter_by_params(self, queryset, params: dict, view): + """根据不同字段类型进行多字段过滤""" + + query = {} + for f_filter in self.field_filters: + query.update(f_filter().gen_query(queryset, params)) + # in operator on many-to-many fields may cause duplicate results, so we use distinct - return queryset.filter(**m2m_query_params, **plain_query_params).distinct() + return queryset.filter(**query).distinct() diff --git a/src/api/bkuser_core/profiles/v3/serializers.py b/src/api/bkuser_core/profiles/v3/serializers.py index c32ec7cee..e962a569b 100644 --- a/src/api/bkuser_core/profiles/v3/serializers.py +++ b/src/api/bkuser_core/profiles/v3/serializers.py @@ -11,7 +11,7 @@ from bkuser_core.apis.v3.serializers import StringArrayField from bkuser_core.departments.serializers import SimpleDepartmentSerializer from bkuser_core.profiles.v2.serializers import LeaderSerializer -from rest_framework.fields import BooleanField, CharField, IntegerField, JSONField +from rest_framework.fields import BooleanField, CharField, DateTimeField, IntegerField, JSONField from rest_framework.serializers import Serializer @@ -31,6 +31,14 @@ class ProfileSerializer(Serializer): position = CharField(required=False, help_text="职位") enabled = BooleanField(required=False, help_text="是否启用", default=True) extras = JSONField(required=False, help_text="扩展字段") + password_valid_days = IntegerField(required=False, help_text="密码有效期") + country_code = CharField(required=False, help_text="国家码") + iso_code = CharField(required=False, help_text="国家码") + time_zone = CharField(required=False, help_text="时区") + + last_login_time = DateTimeField(required=False, help_text="最后登录时间") + create_time = DateTimeField(required=False, help_text="创建时间") + update_time = DateTimeField(required=False, help_text="更新时间") # ------------ @@ -44,9 +52,14 @@ class QueryProfileSerializer(ProfileSerializer): departments = StringArrayField(required=False, help_text="部门id列表") leaders = StringArrayField(required=False, help_text="上级id列表") + username__in = StringArrayField(required=False, help_text="用户名列表") + staff_status__in = StringArrayField(required=False, help_text="在职状态列表") + status__in = StringArrayField(required=False, help_text="账户状态列表") + def to_internal_value(self, data): data = super().to_internal_value(data) + # leaders 更符合规范,兼容 leader if "leaders" in data: data["leader"] = data.pop("leaders") diff --git a/src/api/bkuser_core/profiles/v3/views.py b/src/api/bkuser_core/profiles/v3/views.py index e412eea8d..45b86203c 100644 --- a/src/api/bkuser_core/profiles/v3/views.py +++ b/src/api/bkuser_core/profiles/v3/views.py @@ -34,8 +34,6 @@ class ProfileViewSet(viewsets.ModelViewSet, ListAPIView): pagination_class = AdvancedPagination ordering = "id" - supported_m2m_fields = ["leader", "departments"] - @inject_serializer(query_in=QueryProfileSerializer, out=PaginatedProfileSerializer) def list(self, request, validated_data: dict, *args, **kwargs): """获取用户列表""" @@ -43,7 +41,7 @@ def list(self, request, validated_data: dict, *args, **kwargs): try: queryset = MultipleFieldFilter().filter_by_params( - validated_data, self.filter_queryset(self.get_queryset()), self + self.filter_queryset(self.get_queryset()), validated_data, self ) except IAMPermissionDenied: raise diff --git a/src/api/bkuser_core/tests/apis/v3/profiles/test_list.py b/src/api/bkuser_core/tests/apis/v3/profiles/test_list.py index d04fdb7c0..49dacfffd 100644 --- a/src/api/bkuser_core/tests/apis/v3/profiles/test_list.py +++ b/src/api/bkuser_core/tests/apis/v3/profiles/test_list.py @@ -51,6 +51,24 @@ def test_profile_list(self, factory, view): "qq=aaaa&status=NORMAL", "user-a-1", ), + ( + { + "user-a-1": {"qq": "aaaa", "status": "NORMAL"}, + "user-b-1": {"qq": "aaaa", "status": "DELETED"}, + "user-a-2": {"qq": "bbbb"}, + }, + "qq=aaaa&status__in=NORMAL,DELETED", + "user-a-1,user-b-1", + ), + ( + { + "user-a-1": {"qq": "aaaa", "status": "NORMAL"}, + "user-b-1": {"qq": "aaaa", "status": "DELETED"}, + "user-a-2": {"qq": "bbbb"}, + }, + "username__in=user-a-1,user-b-1&status__in=NORMAL", + "user-a-1", + ), ], ) def test_multiple_fields(self, factory, view, samples, params, expected): From 8cb69738897a3e75af55c350d730669ddcfe1fce Mon Sep 17 00:00:00 2001 From: Blues Yu Date: Mon, 14 Feb 2022 20:36:05 +0800 Subject: [PATCH 002/188] fix: login plugin not loaded normally --- src/login/bklogin/config/overlays/prod.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/login/bklogin/config/overlays/prod.py b/src/login/bklogin/config/overlays/prod.py index b61a48998..11f9f1f63 100644 --- a/src/login/bklogin/config/overlays/prod.py +++ b/src/login/bklogin/config/overlays/prod.py @@ -12,9 +12,12 @@ from bklogin.config.common.django_basic import * # noqa from bklogin.config.common.logging import * # noqa from bklogin.config.common.platform import * # noqa +from bklogin.config.common.plugin import * # noqa from bklogin.config.common.storage import * # noqa from bkuser_global.logging import LoggingType, get_logging SITE_URL = "/login/" -LOGGING = get_logging(logging_type=LoggingType.STDOUT, log_level=LOG_LEVEL, package_name="bkuser_core") +LOGGING = get_logging( + logging_type=LoggingType.STDOUT, log_level=LOG_LEVEL, package_name="bkuser_core", formatter="verbose" +) From 8d6d9b7096125eb4534efa7984c961985ea34a99 Mon Sep 17 00:00:00 2001 From: Blues Yu Date: Mon, 21 Feb 2022 14:16:49 +0800 Subject: [PATCH 003/188] minor: change login page title --- src/login/bklogin/templates/account/login_ce.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/login/bklogin/templates/account/login_ce.html b/src/login/bklogin/templates/account/login_ce.html index 23c9c3afd..b12a584e3 100755 --- a/src/login/bklogin/templates/account/login_ce.html +++ b/src/login/bklogin/templates/account/login_ce.html @@ -8,7 +8,7 @@ - {% trans '统一登录 | 腾讯蓝鲸智云' %} + {% trans '登录 | 腾讯蓝鲸智云' %} {% if is_plain %} - - - - - - - -
-
- {% block body_content %}{% endblock %} -
- - - -
- - - - - - - - - - - - - - - - - - - - {% block script %}{% endblock %} - - diff --git a/src/login/bklogin/templates/account/no_right.html b/src/login/bklogin/templates/account/no_right.html deleted file mode 100755 index ce1bc6059..000000000 --- a/src/login/bklogin/templates/account/no_right.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "account/base.html" %} -{% load i18n %} -{% block body_content %} -
-
{% trans '你不是管理员, 没有用户管理的权限!' %}
-
{% trans '请找管理员申请权限!' %}
-
-{% endblock %} \ No newline at end of file diff --git a/src/login/bklogin/templates/account/user_table.part b/src/login/bklogin/templates/account/user_table.part deleted file mode 100755 index b9d932039..000000000 --- a/src/login/bklogin/templates/account/user_table.part +++ /dev/null @@ -1,116 +0,0 @@ -{% load i18n %} - - - - - - - - - - - - - - {% if records %} - {% for obj in records %} - - - - - - - - - {% endfor %} - {% else %} - - {% endif %} - -
{% trans '用户名' %}{% trans '中文名' %}{% trans '联系电话' %}{% trans '常用邮箱' %}{% trans '角色' %}{% trans '操作' %}
- - - - - - - - - {% if request.user.is_superuser %} - - {% else %} - - {% endif %} - - - - - - {% if request.user.is_superuser %} - - {% endif %} -
{% trans '没有数据' %}
- -
- - -
diff --git a/src/login/bklogin/templates/account/users.html b/src/login/bklogin/templates/account/users.html deleted file mode 100755 index 049d69ade..000000000 --- a/src/login/bklogin/templates/account/users.html +++ /dev/null @@ -1,102 +0,0 @@ -{% extends "account/base.html" %} -{% load i18n %} -{% block body_content %} -