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):
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)

View File

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

View File

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

View File

@ -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, []

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.clean_permissions(permissions, scope=scope)
return permissions

View File

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

View File

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

View File

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

View File

@ -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')),

View File

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

View File

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