mirror of https://github.com/jumpserver/jumpserver
fix: rbac 合并 (#7658)
* perf: 修复一些错误权限位 * Pr@fix rbac@fix rbac permissions (#7648) * fix: 确保每次 migrate 执行更新 role permissions * perf: 修改 choices * feat: 兼容apple m1 * perf: 修改 migrations role permissions * perf: pymysql 导入 * perf: admin 判断 * fix: 修复消息订阅权限 Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: Aaron3S <chenyang@fit2cloud.com> Co-authored-by: feng626 <1304903146@qq.com>pull/7660/head
parent
783c163324
commit
83ff8dbf26
|
@ -54,6 +54,6 @@ class ApplicationAccountSecretViewSet(ApplicationAccountViewSet):
|
||||||
permission_classes = [RBACPermission, NeedMFAVerify]
|
permission_classes = [RBACPermission, NeedMFAVerify]
|
||||||
http_method_names = ['get', 'options']
|
http_method_names = ['get', 'options']
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'retrieve': 'view_applicationaccountsecret',
|
'retrieve': 'applications.view_applicationaccountsecret',
|
||||||
'list': 'view_applicationaccountsecret',
|
'list': 'applications.view_applicationaccountsecret',
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
#
|
#
|
||||||
from django.db.models import TextChoices
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class AppCategory(TextChoices):
|
class AppCategory(models.TextChoices):
|
||||||
db = 'db', _('Database')
|
db = 'db', _('Database')
|
||||||
remote_app = 'remote_app', _('Remote app')
|
remote_app = 'remote_app', _('Remote app')
|
||||||
cloud = 'cloud', 'Cloud'
|
cloud = 'cloud', 'Cloud'
|
||||||
|
@ -14,7 +14,7 @@ class AppCategory(TextChoices):
|
||||||
return dict(cls.choices).get(category, '')
|
return dict(cls.choices).get(category, '')
|
||||||
|
|
||||||
|
|
||||||
class AppType(TextChoices):
|
class AppType(models.TextChoices):
|
||||||
# db category
|
# db category
|
||||||
mysql = 'mysql', 'MySQL'
|
mysql = 'mysql', 'MySQL'
|
||||||
redis = 'redis', 'Redis'
|
redis = 'redis', 'Redis'
|
||||||
|
|
|
@ -8,7 +8,6 @@ from functools import reduce
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from common.db.models import TextChoices
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
@ -59,7 +58,7 @@ class AssetQuerySet(models.QuerySet):
|
||||||
class ProtocolsMixin:
|
class ProtocolsMixin:
|
||||||
protocols = ''
|
protocols = ''
|
||||||
|
|
||||||
class Protocol(TextChoices):
|
class Protocol(models.TextChoices):
|
||||||
ssh = 'ssh', 'SSH'
|
ssh = 'ssh', 'SSH'
|
||||||
rdp = 'rdp', 'RDP'
|
rdp = 'rdp', 'RDP'
|
||||||
telnet = 'telnet', 'Telnet'
|
telnet = 'telnet', 'Telnet'
|
||||||
|
|
|
@ -7,7 +7,6 @@ import random
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
import paramiko
|
import paramiko
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import TextChoices
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
@ -55,7 +54,7 @@ class Gateway(BaseUser):
|
||||||
UNCONNECTIVE_SILENCE_PERIOD_KEY_TMPL = 'asset_unconnective_gateway_silence_period_{}'
|
UNCONNECTIVE_SILENCE_PERIOD_KEY_TMPL = 'asset_unconnective_gateway_silence_period_{}'
|
||||||
UNCONNECTIVE_SILENCE_PERIOD_BEGIN_VALUE = 60 * 5
|
UNCONNECTIVE_SILENCE_PERIOD_BEGIN_VALUE = 60 * 5
|
||||||
|
|
||||||
class Protocol(TextChoices):
|
class Protocol(models.TextChoices):
|
||||||
ssh = 'ssh', 'SSH'
|
ssh = 'ssh', 'SSH'
|
||||||
|
|
||||||
ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
|
ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
|
||||||
|
|
|
@ -5,13 +5,11 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
|
||||||
from common.utils import signer, get_object_or_none
|
from common.utils import signer, get_object_or_none
|
||||||
from common.db.models import TextChoices
|
|
||||||
from .base import BaseUser
|
from .base import BaseUser
|
||||||
from .asset import Asset
|
from .asset import Asset
|
||||||
from .authbook import AuthBook
|
from .authbook import AuthBook
|
||||||
|
@ -24,7 +22,7 @@ logger = logging.getLogger(__name__)
|
||||||
class ProtocolMixin:
|
class ProtocolMixin:
|
||||||
protocol: str
|
protocol: str
|
||||||
|
|
||||||
class Protocol(TextChoices):
|
class Protocol(models.TextChoices):
|
||||||
ssh = 'ssh', 'SSH'
|
ssh = 'ssh', 'SSH'
|
||||||
rdp = 'rdp', 'RDP'
|
rdp = 'rdp', 'RDP'
|
||||||
telnet = 'telnet', 'Telnet'
|
telnet = 'telnet', 'Telnet'
|
||||||
|
@ -217,7 +215,7 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
|
||||||
(LOGIN_MANUAL, _('Manually input'))
|
(LOGIN_MANUAL, _('Manually input'))
|
||||||
)
|
)
|
||||||
|
|
||||||
class Type(TextChoices):
|
class Type(models.TextChoices):
|
||||||
common = 'common', _('Common user')
|
common = 'common', _('Common user')
|
||||||
admin = 'admin', _('Admin user')
|
admin = 'admin', _('Admin user')
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import os
|
import os
|
||||||
|
import platform
|
||||||
|
|
||||||
|
if platform.system() == 'Darwin' and platform.machine() == 'arm64':
|
||||||
|
import pymysql
|
||||||
|
pymysql.version_info = (1, 4, 2, "final", 0)
|
||||||
|
pymysql.install_as_MySQLdb()
|
||||||
|
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
|
@ -68,10 +74,9 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'django.forms',
|
'django.forms',
|
||||||
'simple_history',
|
'simple_history', # 这个要放到最后,别特么瞎改顺序
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
@ -92,7 +97,6 @@ MIDDLEWARE = [
|
||||||
'simple_history.middleware.HistoryRequestMiddleware',
|
'simple_history.middleware.HistoryRequestMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'jumpserver.urls'
|
ROOT_URLCONF = 'jumpserver.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
|
@ -158,13 +162,14 @@ DATABASES = {
|
||||||
'OPTIONS': DB_OPTIONS
|
'OPTIONS': DB_OPTIONS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DB_CA_PATH = os.path.join(PROJECT_DIR, 'data', 'certs', 'db_ca.pem')
|
DB_CA_PATH = os.path.join(PROJECT_DIR, 'data', 'certs', 'db_ca.pem')
|
||||||
if CONFIG.DB_ENGINE.lower() == 'mysql':
|
if CONFIG.DB_ENGINE.lower() == 'mysql':
|
||||||
DB_OPTIONS['init_command'] = "SET sql_mode='STRICT_TRANS_TABLES'"
|
DB_OPTIONS['init_command'] = "SET sql_mode='STRICT_TRANS_TABLES'"
|
||||||
if os.path.isfile(DB_CA_PATH):
|
if os.path.isfile(DB_CA_PATH):
|
||||||
DB_OPTIONS['ssl'] = {'ca': DB_CA_PATH}
|
DB_OPTIONS['ssl'] = {'ca': DB_CA_PATH}
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
|
||||||
#
|
#
|
||||||
|
@ -234,7 +239,6 @@ EMAIL_RECIPIENT = CONFIG.EMAIL_RECIPIENT
|
||||||
EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL
|
EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL
|
||||||
EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS
|
EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS
|
||||||
|
|
||||||
|
|
||||||
# Custom User Auth model
|
# Custom User Auth model
|
||||||
AUTH_USER_MODEL = 'users.User'
|
AUTH_USER_MODEL = 'users.User'
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,15 @@ __all__ = (
|
||||||
|
|
||||||
|
|
||||||
class BackendListView(APIView):
|
class BackendListView(APIView):
|
||||||
|
permission_classes = [IsValidUser]
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
'name': backend,
|
'name': backend,
|
||||||
'name_display': backend.label
|
'name_display': backend.label
|
||||||
}
|
}
|
||||||
for backend in BACKEND.choices
|
for backend in BACKEND if backend.is_enable
|
||||||
if backend.is_enable
|
|
||||||
]
|
]
|
||||||
return Response(data=data)
|
return Response(data=data)
|
||||||
|
|
||||||
|
@ -91,7 +92,6 @@ class UserMsgSubscriptionViewSet(ListModelMixin,
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_test_messages(request):
|
def get_all_test_messages(request):
|
||||||
import textwrap
|
import textwrap
|
||||||
from ..notifications import Message
|
from ..notifications import Message
|
||||||
|
@ -128,5 +128,3 @@ def get_all_test_messages(request):
|
||||||
<hr />
|
<hr />
|
||||||
""").format(msg_cls.__name__, msg_text)
|
""").format(msg_cls.__name__, msg_text)
|
||||||
return HttpResponse(html_data + text_data)
|
return HttpResponse(html_data + text_data)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ class CeleryTaskLogView(PermissionsMixin, TemplateView):
|
||||||
template_name = 'ops/celery_task_log.html'
|
template_name = 'ops/celery_task_log.html'
|
||||||
permission_classes = [RBACPermission]
|
permission_classes = [RBACPermission]
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'GET': 'view_tasklog'
|
'GET': 'ops.view_tasklog'
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
|
@ -56,7 +56,7 @@ class MyGrantedApplicationSystemUsersApi(RoleUserMixin, GrantedApplicationSystem
|
||||||
@method_decorator(tmp_to_root_org(), name='get')
|
@method_decorator(tmp_to_root_org(), name='get')
|
||||||
class ValidateUserApplicationPermissionApi(APIView):
|
class ValidateUserApplicationPermissionApi(APIView):
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'GET': 'view_applicationpermission'
|
'GET': 'ops.view_applicationpermission'
|
||||||
}
|
}
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
|
@ -123,7 +123,7 @@ class UserGroupGrantedNodeAssetsApi(ListAPIView):
|
||||||
class UserGroupGrantedNodesApi(ListAPIView):
|
class UserGroupGrantedNodesApi(ListAPIView):
|
||||||
serializer_class = serializers.NodeGrantedSerializer
|
serializer_class = serializers.NodeGrantedSerializer
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'list': 'view_userassets'
|
'list': 'perms.view_userassets'
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -137,7 +137,7 @@ class UserGroupGrantedNodesApi(ListAPIView):
|
||||||
|
|
||||||
class UserGroupGrantedNodeChildrenAsTreeApi(SerializeToTreeNodeMixin, ListAPIView):
|
class UserGroupGrantedNodeChildrenAsTreeApi(SerializeToTreeNodeMixin, ListAPIView):
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'list': 'view_userassets'
|
'list': 'perms.view_userassets'
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_children_nodes(self, parent_key):
|
def get_children_nodes(self, parent_key):
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.db.models import F
|
from django.db.models import F, TextChoices
|
||||||
|
|
||||||
from common.db.models import TextChoices
|
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
from common.db import models
|
from common.db import models
|
||||||
from common.utils import lazyproperty
|
from common.utils import lazyproperty
|
||||||
|
|
|
@ -5,3 +5,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
class RBACConfig(AppConfig):
|
class RBACConfig(AppConfig):
|
||||||
name = 'rbac'
|
name = 'rbac'
|
||||||
verbose_name = _('RBAC')
|
verbose_name = _('RBAC')
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
from . import signal_handlers
|
||||||
|
super().ready()
|
||||||
|
|
|
@ -64,11 +64,11 @@ class PredefineRole:
|
||||||
}
|
}
|
||||||
return defaults
|
return defaults
|
||||||
|
|
||||||
def get_or_create_role(self):
|
def update_or_create_role(self):
|
||||||
from rbac.models import Role
|
from rbac.models import Role
|
||||||
defaults = self._get_defaults()
|
defaults = self._get_defaults()
|
||||||
permissions = defaults.pop('permissions', [])
|
permissions = defaults.pop('permissions', [])
|
||||||
role, created = Role.objects.get_or_create(defaults, id=self.id)
|
role, created = Role.objects.update_or_create(defaults, id=self.id)
|
||||||
role.permissions.set(permissions)
|
role.permissions.set(permissions)
|
||||||
return role, created
|
return role, created
|
||||||
|
|
||||||
|
@ -125,10 +125,10 @@ class BuiltinRole:
|
||||||
return mapper[name].get_role()
|
return mapper[name].get_role()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def sync_to_db(cls):
|
def sync_to_db(cls, show_msg=False):
|
||||||
roles = cls.get_roles()
|
roles = cls.get_roles()
|
||||||
|
|
||||||
for pre_role in roles.values():
|
for pre_role in roles.values():
|
||||||
role, created = pre_role.get_or_create_role()
|
role, created = pre_role.update_or_create_role()
|
||||||
print("Create builtin Role: {} - {}".format(role.name, created))
|
if show_msg:
|
||||||
|
print("Update builtin Role: {} - {}".format(role.name, created))
|
||||||
|
|
|
@ -5,7 +5,7 @@ from rbac.builtin import BuiltinRole
|
||||||
|
|
||||||
|
|
||||||
def create_builtin_roles(apps, schema_editor):
|
def create_builtin_roles(apps, schema_editor):
|
||||||
BuiltinRole.sync_to_db()
|
BuiltinRole.sync_to_db(show_msg=True)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import uuid
|
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from django.db import models
|
|
||||||
from django.db.models import F, Count, Q
|
from django.db.models import F, Count, Q
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||||
|
|
|
@ -49,10 +49,10 @@ class Role(JMSModel):
|
||||||
return '%s(%s)' % (self.name, self.get_scope_display())
|
return '%s(%s)' % (self.name, self.get_scope_display())
|
||||||
|
|
||||||
def is_system_admin(self):
|
def is_system_admin(self):
|
||||||
return self.id == self.BuiltinRole.system_admin.id and self.builtin
|
return str(self.id) == self.BuiltinRole.system_admin.id and self.builtin
|
||||||
|
|
||||||
def is_org_admin(self):
|
def is_org_admin(self):
|
||||||
return self.id == self.BuiltinRole.org_admin.id and self.builtin
|
return str(self.id) == self.BuiltinRole.org_admin.id and self.builtin
|
||||||
|
|
||||||
def is_admin(self):
|
def is_admin(self):
|
||||||
yes = self.is_system_admin() or self.is_org_admin()
|
yes = self.is_system_admin() or self.is_org_admin()
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.db.models.signals import post_migrate
|
||||||
|
from django.apps import apps
|
||||||
|
|
||||||
|
from .builtin import BuiltinRole
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_migrate)
|
||||||
|
def after_migrate_update_builtin_role_permissions(sender, app_config, **kwargs):
|
||||||
|
# 最后一个 app migrations 后执行, 更新内置角色的权限
|
||||||
|
last_app = list(apps.get_app_configs())[-1]
|
||||||
|
if app_config.name == last_app.name:
|
||||||
|
print("After migration, update builtin role permissions")
|
||||||
|
BuiltinRole.sync_to_db()
|
|
@ -85,7 +85,7 @@ class TerminalViewSet(JMSBulkModelViewSet):
|
||||||
|
|
||||||
class TerminalConfig(APIView):
|
class TerminalConfig(APIView):
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'GET': 'view_terminalconfig'
|
'GET': 'terminal.view_terminalconfig'
|
||||||
}
|
}
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
|
|
|
@ -13,7 +13,7 @@ from django.core.cache import cache
|
||||||
from assets.models import Asset
|
from assets.models import Asset
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
from common.db.models import TextChoices
|
from django.db.models import TextChoices
|
||||||
from ..backends import get_multi_command_storage
|
from ..backends import get_multi_command_storage
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ from orgs.utils import current_org
|
||||||
from orgs.models import Organization
|
from orgs.models import Organization
|
||||||
from common.utils import date_expired_default, get_logger, lazyproperty, random_string
|
from common.utils import date_expired_default, get_logger, lazyproperty, random_string
|
||||||
from common import fields
|
from common import fields
|
||||||
from common.db.models import TextChoices
|
from django.db.models import TextChoices
|
||||||
from ..signals import post_user_change_password, post_user_leave_org, pre_user_leave_org
|
from ..signals import post_user_change_password, post_user_leave_org, pre_user_leave_org
|
||||||
|
|
||||||
__all__ = ['User', 'UserPasswordHistory']
|
__all__ = ['User', 'UserPasswordHistory']
|
||||||
|
|
|
@ -12,7 +12,7 @@ chardet==3.0.4
|
||||||
configparser==3.5.0
|
configparser==3.5.0
|
||||||
coreapi==2.3.3
|
coreapi==2.3.3
|
||||||
coreschema==0.0.4
|
coreschema==0.0.4
|
||||||
cryptography==3.3.2
|
cryptography==36.0.1
|
||||||
decorator==4.1.2
|
decorator==4.1.2
|
||||||
Django==3.1.13
|
Django==3.1.13
|
||||||
django-auth-ldap==2.2.0
|
django-auth-ldap==2.2.0
|
||||||
|
@ -52,8 +52,8 @@ passlib==1.7.1
|
||||||
Pillow==8.3.2
|
Pillow==8.3.2
|
||||||
pyasn1==0.4.8
|
pyasn1==0.4.8
|
||||||
pycparser==2.19
|
pycparser==2.19
|
||||||
pycryptodome==3.10.1
|
pycryptodome==3.12.0
|
||||||
pycryptodomex==3.10.1
|
pycryptodomex==3.12.0
|
||||||
pyotp==2.2.6
|
pyotp==2.2.6
|
||||||
PyNaCl==1.2.1
|
PyNaCl==1.2.1
|
||||||
python-dateutil==2.8.2
|
python-dateutil==2.8.2
|
||||||
|
|
Loading…
Reference in New Issue