diff --git a/apps/authentication/api/temp_token.py b/apps/authentication/api/temp_token.py index 98c1d74d6..a8fcc02af 100644 --- a/apps/authentication/api/temp_token.py +++ b/apps/authentication/api/temp_token.py @@ -15,7 +15,7 @@ class TempTokenViewSet(JMSModelViewSet): def get_queryset(self): username = self.request.user.username - return TempToken.objects.filter(username=username) + return TempToken.objects.filter(username=username).order_by('-date_created') @action(methods=['PATCH'], detail=True, url_path='expire') def expire(self, *args, **kwargs): @@ -24,4 +24,3 @@ class TempTokenViewSet(JMSModelViewSet): instance.save() serializer = self.get_serializer(instance) return Response(serializer.data) - diff --git a/apps/authentication/migrations/0010_temptoken.py b/apps/authentication/migrations/0010_temptoken.py index 914188d3f..b76ae0f97 100644 --- a/apps/authentication/migrations/0010_temptoken.py +++ b/apps/authentication/migrations/0010_temptoken.py @@ -23,7 +23,7 @@ class Migration(migrations.Migration): ('secret', models.CharField(max_length=64, verbose_name='Secret')), ('verified', models.BooleanField(default=False, verbose_name='Verified')), ('date_verified', models.DateTimeField(null=True, verbose_name='Date verified')), - ('date_expired', models.DateTimeField(verbose_name='Date verified')), + ('date_expired', models.DateTimeField(verbose_name='Date expired')), ], options={ 'verbose_name': 'Temporary token', diff --git a/apps/rbac/api/role.py b/apps/rbac/api/role.py index a11edc409..ceb2e9c19 100644 --- a/apps/rbac/api/role.py +++ b/apps/rbac/api/role.py @@ -4,6 +4,7 @@ from rest_framework.exceptions import PermissionDenied from rest_framework.decorators import action from common.drf.api import JMSModelViewSet +from ..filters import RoleFilter from ..serializers import RoleSerializer, RoleUserSerializer from ..models import Role, SystemRole, OrgRole from .permission import PermissionViewSet @@ -20,8 +21,8 @@ class RoleViewSet(JMSModelViewSet): 'default': RoleSerializer, 'users': RoleUserSerializer, } - filterset_fields = ['name', 'scope', 'builtin'] - search_fields = filterset_fields + filterset_class = RoleFilter + search_fields = ('name', 'scope', 'builtin') rbac_perms = { 'users': 'rbac.view_rolebinding' } diff --git a/apps/rbac/builtin.py b/apps/rbac/builtin.py index d8e84a554..a199c149c 100644 --- a/apps/rbac/builtin.py +++ b/apps/rbac/builtin.py @@ -2,6 +2,13 @@ from django.utils.translation import ugettext_noop from .const import Scope, system_exclude_permissions, org_exclude_permissions +system_user_perms = ( + ('authentication', 'connectiontoken', 'add', 'connectiontoken'), + ('authentication', 'temptoken', 'add', 'temptoken'), + ('tickets', 'ticket', 'view', 'ticket'), + ('orgs', 'organization', 'view', 'rootorg'), +) + # Todo: 获取应该区分 系统用户,和组织用户的权限 # 工作台也区分组织后再考虑 user_perms = ( @@ -15,10 +22,6 @@ user_perms = ( ('assets', 'node', 'match', 'node'), ('applications', 'application', 'match', 'application'), ('ops', 'commandexecution', 'add', 'commandexecution'), - ('authentication', 'connectiontoken', 'add', 'connectiontoken'), - ('authentication', 'temptoken', 'add', 'temptoken'), - ('tickets', 'ticket', 'view', 'ticket'), - ('orgs', 'organization', 'view', 'rootorg'), ) auditor_perms = user_perms + ( @@ -104,7 +107,7 @@ class BuiltinRole: '4', ugettext_noop('SystemComponent'), Scope.system, app_exclude_perms, 'exclude' ) system_user = PredefineRole( - '3', ugettext_noop('User'), Scope.system, user_perms + '3', ugettext_noop('User'), Scope.system, system_user_perms ) org_admin = PredefineRole( '5', ugettext_noop('OrgAdmin'), Scope.org, [] diff --git a/apps/rbac/filters.py b/apps/rbac/filters.py new file mode 100644 index 000000000..f9a936b66 --- /dev/null +++ b/apps/rbac/filters.py @@ -0,0 +1,25 @@ +from django_filters import rest_framework as filters + +from common.drf.filters import BaseFilterSet +from rbac.models import Role + + +class RoleFilter(BaseFilterSet): + name = filters.CharFilter(method='filter_name') + + class Meta: + model = Role + fields = ('name', 'scope', 'builtin') + + @staticmethod + def filter_name(queryset, name, value): + builtin_ids = [] + for role in queryset.filter(builtin=True): + if value in role.display_name: + builtin_ids.append(role.id) + if builtin_ids: + builtin_qs = queryset.model.objects.filter(id__in=builtin_ids) + else: + builtin_qs = queryset.model.objects.none() + queryset = queryset.filter(name__icontains=value) + return queryset | builtin_qs diff --git a/apps/rbac/models/permission.py b/apps/rbac/models/permission.py index a4ea91b15..bc8fa6231 100644 --- a/apps/rbac/models/permission.py +++ b/apps/rbac/models/permission.py @@ -90,4 +90,3 @@ class Permission(DjangoPermission): permissions = cls.objects.all() permissions = cls.clean_permissions(permissions, scope=scope) return permissions - diff --git a/apps/rbac/models/role.py b/apps/rbac/models/role.py index b47bccb5b..98a226e6f 100644 --- a/apps/rbac/models/role.py +++ b/apps/rbac/models/role.py @@ -121,6 +121,20 @@ class Role(JMSModel): def is_org(self): return self.scope == const.Scope.org + @classmethod + def get_roles_by_perm(cls, perm): + app_label, codename = perm.split('.') + p = Permission.objects.filter( + codename=codename, + content_type__app_label=app_label + ).first() + if not p: + return p.roles.none() + role_ids = list(p.roles.all().values_list('id', flat=True)) + admin_ids = [BuiltinRole.system_admin.id, BuiltinRole.org_admin.id] + role_ids += admin_ids + return cls.objects.filter(id__in=role_ids) + class SystemRole(Role): objects = SystemRoleManager() diff --git a/apps/rbac/models/rolebinding.py b/apps/rbac/models/rolebinding.py index a2ee06022..643e38207 100644 --- a/apps/rbac/models/rolebinding.py +++ b/apps/rbac/models/rolebinding.py @@ -100,6 +100,28 @@ class RoleBinding(JMSModel): def is_scope_org(self): return self.scope == Scope.org + @classmethod + def get_user_has_the_perm_orgs(cls, perm, user): + from orgs.models import Organization + + roles = Role.get_roles_by_perm(perm) + bindings = list(cls.objects.root_all().filter(role__in=roles, user=user)) + system_bindings = [b for b in bindings if b.scope == Role.Scope.system.value] + + if perm == 'rbac.view_workbench': + all_orgs = user.orgs.all() + else: + all_orgs = Organization.objects.all() + + if system_bindings: + orgs = all_orgs + else: + org_ids = [b.org.id for b in bindings if b.org] + orgs = all_orgs.filter(id__in=org_ids) + if orgs and user.has_perm('orgs.view_rootorg'): + orgs = [Organization.root(), *list(orgs)] + return orgs + class OrgRoleBindingManager(RoleBindingManager): def get_queryset(self): diff --git a/apps/terminal/backends/command/es.py b/apps/terminal/backends/command/es.py index 2e8a82c88..c7ae7060e 100644 --- a/apps/terminal/backends/command/es.py +++ b/apps/terminal/backends/command/es.py @@ -59,10 +59,15 @@ class CommandStore(object): data = self.es.indices.get_mapping(self.index) except NotFoundError: return False - + info = self.es.info() + version = info['version']['number'].split('.')[0] try: - # 检测索引是不是新的类型 - properties = data[self.index]['mappings']['properties'] + if version == '6': + # 检测索引是不是新的类型 es6 + properties = data[self.index]['mappings']['data']['properties'] + else: + # 检测索引是不是新的类型 es7 default index type: _doc + properties = data[self.index]['mappings']['properties'] if properties['session']['type'] == 'keyword' \ and properties['org_id']['type'] == 'keyword': return True @@ -75,27 +80,30 @@ class CommandStore(object): self._ensure_index_exists() def _ensure_index_exists(self): - mappings = { - "mappings": { - "properties": { - "session": { - "type": "keyword" - }, - "org_id": { - "type": "keyword" - }, - "@timestamp": { - "type": "date" - }, - "timestamp": { - "type": "long" - } - } + properties = { + "session": { + "type": "keyword" + }, + "org_id": { + "type": "keyword" + }, + "@timestamp": { + "type": "date" + }, + "timestamp": { + "type": "long" } } + info = self.es.info() + version = info['version']['number'].split('.')[0] + if version == '6': + mappings = {'mappings': {'data': {'properties': properties}}} + else: + mappings = {'mappings': {'properties': properties}} try: self.es.indices.create(self.index, body=mappings) + return except RequestError as e: logger.exception(e) diff --git a/apps/terminal/migrations/0048_endpoint_endpointrule.py b/apps/terminal/migrations/0048_endpoint_endpointrule.py index 6400f710a..a03d19e98 100644 --- a/apps/terminal/migrations/0048_endpoint_endpointrule.py +++ b/apps/terminal/migrations/0048_endpoint_endpointrule.py @@ -20,7 +20,7 @@ def migrate_endpoints(apps, schema_editor): 'http_port': 0, 'created_by': 'System' } - default_endpoint = Endpoint.objects.create(**default_data) + Endpoint.objects.create(**default_data) if not settings.XRDP_ENABLED: return @@ -81,8 +81,8 @@ class Migration(migrations.Migration): ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), - ('name', models.CharField(max_length=128, unique=True, blank=True, verbose_name='Name')), - ('host', models.CharField(max_length=256, verbose_name='Host')), + ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')), + ('host', models.CharField(max_length=256, verbose_name='Host', blank=True)), ('https_port', common.fields.model.PortField(default=443, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='HTTPS Port')), ('http_port', common.fields.model.PortField(default=80, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='HTTP Port')), ('ssh_port', common.fields.model.PortField(default=2222, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='SSH Port')), diff --git a/apps/users/models/user.py b/apps/users/models/user.py index ede795d13..80b938468 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -863,23 +863,20 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser): return None return self.SOURCE_BACKEND_MAPPING.get(self.source, []) - @property - def all_orgs(self): - from rbac.builtin import BuiltinRole - has_system_role = self.system_roles.all() \ - .exclude(name=BuiltinRole.system_user.name) \ - .exists() - if has_system_role: - orgs = list(Organization.objects.all()) - else: - orgs = list(self.orgs.distinct()) - if self.has_perm('orgs.view_rootorg'): - orgs = [Organization.root()] + orgs - return orgs + @lazyproperty + def console_orgs(self): + from rbac.models import RoleBinding + return RoleBinding.get_user_has_the_perm_orgs('rbac.view_console', self) - @property - def my_orgs(self): - return list(self.orgs.distinct()) + @lazyproperty + def audit_orgs(self): + from rbac.models import RoleBinding + return RoleBinding.get_user_has_the_perm_orgs('rbac.view_audit', self) + + @lazyproperty + def workbench_orgs(self): + from rbac.models import RoleBinding + return RoleBinding.get_user_has_the_perm_orgs('rbac.view_workbench', self) class Meta: ordering = ['username'] diff --git a/apps/users/serializers/profile.py b/apps/users/serializers/profile.py index 55083c23d..ac9671d23 100644 --- a/apps/users/serializers/profile.py +++ b/apps/users/serializers/profile.py @@ -121,14 +121,16 @@ class UserProfileSerializer(UserSerializer): mfa_level = serializers.ChoiceField(choices=MFA_LEVEL_CHOICES, label=_('MFA'), required=False) guide_url = serializers.SerializerMethodField() receive_backends = serializers.ListField(child=serializers.CharField(), read_only=True) - orgs = UserOrgSerializer(many=True, read_only=True, source='all_orgs') - myorgs = UserOrgSerializer(many=True, read_only=True, source='my_orgs') + console_orgs = UserOrgSerializer(many=True, read_only=True) + audit_orgs = UserOrgSerializer(many=True, read_only=True) + workbench_orgs = UserOrgSerializer(many=True, read_only=True) perms = serializers.ListField(label=_("Perms"), read_only=True) class Meta(UserSerializer.Meta): read_only_fields = [ 'date_joined', 'last_login', 'created_by', 'source', - 'receive_backends', 'orgs', 'myorgs', 'perms', + 'console_orgs', 'audit_orgs', 'workbench_orgs', + 'receive_backends', 'perms', ] fields = UserSerializer.Meta.fields + [ 'public_key_comment', 'public_key_hash_md5', 'guide_url',