Merge pull request #8085 from jumpserver/dev

v2.21.0-rc5
pull/8133/head
老广 2022-04-19 13:15:16 +08:00 committed by GitHub
commit 714b6b1233
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 122 additions and 52 deletions

View File

@ -15,7 +15,7 @@ class TempTokenViewSet(JMSModelViewSet):
def get_queryset(self): def get_queryset(self):
username = self.request.user.username 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') @action(methods=['PATCH'], detail=True, url_path='expire')
def expire(self, *args, **kwargs): def expire(self, *args, **kwargs):
@ -24,4 +24,3 @@ class TempTokenViewSet(JMSModelViewSet):
instance.save() instance.save()
serializer = self.get_serializer(instance) serializer = self.get_serializer(instance)
return Response(serializer.data) return Response(serializer.data)

View File

@ -23,7 +23,7 @@ class Migration(migrations.Migration):
('secret', models.CharField(max_length=64, verbose_name='Secret')), ('secret', models.CharField(max_length=64, verbose_name='Secret')),
('verified', models.BooleanField(default=False, verbose_name='Verified')), ('verified', models.BooleanField(default=False, verbose_name='Verified')),
('date_verified', models.DateTimeField(null=True, verbose_name='Date 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={ options={
'verbose_name': 'Temporary token', 'verbose_name': 'Temporary token',

View File

@ -4,6 +4,7 @@ from rest_framework.exceptions import PermissionDenied
from rest_framework.decorators import action from rest_framework.decorators import action
from common.drf.api import JMSModelViewSet from common.drf.api import JMSModelViewSet
from ..filters import RoleFilter
from ..serializers import RoleSerializer, RoleUserSerializer from ..serializers import RoleSerializer, RoleUserSerializer
from ..models import Role, SystemRole, OrgRole from ..models import Role, SystemRole, OrgRole
from .permission import PermissionViewSet from .permission import PermissionViewSet
@ -20,8 +21,8 @@ class RoleViewSet(JMSModelViewSet):
'default': RoleSerializer, 'default': RoleSerializer,
'users': RoleUserSerializer, 'users': RoleUserSerializer,
} }
filterset_fields = ['name', 'scope', 'builtin'] filterset_class = RoleFilter
search_fields = filterset_fields search_fields = ('name', 'scope', 'builtin')
rbac_perms = { rbac_perms = {
'users': 'rbac.view_rolebinding' 'users': 'rbac.view_rolebinding'
} }

View File

