mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-15 08:32:48 +00:00
Compare commits
19 Commits
pr@dev@per
...
v2.20.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81a5febfbc | ||
|
|
a09a59e7b3 | ||
|
|
a7d0e3ce70 | ||
|
|
6f762d7198 | ||
|
|
ddbcbb0d66 | ||
|
|
a1976e4f0b | ||
|
|
0eeb1e6f6d | ||
|
|
ae5487720c | ||
|
|
0d8386359e | ||
|
|
d338e53862 | ||
|
|
441c6a7f60 | ||
|
|
79a40f7ded | ||
|
|
b9c221c856 | ||
|
|
f6dada03bf | ||
|
|
d3d18b8f48 | ||
|
|
a2f23c2d81 | ||
|
|
f0df23a15a | ||
|
|
a6a4bb1c7d | ||
|
|
9ac3c6b120 |
@@ -8,6 +8,7 @@ from django.conf import settings
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
from common.mixins import CommonModelMixin
|
||||
from common.tree import TreeNode
|
||||
from common.utils import is_uuid
|
||||
from assets.models import Asset, SystemUser
|
||||
|
||||
from ..utils import KubernetesTree
|
||||
@@ -100,6 +101,9 @@ class ApplicationTreeNodeMixin:
|
||||
type_category_mapper = const.AppType.type_category_mapper()
|
||||
types = const.AppType.type_category_mapper().keys()
|
||||
for tp in types:
|
||||
# TODO: Temporary exclude mongodb
|
||||
if tp == const.AppType.mongodb:
|
||||
continue
|
||||
if not settings.XPACK_ENABLED and const.AppType.is_xpack(tp):
|
||||
continue
|
||||
category = type_category_mapper.get(tp)
|
||||
@@ -264,12 +268,12 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
|
||||
'parameters': parameters
|
||||
}
|
||||
|
||||
def get_remote_app_asset(self):
|
||||
def get_remote_app_asset(self, raise_exception=True):
|
||||
asset_id = self.attrs.get('asset')
|
||||
if not asset_id:
|
||||
if is_uuid(asset_id):
|
||||
return Asset.objects.filter(id=asset_id).first()
|
||||
if raise_exception:
|
||||
raise ValueError("Remote App not has asset attr")
|
||||
asset = Asset.objects.filter(id=asset_id).first()
|
||||
return asset
|
||||
|
||||
|
||||
class ApplicationUser(SystemUser):
|
||||
|
||||
@@ -16,7 +16,7 @@ from perms.filters import AssetPermissionFilter
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from assets.api import FilterAssetByNodeMixin
|
||||
from ..models import Asset, Node, Platform
|
||||
from ..models import Asset, Node, Platform, Gateway
|
||||
from .. import serializers
|
||||
from ..tasks import (
|
||||
update_assets_hardware_info_manual, test_assets_connectivity_manual,
|
||||
@@ -181,7 +181,7 @@ class AssetsTaskCreateApi(AssetsTaskMixin, generics.CreateAPIView):
|
||||
def check_permissions(self, request):
|
||||
action = request.data.get('action')
|
||||
action_perm_require = {
|
||||
'refresh': 'assets.refresh_assethardwareinfo1',
|
||||
'refresh': 'assets.refresh_assethardwareinfo',
|
||||
}
|
||||
perm_required = action_perm_require.get(action)
|
||||
has = self.request.user.has_perm(perm_required)
|
||||
@@ -199,7 +199,7 @@ class AssetGatewayListApi(generics.ListAPIView):
|
||||
asset_id = self.kwargs.get('pk')
|
||||
asset = get_object_or_404(Asset, pk=asset_id)
|
||||
if not asset.domain:
|
||||
return []
|
||||
return Gateway.objects.none()
|
||||
queryset = asset.domain.gateways.filter(protocol='ssh')
|
||||
return queryset
|
||||
|
||||
|
||||
@@ -133,6 +133,14 @@ class AuthMixin:
|
||||
self.password = password
|
||||
|
||||
def load_app_more_auth(self, app_id=None, username=None, user_id=None):
|
||||
from applications.models import Application
|
||||
app = get_object_or_none(Application, pk=app_id)
|
||||
if app and app.category_remote_app:
|
||||
# Remote app
|
||||
self._load_remoteapp_more_auth(app, username, user_id)
|
||||
return
|
||||
|
||||
# Other app
|
||||
self._clean_auth_info_if_manual_login_mode()
|
||||
# 加载临时认证信息
|
||||
if self.login_mode == self.LOGIN_MANUAL:
|
||||
@@ -148,6 +156,11 @@ class AuthMixin:
|
||||
_username = username
|
||||
self.username = _username
|
||||
|
||||
def _load_remoteapp_more_auth(self, app, username, user_id):
|
||||
asset = app.get_remote_app_asset(raise_exception=False)
|
||||
if asset:
|
||||
self.load_asset_more_auth(asset_id=asset.id, username=username, user_id=user_id)
|
||||
|
||||
def load_asset_special_auth(self, asset, username=''):
|
||||
"""
|
||||
AuthBook 的数据状态
|
||||
|
||||
@@ -8,7 +8,6 @@ from .. import serializers
|
||||
|
||||
|
||||
class AccessKeyViewSet(ModelViewSet):
|
||||
permission_classes = (IsValidUser,)
|
||||
serializer_class = serializers.AccessKeySerializer
|
||||
search_fields = ['^id', '^secret']
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:449810c3661c09f6448b9c67e7a193f303a3bef7ccc3d0f1efe6e099804e782a
|
||||
oid sha256:b1c6c0f9212f9d154a432d93785677ebc206eed4fd4338d3fe11b4b528d65c11
|
||||
size 104323
|
||||
|
||||
@@ -2050,7 +2050,7 @@ msgstr "请修改密码"
|
||||
|
||||
#: authentication/models.py:33 terminal/serializers/storage.py:30
|
||||
msgid "Access key"
|
||||
msgstr "Api key"
|
||||
msgstr "API key"
|
||||
|
||||
#: authentication/models.py:40
|
||||
msgid "Private Token"
|
||||
|
||||
@@ -16,7 +16,7 @@ class RBACBackend(JMSBaseAuthBackend):
|
||||
return False
|
||||
|
||||
def has_perm(self, user_obj, perm, obj=None):
|
||||
if not user_obj.is_active:
|
||||
if not user_obj.is_active or not perm:
|
||||
raise PermissionDenied()
|
||||
if perm == '*':
|
||||
return True
|
||||
|
||||
@@ -22,7 +22,6 @@ exclude_permissions = (
|
||||
('common', 'setting', '*', '*'),
|
||||
|
||||
('authentication', 'privatetoken', '*', '*'),
|
||||
('authentication', 'accesskey', 'change,delete', 'accesskey'),
|
||||
('authentication', 'connectiontoken', 'change,delete', 'connectiontoken'),
|
||||
('authentication', 'ssotoken', '*', '*'),
|
||||
('authentication', 'superconnectiontoken', 'change,delete', 'superconnectiontoken'),
|
||||
@@ -49,6 +48,8 @@ exclude_permissions = (
|
||||
('rbac', 'contenttype', '*', '*'),
|
||||
('rbac', 'permission', 'add,delete,change', 'permission'),
|
||||
('rbac', 'rolebinding', '*', '*'),
|
||||
('rbac', 'systemrolebinding', 'change', 'systemrolebinding'),
|
||||
('rbac', 'orgrolebinding', 'change', 'orgrolebinding'),
|
||||
('rbac', 'role', '*', '*'),
|
||||
('ops', 'adhoc', 'delete,change', '*'),
|
||||
('ops', 'adhocexecution', 'add,delete,change', '*'),
|
||||
@@ -99,6 +100,7 @@ only_system_permissions = (
|
||||
('orgs', 'organization', '*', '*'),
|
||||
('xpack', 'license', '*', '*'),
|
||||
('settings', 'setting', '*', '*'),
|
||||
('tickets', '*', '*', '*'),
|
||||
('ops', 'task', 'view', 'taskmonitor'),
|
||||
('terminal', 'terminal', '*', '*'),
|
||||
('terminal', 'commandstorage', '*', '*'),
|
||||
@@ -106,6 +108,7 @@ only_system_permissions = (
|
||||
('terminal', 'status', '*', '*'),
|
||||
('terminal', 'task', '*', '*'),
|
||||
('authentication', '*', '*', '*'),
|
||||
('tickets', '*', '*', '*'),
|
||||
)
|
||||
|
||||
only_org_permissions = (
|
||||
|
||||
@@ -93,7 +93,7 @@ class RBACPermission(permissions.DjangoModelPermissions):
|
||||
try:
|
||||
queryset = self._queryset(view)
|
||||
model_cls = queryset.model
|
||||
except AssertionError:
|
||||
except:
|
||||
model_cls = None
|
||||
return model_cls
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@ special_pid_mapper = {
|
||||
'terminal.status': 'terminal_node',
|
||||
'terminal.task': 'terminal_node',
|
||||
'audits.ftplog': 'terminal',
|
||||
'rbac.menupermission': 'view_other',
|
||||
'perms.view_myassets': 'my_assets',
|
||||
'perms.view_myapps': 'my_apps',
|
||||
'ops.add_commandexecution': 'view_workspace',
|
||||
|
||||
@@ -6,7 +6,7 @@ from common.exceptions import JMSException
|
||||
from common.utils import lazyproperty
|
||||
from rbac.permissions import RBACPermission
|
||||
from tickets import serializers
|
||||
from tickets.models import Ticket
|
||||
from tickets.models import Ticket, Comment
|
||||
from tickets.permissions.comment import IsAssignee, IsApplicant, IsSwagger
|
||||
|
||||
|
||||
@@ -36,5 +36,7 @@ class CommentViewSet(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
if getattr(self, 'swagger_fake_view', False):
|
||||
return Comment.objects.none()
|
||||
queryset = self.ticket.comments.all()
|
||||
return queryset
|
||||
|
||||
@@ -108,6 +108,9 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelV
|
||||
self.check_object_permissions(self.request, user)
|
||||
return super().perform_bulk_update(serializer)
|
||||
|
||||
def allow_bulk_destroy(self, qs, filtered):
|
||||
return filtered.count() < qs.count()
|
||||
|
||||
def perform_bulk_destroy(self, objects):
|
||||
for obj in objects:
|
||||
self.check_object_permissions(self.request, obj)
|
||||
|
||||
@@ -224,6 +224,8 @@ class RoleManager(models.Manager):
|
||||
RoleBinding.objects.bulk_create(items, ignore_conflicts=True)
|
||||
except Exception as e:
|
||||
logger.error('Create role binding error: {}'.format(e))
|
||||
finally:
|
||||
self.user.expire_users_rbac_perms_cache()
|
||||
|
||||
def set(self, roles):
|
||||
self.clear()
|
||||
@@ -237,16 +239,16 @@ class RoleManager(models.Manager):
|
||||
|
||||
class OrgRoleManager(RoleManager):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
from rbac.const import Scope
|
||||
self.scope = Scope.org
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class SystemRoleManager(RoleManager):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
from rbac.const import Scope
|
||||
self.scope = Scope.system
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class RoleMixin:
|
||||
@@ -257,6 +259,8 @@ class RoleMixin:
|
||||
_org_roles = None
|
||||
_system_roles = None
|
||||
PERM_CACHE_KEY = 'USER_PERMS_{}_{}'
|
||||
_is_superuser = None
|
||||
_update_superuser = False
|
||||
|
||||
@lazyproperty
|
||||
def roles(self):
|
||||
@@ -288,17 +292,25 @@ class RoleMixin:
|
||||
key = cls.PERM_CACHE_KEY.format('*', '*')
|
||||
cache.delete_pattern(key)
|
||||
|
||||
@lazyproperty
|
||||
@property
|
||||
def is_superuser(self):
|
||||
"""
|
||||
由于这里用了 cache ,所以不能改成 self.system_roles.filter().exists() 会查询的
|
||||
"""
|
||||
if self._is_superuser is not None:
|
||||
return self._is_superuser
|
||||
from rbac.builtin import BuiltinRole
|
||||
# return self.system_roles.all().filter(id=BuiltinRole.system_admin.id).exists()
|
||||
ids = [str(r.id) for r in self.system_roles.all()]
|
||||
yes = BuiltinRole.system_admin.id in ids
|
||||
self._is_superuser = yes
|
||||
return yes
|
||||
|
||||
@is_superuser.setter
|
||||
def is_superuser(self, value):
|
||||
self._is_superuser = value
|
||||
self._update_superuser = True
|
||||
|
||||
@lazyproperty
|
||||
def is_org_admin(self):
|
||||
from rbac.builtin import BuiltinRole
|
||||
|
||||
@@ -49,6 +49,8 @@ class RolesSerializerMixin(serializers.Serializer):
|
||||
return fields
|
||||
|
||||
action = view.action or 'list'
|
||||
if action in ('partial_bulk_update', 'bulk_update', 'partial_update', 'update'):
|
||||
action = 'create'
|
||||
model_cls_field_mapper = {
|
||||
SystemRoleBinding: ['system_roles', 'system_roles_display'],
|
||||
OrgRoleBinding: ['org_roles', 'system_roles_display']
|
||||
@@ -130,6 +132,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer
|
||||
'date_joined', 'last_login', 'created_by', 'is_first_login',
|
||||
'wecom_id', 'dingtalk_id', 'feishu_id'
|
||||
]
|
||||
disallow_self_update_fields = ['is_active']
|
||||
extra_kwargs = {
|
||||
'password': {'write_only': True, 'required': False, 'allow_null': True, 'allow_blank': True},
|
||||
'public_key': {'write_only': True},
|
||||
@@ -178,7 +181,23 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer
|
||||
attrs.pop(field, None)
|
||||
return attrs
|
||||
|
||||
def check_disallow_self_update_fields(self, attrs):
|
||||
request = self.context.get('request')
|
||||
if not request or not request.user.is_authenticated:
|
||||
return attrs
|
||||
if not self.instance:
|
||||
return attrs
|
||||
if request.user.id != self.instance.id:
|
||||
return attrs
|
||||
disallow_fields = set(list(attrs.keys())) & set(self.Meta.disallow_self_update_fields)
|
||||
if not disallow_fields:
|
||||
return attrs
|
||||
# 用户自己不能更新自己的一些字段
|
||||
error = 'User Cannot self-update fields: {}'.format(disallow_fields)
|
||||
raise serializers.ValidationError(error)
|
||||
|
||||
def validate(self, attrs):
|
||||
attrs = self.check_disallow_self_update_fields(attrs)
|
||||
attrs = self.change_password_to_raw(attrs)
|
||||
attrs = self.clean_auth_fields(attrs)
|
||||
attrs.pop('password_strategy', None)
|
||||
@@ -203,17 +222,6 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer
|
||||
field.set(value)
|
||||
return instance
|
||||
|
||||
def validate_is_active(self, is_active):
|
||||
request = self.context.get('request')
|
||||
if not request or not request.user.is_authenticated:
|
||||
return is_active
|
||||
|
||||
user = request.user
|
||||
if user.id == self.instance.id and not is_active:
|
||||
# 用户自己不能禁用启用自己
|
||||
raise serializers.ValidationError("Cannot inactive self")
|
||||
return is_active
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
save_handler = partial(super().update, instance)
|
||||
instance = self.save_and_set_custom_m2m_fields(validated_data, save_handler, created=False)
|
||||
|
||||
@@ -60,9 +60,30 @@ def save_passwd_change(sender, instance: User, **kwargs):
|
||||
)
|
||||
|
||||
|
||||
def update_role_superuser_if_need(user):
|
||||
if not user._update_superuser:
|
||||
return
|
||||
value = user._is_superuser
|
||||
from rbac.models import SystemRoleBinding
|
||||
from rbac.builtin import BuiltinRole
|
||||
role = BuiltinRole.system_admin.get_role()
|
||||
|
||||
kwargs = {'user_id': user.id, 'role_id': role.id, 'scope': 'system'}
|
||||
exists = SystemRoleBinding.objects.filter(**kwargs).exists()
|
||||
# 需要添加并且不存在
|
||||
if value and not exists:
|
||||
SystemRoleBinding.objects.create(**kwargs)
|
||||
# 需要删除并且存在
|
||||
elif not value and exists:
|
||||
SystemRoleBinding.objects.filter(**kwargs).delete()
|
||||
else:
|
||||
print("No need operate")
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
@on_transaction_commit
|
||||
def on_user_create_set_default_system_role(sender, instance, created, **kwargs):
|
||||
update_role_superuser_if_need(instance)
|
||||
if not created:
|
||||
return
|
||||
has_system_role = instance.system_roles.all().exists()
|
||||
|
||||
@@ -58,7 +58,7 @@ def clean_db_content_types():
|
||||
('perms', 'applicationpermission', 'view_permuserapplication'),
|
||||
('perms', 'assetpermission', 'view_permuserasset'),
|
||||
('perms', 'assetpermission', 'view_permusergroupasset'),
|
||||
|
||||
('rbac', 'menupermission', 'view_dashboard'),
|
||||
('applications', 'databaseapp', 'add_databaseapp'),
|
||||
('applications', 'databaseapp', 'change_databaseapp'),
|
||||
('applications', 'databaseapp', 'delete_databaseapp'),
|
||||
|
||||
Reference in New Issue
Block a user