Merge branch 'v3' of github.com:jumpserver/jumpserver into v3

pull/8997/head
ibuler 2022-10-28 16:26:00 +08:00
commit 994a484adb
3 changed files with 62 additions and 54 deletions

View File

@ -16,9 +16,9 @@ def migrate_system_user_to_account(apps, schema_editor):
count += len(connection_tokens) count += len(connection_tokens)
updated = [] updated = []
for connection_token in connection_tokens: for connection_token in connection_tokens:
connection_token.account = connection_token.system_user.username connection_token.account_username = connection_token.system_user.username
updated.append(connection_token) updated.append(connection_token)
connection_token_model.objects.bulk_update(updated, ['account']) connection_token_model.objects.bulk_update(updated, ['account_username'])
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -42,7 +42,7 @@ class Migration(migrations.Migration):
), ),
migrations.AddField( migrations.AddField(
model_name='connectiontoken', model_name='connectiontoken',
name='account', name='account_username',
field=models.CharField(default='', max_length=128, verbose_name='Account'), field=models.CharField(default='', max_length=128, verbose_name='Account'),
), ),
migrations.RunPython(migrate_system_user_to_account), migrations.RunPython(migrate_system_user_to_account),

View File

@ -25,12 +25,12 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
'assets.Asset', on_delete=models.SET_NULL, null=True, blank=True, 'assets.Asset', on_delete=models.SET_NULL, null=True, blank=True,
related_name='connection_tokens', verbose_name=_('Asset'), related_name='connection_tokens', verbose_name=_('Asset'),
) )
user_display = models.CharField(max_length=128, default='', verbose_name=_("User display"))
asset_display = models.CharField(max_length=128, default='', verbose_name=_("Asset display"))
account = models.CharField(max_length=128, default='', verbose_name=_("Account"))
protocol = models.CharField( protocol = models.CharField(
choices=Protocol.choices, max_length=16, default=Protocol.ssh, verbose_name=_("Protocol") choices=Protocol.choices, max_length=16, default=Protocol.ssh, verbose_name=_("Protocol")
) )
user_display = models.CharField(max_length=128, default='', verbose_name=_("User display"))
asset_display = models.CharField(max_length=128, default='', verbose_name=_("Asset display"))
account_username = models.CharField(max_length=128, default='', verbose_name=_("Account"))
secret = models.CharField(max_length=64, default='', verbose_name=_("Secret")) secret = models.CharField(max_length=64, default='', verbose_name=_("Secret"))
date_expired = models.DateTimeField( date_expired = models.DateTimeField(
default=date_expired_default, verbose_name=_("Date expired") default=date_expired_default, verbose_name=_("Date expired")
@ -43,6 +43,10 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
('view_connectiontokensecret', _('Can view connection token secret')) ('view_connectiontokensecret', _('Can view connection token secret'))
] ]
@property
def is_valid(self):
return not self.is_expired
@property @property
def is_expired(self): def is_expired(self):
return self.date_expired < timezone.now() return self.date_expired < timezone.now()
@ -55,10 +59,6 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
seconds = 0 seconds = 0
return int(seconds) return int(seconds)
@property
def is_valid(self):
return not self.is_expired
@classmethod @classmethod
def get_default_date_expired(cls): def get_default_date_expired(cls):
return date_expired_default() return date_expired_default()
@ -75,35 +75,28 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
# actions 和 expired_at 在 check_valid() 中赋值 # actions 和 expired_at 在 check_valid() 中赋值
actions = expire_at = None actions = expire_at = None
def check_valid(self): def check_permission(self):
from perms.utils.account import PermAccountUtil from perms.utils.account import PermAccountUtil
if self.is_expired: if self.is_expired:
is_valid = False is_valid = False
error = _('Connection token expired at: {}').format(as_current_tz(self.date_expired)) error = _('Connection token expired at: {}').format(as_current_tz(self.date_expired))
return is_valid, error return is_valid, error
if not self.user: if not self.user or not self.user.is_valid:
is_valid = False is_valid = False
error = _('User not exists') error = _('No user or invalid user')
return is_valid, error return is_valid, error
if not self.user.is_valid: if not self.asset or self.asset.is_active:
is_valid = False is_valid = False
error = _('User invalid, disabled or expired') error = _('No asset or inactive asset')
return is_valid, error return is_valid, error
if not self.asset: if not self.account_username:
is_valid = False is_valid = False
error = _('Asset not exists') error = _('No account')
return is_valid, error
if not self.asset.is_active:
is_valid = False
error = _('Asset inactive')
return is_valid, error
if not self.account:
is_valid = False
error = _('Account not exists')
return is_valid, error return is_valid, error
actions, expire_at = PermAccountUtil().validate_permission( account_util = PermAccountUtil()
self.user, self.asset, self.account actions, expire_at = account_util.validate_permission(
self.user, self.asset, self.account_username
) )
if not actions or expire_at < time.time(): if not actions or expire_at < time.time():
is_valid = False is_valid = False
@ -113,6 +106,13 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
self.expire_at = expire_at self.expire_at = expire_at
return True, '' return True, ''
@lazyproperty
def account(self):
if not self.asset:
return None
account = self.asset.accounts.filter(username=self.account_username).first()
return account
@lazyproperty @lazyproperty
def domain(self): def domain(self):
domain = self.asset.domain if self.asset else None domain = self.asset.domain if self.asset else None