@ -2,6 +2,13 @@ from django.utils.translation import ugettext_noop
from .const import Scope, system_exclude_permissions, org_exclude_permissions 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: 获取应该区分 系统用户,和组织用户的权限 # Todo: 获取应该区分 系统用户,和组织用户的权限
# 工作台也区分组织后再考虑 # 工作台也区分组织后再考虑
user_perms = ( user_perms = (
@ -15,10 +22,6 @@ user_perms = (
('assets', 'node', 'match', 'node'), ('assets', 'node', 'match', 'node'),
('applications', 'application', 'match', 'application'), ('applications', 'application', 'match', 'application'),
('ops', 'commandexecution', 'add', 'commandexecution'), ('ops', 'commandexecution', 'add', 'commandexecution'),
('authentication', 'connectiontoken', 'add', 'connectiontoken'),
('authentication', 'temptoken', 'add', 'temptoken'),
('tickets', 'ticket', 'view', 'ticket'),
('orgs', 'organization', 'view', 'rootorg'),
) )
auditor_perms = user_perms + ( auditor_perms = user_perms + (
@ -104,7 +107,7 @@ class BuiltinRole:
'4', ugettext_noop('SystemComponent'), Scope.system, app_exclude_perms, 'exclude' '4', ugettext_noop('SystemComponent'), Scope.system, app_exclude_perms, 'exclude'
) )
system_user = PredefineRole( system_user = PredefineRole(
'3', ugettext_noop('User'), Scope.system, user_perms '3', ugettext_noop('User'), Scope.system, system_user_perms
) )
org_admin = PredefineRole( org_admin = PredefineRole(
'5', ugettext_noop('OrgAdmin'), Scope.org, [] '5', ugettext_noop('OrgAdmin'), Scope.org, []

25
apps/rbac/filters.py Normal file
View File

@ -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

View File

@ -90,4 +90,3 @@ class Permission(DjangoPermission):
permissions = cls.objects.all() permissions = cls.objects.all()
permissions = cls.clean_permissions(permissions, scope=scope) permissions = cls.clean_permissions(permissions, scope=scope)
return permissions return permissions

View File

@ -121,6 +121,20 @@ class Role(JMSModel):
def is_org(self): def is_org(self):
return self.scope == const.Scope.org 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): class SystemRole(Role):
objects = SystemRoleManager() objects = SystemRoleManager()

View File

@ -100,6 +100,28 @@ class RoleBinding(JMSModel):
def is_scope_org(self): def is_scope_org(self):
return self.scope == Scope.org 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): class OrgRoleBindingManager(RoleBindingManager):
def get_queryset(self): def get_queryset(self):

View File

@ -59,10 +59,15 @@ class CommandStore(object):
data = self.es.indices.get_mapping(self.index) data = self.es.indices.get_mapping(self.index)
except NotFoundError: except NotFoundError:
return False return False
info = self.es.info()
version = info['version']['number'].split('.')[0]
try: try:
# 检测索引是不是新的类型 if version == '6':
properties = data[self.index]['mappings']['properties'] # 检测索引是不是新的类型 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' \ if properties['session']['type'] == 'keyword' \
and properties['org_id']['type'] == 'keyword': and properties['org_id']['type'] == 'keyword':
return True return True
@ -75,27 +80,30 @@ class CommandStore(object):
self._ensure_index_exists() self._ensure_index_exists()
def _ensure_index_exists(self): def _ensure_index_exists(self):
mappings = { properties = {
"mappings": { "session": {
"properties": { "type": "keyword"
"session": { },
"type": "keyword" "org_id": {
}, "type": "keyword"
"org_id": { },
"type": "keyword" "@timestamp": {
}, "type": "date"
"@timestamp": { },
"type": "date" "timestamp": {
}, "type": "long"
"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: try:
self.es.indices.create(self.index, body=mappings) self.es.indices.create(self.index, body=mappings)
return
except RequestError as e: except RequestError as e:
logger.exception(e) logger.exception(e)

View File

@ -20,7 +20,7 @@ def migrate_endpoints(apps, schema_editor):
'http_port': 0, 'http_port': 0,
'created_by': 'System' 'created_by': 'System'
} }
default_endpoint = Endpoint.objects.create(**default_data) Endpoint.objects.create(**default_data)
if not settings.XRDP_ENABLED: if not settings.XRDP_ENABLED:
return return
@ -81,8 +81,8 @@ class Migration(migrations.Migration):
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), ('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')), ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, blank=True, verbose_name='Name')), ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('host', models.CharField(max_length=256, verbose_name='Host')), ('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')), ('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')), ('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')), ('ssh_port', common.fields.model.PortField(default=2222, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='SSH Port')),

View File

@ -863,23 +863,20 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
return None return None
return self.SOURCE_BACKEND_MAPPING.get(self.source, []) return self.SOURCE_BACKEND_MAPPING.get(self.source, [])
@property @lazyproperty
def all_orgs(self): def console_orgs(self):
from rbac.builtin import BuiltinRole from rbac.models import RoleBinding
has_system_role = self.system_roles.all() \ return RoleBinding.get_user_has_the_perm_orgs('rbac.view_console', self)
.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
@property @lazyproperty
def my_orgs(self): def audit_orgs(self):
return list(self.orgs.distinct()) 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: class Meta:
ordering = ['username'] ordering = ['username']

View File

@ -121,14 +121,16 @@ class UserProfileSerializer(UserSerializer):
mfa_level = serializers.ChoiceField(choices=MFA_LEVEL_CHOICES, label=_('MFA'), required=False) mfa_level = serializers.ChoiceField(choices=MFA_LEVEL_CHOICES, label=_('MFA'), required=False)
guide_url = serializers.SerializerMethodField() guide_url = serializers.SerializerMethodField()
receive_backends = serializers.ListField(child=serializers.CharField(), read_only=True) receive_backends = serializers.ListField(child=serializers.CharField(), read_only=True)
orgs = UserOrgSerializer(many=True, read_only=True, source='all_orgs') console_orgs = UserOrgSerializer(many=True, read_only=True)
myorgs = UserOrgSerializer(many=True, read_only=True, source='my_orgs') audit_orgs = UserOrgSerializer(many=True, read_only=True)
workbench_orgs = UserOrgSerializer(many=True, read_only=True)
perms = serializers.ListField(label=_("Perms"), read_only=True) perms = serializers.ListField(label=_("Perms"), read_only=True)
class Meta(UserSerializer.Meta): class Meta(UserSerializer.Meta):
read_only_fields = [ read_only_fields = [
'date_joined', 'last_login', 'created_by', 'source', '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 + [ fields = UserSerializer.Meta.fields + [
'public_key_comment', 'public_key_hash_md5', 'guide_url', 'public_key_comment', 'public_key_hash_md5', 'guide_url',