diff --git a/apps/jumpserver/api/__init__.py b/apps/jumpserver/api/__init__.py index 34fb94102..d60681e7a 100644 --- a/apps/jumpserver/api/__init__.py +++ b/apps/jumpserver/api/__init__.py @@ -1,3 +1,4 @@ from .aggregate import * from .dashboard import IndexApi from .health import PrometheusMetricsApi, HealthCheckView +from .search import GlobalSearchView \ No newline at end of file diff --git a/apps/jumpserver/api/search.py b/apps/jumpserver/api/search.py new file mode 100644 index 000000000..b9a61cb21 --- /dev/null +++ b/apps/jumpserver/api/search.py @@ -0,0 +1,85 @@ +import re + +from django.contrib.postgres.search import SearchVector +from rest_framework.views import APIView +from rest_framework.response import Response +from django.conf import settings +from django.db.models import Q +from rest_framework.permissions import IsAuthenticated + + +class GlobalSearchView(APIView): + limits = 5 + permission_classes = [IsAuthenticated] + + def get_models(self): + from users.models import User, UserGroup + from assets.models import Asset + from accounts.models import Account + from perms.models import AssetPermission + return [ + [User, ['name', 'username']], + [UserGroup, ['name', 'comment']], + [Asset, ['name', 'address']], + [Account, ['name', 'username']], + [AssetPermission, ['name', 'comment']], + ] + + def search_model(self, model, fields, keyword): + queryset = model.objects.all() + if hasattr(model, 'get_queryset'): + queryset = model.get_queryset() + + if settings.DB_ENGINE == 'postgres': + qs = model.objects.annotate( + search=SearchVector(*fields), + ).filter(search=keyword) + else: + q = Q() + for field in fields: + q |= Q(**{field + '__icontains': keyword}) + qs = queryset.filter(q) + return qs[:self.limits] + + def get_result(self, model, fields, item, keyword): + d = { + "id": item.id, "name": item.name, + "model": model.__name__, "model_label": model._meta.verbose_name, + } + content = "" + value_list = [item.name] + + for field in fields: + field_label = model._meta.get_field(field).verbose_name + value = getattr(item, field) + + if value in value_list: + continue + value_list.append(value) + + if content: + continue + content += f"{field_label}: {value} " + + display = str(item).replace(item.name, '').replace('(', '').replace(')', '') + if display not in value: + content += f" {display} " + + d["content"] = content + return d + + def get(self, request): + q = request.query_params.get("q", "").strip() + models = self.get_models() + results = [] + + for model, fields in models: + perm = model._meta.app_label + '.' + 'view_' + model._meta.model_name + if not request.user.has_perm(perm): + continue + qs = self.search_model(model, fields, q) + for item in qs: + d = self.get_result(model, fields, item, q) + results.append(d) + + return Response(results) \ No newline at end of file diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index 9bc029dd5..d8ee390ff 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -38,6 +38,7 @@ api_v1 = resource_api + [ path('resources/', api.ResourceTypeListApi.as_view(), name='resource-list'), path('resources//', api.ResourceListApi.as_view()), path('resources///', api.ResourceDetailApi.as_view()), + path('search/', api.GlobalSearchView.as_view()), ] app_view_patterns = [ diff --git a/apps/perms/utils/asset_perm.py b/apps/perms/utils/asset_perm.py index 3740863ff..4e0a076a5 100644 --- a/apps/perms/utils/asset_perm.py +++ b/apps/perms/utils/asset_perm.py @@ -149,6 +149,7 @@ class PermAssetDetailUtil: def get_permed_accounts_from_perms(cls, perms, user, asset): # alias: is a collection of account usernames and special accounts [@ALL, @INPUT, @USER, @ANON] alias_action_bit_mapper, alias_date_expired_mapper = cls.parse_alias_action_date_expire(perms, asset) + # 展开 alias 到具体的账号 cleaned_accounts_action_bit, cleaned_accounts_expired = cls.map_alias_to_accounts( alias_action_bit_mapper, alias_date_expired_mapper, asset, user )