View File

@ -5,7 +5,7 @@ from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from authentication.models import ConnectionToken from authentication.models import ConnectionToken
from common.utils import pretty_string from common.utils import pretty_string
from common.utils.random import random_string from common.utils.random import random_string
from assets.models import Asset, Gateway, Domain, CommandFilterRule from assets.models import Asset, Gateway, Domain, CommandFilterRule, Account
from users.models import User from users.models import User
from perms.serializers.permission import ActionsField from perms.serializers.permission import ActionsField
@ -24,34 +24,33 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
model = ConnectionToken model = ConnectionToken
fields_mini = ['id'] fields_mini = ['id']
fields_small = fields_mini + [ fields_small = fields_mini + [
'secret', 'date_expired', 'date_created', 'date_updated', 'secret', 'account_username', 'date_expired',
'date_created', 'date_updated',
'created_by', 'updated_by', 'org_id', 'org_name', 'created_by', 'updated_by', 'org_id', 'org_name',
] ]
fields_fk = [ fields_fk = [
'user', 'system_user', 'asset', 'user', 'asset',
] ]
read_only_fields = [ read_only_fields = [
# 普通 Token 不支持指定 user # 普通 Token 不支持指定 user
'user', 'is_valid', 'expire_time', 'user', 'is_valid', 'expire_time',
'user_display', 'system_user_display', 'user_display', 'asset_display',
'asset_display',
] ]
fields = fields_small + fields_fk + read_only_fields fields = fields_small + fields_fk + read_only_fields
def get_request_user(self):
request = self.context.get('request')
user = request.user if request else None
return user
def get_user(self, attrs):
return self.get_request_user()
def validate(self, attrs): def validate(self, attrs):
fields_attrs = self.construct_internal_fields_attrs(attrs) fields_attrs = self.construct_internal_fields_attrs(attrs)
attrs.update(fields_attrs) attrs.update(fields_attrs)
return attrs return attrs
@property
def request_user(self):
request = self.context.get('request')
if request:
return request.user
def get_user(self, attrs):
return self.request_user
def construct_internal_fields_attrs(self, attrs): def construct_internal_fields_attrs(self, attrs):
asset = attrs.get('asset') or '' asset = attrs.get('asset') or ''
asset_display = pretty_string(str(asset), max_length=128) asset_display = pretty_string(str(asset), max_length=128)
@ -63,8 +62,7 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
if not isinstance(asset, Asset): if not isinstance(asset, Asset):
error = '' error = ''
raise serializers.ValidationError(error) raise serializers.ValidationError(error)
attrs = {
return {
'user': user, 'user': user,
'secret': secret, 'secret': secret,
'user_display': user_display, 'user_display': user_display,
@ -72,6 +70,7 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
'date_expired': date_expired, 'date_expired': date_expired,
'org_id': org_id, 'org_id': org_id,
} }
return attrs
class ConnectionTokenDisplaySerializer(ConnectionTokenSerializer): class ConnectionTokenDisplaySerializer(ConnectionTokenSerializer):
@ -95,7 +94,7 @@ class SuperConnectionTokenSerializer(ConnectionTokenSerializer):
] ]
def get_user(self, attrs): def get_user(self, attrs):
return attrs.get('user') or self.request_user return attrs.get('user') or self.get_request_user()
# #
@ -104,31 +103,37 @@ class SuperConnectionTokenSerializer(ConnectionTokenSerializer):
class ConnectionTokenUserSerializer(serializers.ModelSerializer): class ConnectionTokenUserSerializer(serializers.ModelSerializer):
""" User """
class Meta: class Meta:
model = User model = User
fields = ['id', 'name', 'username', 'email'] fields = ['id', 'name', 'username', 'email']
class ConnectionTokenAssetSerializer(serializers.ModelSerializer): class ConnectionTokenAssetSerializer(serializers.ModelSerializer):
""" Asset """
class Meta: class Meta:
model = Asset model = Asset
fields = ['id', 'name', 'ip', 'protocols', 'org_id'] fields = ['id', 'name', 'ip', 'protocols', 'org_id']
class ConnectionTokenAccountSerializer(serializers.ModelSerializer):
""" Account """
class Meta:
model = Account
fields = [
'id', 'name', 'username', 'secret_type', 'secret', 'version'
]
class ConnectionTokenGatewaySerializer(serializers.ModelSerializer): class ConnectionTokenGatewaySerializer(serializers.ModelSerializer):
""" Gateway """
class Meta: class Meta:
model = Gateway model = Gateway
fields = ['id', 'ip', 'port', 'username', 'password', 'private_key'] fields = ['id', 'ip', 'port', 'username', 'password', 'private_key']
class ConnectionTokenRemoteAppSerializer(serializers.Serializer):
program = serializers.CharField(allow_null=True, allow_blank=True)
working_directory = serializers.CharField(allow_null=True, allow_blank=True)
parameters = serializers.CharField(allow_null=True, allow_blank=True)
class ConnectionTokenDomainSerializer(serializers.ModelSerializer): class ConnectionTokenDomainSerializer(serializers.ModelSerializer):
""" Domain """
gateways = ConnectionTokenGatewaySerializer(many=True, read_only=True) gateways = ConnectionTokenGatewaySerializer(many=True, read_only=True)
class Meta: class Meta:
@ -137,6 +142,7 @@ class ConnectionTokenDomainSerializer(serializers.ModelSerializer):
class ConnectionTokenCmdFilterRuleSerializer(serializers.ModelSerializer): class ConnectionTokenCmdFilterRuleSerializer(serializers.ModelSerializer):
""" Command filter rule """
class Meta: class Meta:
model = CommandFilterRule model = CommandFilterRule
fields = [ fields = [
@ -148,7 +154,7 @@ class ConnectionTokenCmdFilterRuleSerializer(serializers.ModelSerializer):
class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin): class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
user = ConnectionTokenUserSerializer(read_only=True) user = ConnectionTokenUserSerializer(read_only=True)
asset = ConnectionTokenAssetSerializer(read_only=True) asset = ConnectionTokenAssetSerializer(read_only=True)
remote_app = ConnectionTokenRemoteAppSerializer(read_only=True) account = ConnectionTokenAccountSerializer(read_only=True)
gateway = ConnectionTokenGatewaySerializer(read_only=True) gateway = ConnectionTokenGatewaySerializer(read_only=True)
domain = ConnectionTokenDomainSerializer(read_only=True) domain = ConnectionTokenDomainSerializer(read_only=True)
cmd_filter_rules = ConnectionTokenCmdFilterRuleSerializer(many=True) cmd_filter_rules = ConnectionTokenCmdFilterRuleSerializer(many=True)
@ -158,6 +164,8 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
class Meta: class Meta:
model = ConnectionToken model = ConnectionToken
fields = [ fields = [
'id', 'secret', 'type', 'user', 'asset', 'account', 'protocol', 'id', 'secret',
'cmd_filter_rules', 'domain', 'gateway', 'actions', 'expired_at', 'user', 'asset', 'account_username', 'account', 'protocol',
'domain', 'gateway', 'cmd_filter_rules',
'actions', 'expired_at',
] ]