mirror of
https://github.com/jumpserver/jumpserver.git
synced 2026-03-18 11:02:09 +00:00
feat: Add permission check for reading account secrets based on system settings (#16337)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from django.conf import settings
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@@ -166,7 +167,7 @@ class AccountViewSet(OrgBulkModelViewSet):
|
||||
|
||||
class AccountSecretsViewSet(AccountRecordViewLogMixin, AccountViewSet):
|
||||
"""
|
||||
因为可能要导出所有账号,所以单独建立了一个 viewset
|
||||
因为可能要导出所有账号,所以单独建立了一个 viewset
|
||||
"""
|
||||
serializer_classes = {
|
||||
'default': serializers.AccountSecretSerializer,
|
||||
|
||||
@@ -81,4 +81,7 @@ class IntegrationApplicationViewSet(OrgBulkModelViewSet):
|
||||
remote_addr=get_request_ip(request), service=service.name, service_id=service.id,
|
||||
account=f'{account.name}({account.username})', asset=f'{asset.name}({asset.address})',
|
||||
)
|
||||
return Response(data={'id': request.user.id, 'secret': account.secret})
|
||||
|
||||
# 根据配置决定是否返回密码
|
||||
secret = account.secret if settings.SECURITY_ACCOUNT_SECRET_READ else None
|
||||
return Response(data={'id': request.user.id, 'secret': secret})
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_filters import rest_framework as drf_filters
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
|
||||
@@ -14,7 +14,7 @@ from accounts.models import Account, AccountTemplate, GatheredAccount
|
||||
from accounts.tasks import push_accounts_to_assets_task
|
||||
from assets.const import Category, AllTypes
|
||||
from assets.models import Asset
|
||||
from common.serializers import SecretReadableMixin, CommonBulkModelSerializer
|
||||
from common.serializers import SecretReadableMixin, SecretReadableCheckMixin, CommonBulkModelSerializer
|
||||
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField
|
||||
from common.utils import get_logger
|
||||
from .base import BaseAccountSerializer, AuthValidateMixin
|
||||
@@ -478,7 +478,7 @@ class AssetAccountBulkSerializer(
|
||||
return results
|
||||
|
||||
|
||||
class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
|
||||
class AccountSecretSerializer(SecretReadableCheckMixin, SecretReadableMixin, AccountSerializer):
|
||||
spec_info = serializers.DictField(label=_('Spec info'), read_only=True)
|
||||
|
||||
class Meta(AccountSerializer.Meta):
|
||||
@@ -491,9 +491,10 @@ class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
|
||||
exclude_backup_fields = [
|
||||
'passphrase', 'push_now', 'params', 'spec_info'
|
||||
]
|
||||
secret_fields = ['secret']
|
||||
|
||||
|
||||
class AccountHistorySerializer(serializers.ModelSerializer):
|
||||
class AccountHistorySerializer(SecretReadableCheckMixin, serializers.ModelSerializer):
|
||||
secret_type = LabeledChoiceField(choices=SecretType.choices, label=_('Secret type'))
|
||||
secret = serializers.CharField(label=_('Secret'), read_only=True)
|
||||
id = serializers.IntegerField(label=_('ID'), source='history_id', read_only=True)
|
||||
@@ -509,6 +510,7 @@ class AccountHistorySerializer(serializers.ModelSerializer):
|
||||
'history_user': {'label': _('User')},
|
||||
'history_date': {'label': _('Date')},
|
||||
}
|
||||
secret_fields = ['secret']
|
||||
|
||||
|
||||
class AccountTaskSerializer(serializers.Serializer):
|
||||
|
||||
@@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from accounts.models import AccountTemplate
|
||||
from common.serializers import SecretReadableMixin
|
||||
from common.serializers import SecretReadableMixin, SecretReadableCheckMixin
|
||||
from common.serializers.fields import ObjectRelatedField
|
||||
from .base import BaseAccountSerializer
|
||||
|
||||
@@ -62,10 +62,11 @@ class AccountDetailTemplateSerializer(AccountTemplateSerializer):
|
||||
fields = AccountTemplateSerializer.Meta.fields + ['spec_info']
|
||||
|
||||
|
||||
class AccountTemplateSecretSerializer(SecretReadableMixin, AccountDetailTemplateSerializer):
|
||||
class AccountTemplateSecretSerializer(SecretReadableCheckMixin, SecretReadableMixin, AccountDetailTemplateSerializer):
|
||||
class Meta(AccountDetailTemplateSerializer.Meta):
|
||||
fields = AccountDetailTemplateSerializer.Meta.fields
|
||||
extra_kwargs = {
|
||||
**AccountDetailTemplateSerializer.Meta.extra_kwargs,
|
||||
'secret': {'write_only': False},
|
||||
}
|
||||
secret_fields = ['secret']
|
||||
|
||||
@@ -30,12 +30,30 @@ __all__ = [
|
||||
"CommonSerializerMixin",
|
||||
"CommonBulkSerializerMixin",
|
||||
"SecretReadableMixin",
|
||||
"SecretReadableCheckMixin",
|
||||
"CommonModelSerializer",
|
||||
"CommonBulkModelSerializer",
|
||||
"ResourceLabelsMixin",
|
||||
]
|
||||
|
||||
|
||||
class SecretReadableCheckMixin(serializers.Serializer):
|
||||
"""
|
||||
根据 SECURITY_ACCOUNT_SECRET_READ 配置控制密码字段的可读性
|
||||
当配置为 False 时,密码字段返回 None
|
||||
"""
|
||||
|
||||
def to_representation(self, instance):
|
||||
ret = super().to_representation(instance)
|
||||
|
||||
if not settings.SECURITY_ACCOUNT_SECRET_READ:
|
||||
secret_fields = getattr(self.Meta, 'secret_fields', ['secret'])
|
||||
for field_name in secret_fields:
|
||||
if field_name in ret:
|
||||
ret[field_name] = '<REDACTED>'
|
||||
return ret
|
||||
|
||||
|
||||
class SecretReadableMixin(serializers.Serializer):
|
||||
"""加密字段 (EncryptedField) 可读性"""
|
||||
|
||||
|
||||
@@ -1636,7 +1636,8 @@
|
||||
"setVariable": "Set variable",
|
||||
"userId": "User ID",
|
||||
"userName": "User name",
|
||||
"AccountSecretReadDisabled": "Account secret reading has been disabled by administrator",
|
||||
"AccessToken": "Access tokens",
|
||||
"AccessTokenTip": "Access Token is a temporary credential generated through the OAuth2 (Authorization Code Grant) flow using the JumpServer client, which is used to access protected resources.",
|
||||
"Revoke": "Revoke"
|
||||
}
|
||||
}
|
||||
@@ -1648,5 +1648,6 @@
|
||||
"selectFiles": "已选择选择{number}文件",
|
||||
"AccessToken": "访问令牌",
|
||||
"AccessTokenTip": "访问令牌是通过 JumpServer 客户端使用 OAuth2(授权码授权)流程生成的临时凭证,用于访问受保护的资源。",
|
||||
"Revoke": "撤销"
|
||||
}
|
||||
"Revoke": "撤销",
|
||||
"AccountSecretReadDisabled": "账号密码读取功能已被管理员禁用"
|
||||
}
|
||||
@@ -575,6 +575,7 @@ class Config(dict):
|
||||
],
|
||||
'SECURITY_SERVICE_ACCOUNT_REGISTRATION': 'auto',
|
||||
'SECURITY_VIEW_AUTH_NEED_MFA': True,
|
||||
'SECURITY_ACCOUNT_SECRET_READ': True,
|
||||
'SECURITY_MAX_IDLE_TIME': 30,
|
||||
'SECURITY_MAX_SESSION_TIME': 24,
|
||||
'SECURITY_PASSWORD_EXPIRATION_TIME': 9999,
|
||||
|
||||
@@ -60,6 +60,7 @@ VERIFY_CODE_TTL = CONFIG.VERIFY_CODE_TTL
|
||||
SECURITY_MFA_VERIFY_TTL = CONFIG.SECURITY_MFA_VERIFY_TTL
|
||||
SECURITY_UNCOMMON_USERS_TTL = CONFIG.SECURITY_UNCOMMON_USERS_TTL
|
||||
SECURITY_VIEW_AUTH_NEED_MFA = CONFIG.SECURITY_VIEW_AUTH_NEED_MFA
|
||||
SECURITY_ACCOUNT_SECRET_READ = CONFIG.SECURITY_ACCOUNT_SECRET_READ
|
||||
SECURITY_SERVICE_ACCOUNT_REGISTRATION = CONFIG.SECURITY_SERVICE_ACCOUNT_REGISTRATION
|
||||
SECURITY_LOGIN_CAPTCHA_ENABLED = CONFIG.SECURITY_LOGIN_CAPTCHA_ENABLED
|
||||
SECURITY_MFA_IN_LOGIN_PAGE = CONFIG.SECURITY_MFA_IN_LOGIN_PAGE
|
||||
@@ -268,4 +269,4 @@ LOKI_BASE_URL = CONFIG.LOKI_BASE_URL
|
||||
TOOL_USER_ENABLED = CONFIG.TOOL_USER_ENABLED
|
||||
|
||||
SUGGESTION_LIMIT = CONFIG.SUGGESTION_LIMIT
|
||||
MCP_ENABLED = CONFIG.MCP_ENABLED
|
||||
MCP_ENABLED = CONFIG.MCP_ENABLED
|
||||
|
||||
@@ -21,6 +21,7 @@ class PrivateSettingSerializer(PublicSettingSerializer):
|
||||
TICKET_AUTHORIZE_DEFAULT_TIME_UNIT = serializers.CharField()
|
||||
AUTH_LDAP_SYNC_ORG_IDS = serializers.ListField()
|
||||
SECURITY_MAX_IDLE_TIME = serializers.IntegerField()
|
||||
SECURITY_ACCOUNT_SECRET_READ = serializers.BooleanField()
|
||||
SECURITY_VIEW_AUTH_NEED_MFA = serializers.BooleanField()
|
||||
SECURITY_MFA_AUTH = serializers.IntegerField()
|
||||
SECURITY_MFA_VERIFY_TTL = serializers.IntegerField()
|
||||
|
||||
Reference in New Issue
Block a user