From 2b5b4ad605397b0d5e649924d636c5d7fea7dfc4 Mon Sep 17 00:00:00 2001 From: "Jiangjie.Bai" Date: Fri, 28 Oct 2022 15:01:17 +0800 Subject: [PATCH 1/2] =?UTF-8?q?refactor:=20ConnectionToken=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E8=A1=A8=E5=AD=97=E6=AE=B5=E5=90=8D=E7=A7=B0=20accoun?= =?UTF-8?q?t=20->=20account=5Fusername?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0012_auto_20220816_1629.py | 6 +-- .../authentication/models/connection_token.py | 37 +++++++------------ 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/apps/authentication/migrations/0012_auto_20220816_1629.py b/apps/authentication/migrations/0012_auto_20220816_1629.py index 23bcccb68..8310c4fbb 100644 --- a/apps/authentication/migrations/0012_auto_20220816_1629.py +++ b/apps/authentication/migrations/0012_auto_20220816_1629.py @@ -16,9 +16,9 @@ def migrate_system_user_to_account(apps, schema_editor): count += len(connection_tokens) updated = [] 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) - connection_token_model.objects.bulk_update(updated, ['account']) + connection_token_model.objects.bulk_update(updated, ['account_username']) class Migration(migrations.Migration): @@ -42,7 +42,7 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name='connectiontoken', - name='account', + name='account_username', field=models.CharField(default='', max_length=128, verbose_name='Account'), ), migrations.RunPython(migrate_system_user_to_account), diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index 9476d348c..7bf66a2ee 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -25,12 +25,12 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel): 'assets.Asset', on_delete=models.SET_NULL, null=True, blank=True, 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( 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")) date_expired = models.DateTimeField( default=date_expired_default, verbose_name=_("Date expired") @@ -43,6 +43,10 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel): ('view_connectiontokensecret', _('Can view connection token secret')) ] + @property + def is_valid(self): + return not self.is_expired + @property def is_expired(self): return self.date_expired < timezone.now() @@ -55,10 +59,6 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel): seconds = 0 return int(seconds) - @property - def is_valid(self): - return not self.is_expired - @classmethod def get_default_date_expired(cls): return date_expired_default() @@ -81,30 +81,21 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel): is_valid = False error = _('Connection token expired at: {}').format(as_current_tz(self.date_expired)) return is_valid, error - if not self.user: + if not self.user or not self.user.is_valid: is_valid = False - error = _('User not exists') + error = _('No user or invalid user') return is_valid, error - if not self.user.is_valid: + if not self.asset or self.asset.is_active: is_valid = False - error = _('User invalid, disabled or expired') - return is_valid, error - if not self.asset: - is_valid = False - error = _('Asset not exists') - return is_valid, error - if not self.asset.is_active: - is_valid = False - error = _('Asset inactive') + error = _('No asset or inactive asset') return is_valid, error if not self.account: is_valid = False - error = _('Account not exists') + error = _('No account') return is_valid, error - actions, expire_at = PermAccountUtil().validate_permission( - self.user, self.asset, self.account - ) + account_util = PermAccountUtil() + actions, expire_at = account_util.validate_permission(self.user, self.asset, self.account) if not actions or expire_at < time.time(): is_valid = False error = _('User has no permission to access asset or permission expired') From bcd1d5585b154e6092128affa4ceb04dec9117e8 Mon Sep 17 00:00:00 2001 From: "Jiangjie.Bai" Date: Fri, 28 Oct 2022 15:58:05 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20ConnectionToken=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=20Model=20=E5=92=8C=E5=BA=8F=E5=88=97=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../authentication/models/connection_token.py | 15 ++++- .../serializers/connection_token.py | 62 +++++++++++-------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index 7bf66a2ee..c8fae3790 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -75,7 +75,7 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel): # actions 和 expired_at 在 check_valid() 中赋值 actions = expire_at = None - def check_valid(self): + def check_permission(self): from perms.utils.account import PermAccountUtil if self.is_expired: is_valid = False @@ -89,13 +89,15 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel): is_valid = False error = _('No asset or inactive asset') return is_valid, error - if not self.account: + if not self.account_username: is_valid = False error = _('No account') return is_valid, error account_util = PermAccountUtil() - actions, expire_at = account_util.validate_permission(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(): is_valid = False error = _('User has no permission to access asset or permission expired') @@ -104,6 +106,13 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel): self.expire_at = expire_at 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 def domain(self): domain = self.asset.domain if self.asset else None diff --git a/apps/authentication/serializers/connection_token.py b/apps/authentication/serializers/connection_token.py index 86388155b..8f36ddc2b 100644 --- a/apps/authentication/serializers/connection_token.py +++ b/apps/authentication/serializers/connection_token.py @@ -5,7 +5,7 @@ from orgs.mixins.serializers import OrgResourceModelSerializerMixin from authentication.models import ConnectionToken from common.utils import pretty_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 perms.serializers.permission import ActionsField @@ -24,34 +24,33 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin): model = ConnectionToken fields_mini = ['id'] 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', ] fields_fk = [ - 'user', 'system_user', 'asset', + 'user', 'asset', ] read_only_fields = [ # 普通 Token 不支持指定 user 'user', 'is_valid', 'expire_time', - 'user_display', 'system_user_display', - 'asset_display', + 'user_display', 'asset_display', ] 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): fields_attrs = self.construct_internal_fields_attrs(attrs) attrs.update(fields_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): asset = attrs.get('asset') or '' asset_display = pretty_string(str(asset), max_length=128) @@ -63,8 +62,7 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin): if not isinstance(asset, Asset): error = '' raise serializers.ValidationError(error) - - return { + attrs = { 'user': user, 'secret': secret, 'user_display': user_display, @@ -72,6 +70,7 @@ class ConnectionTokenSerializer(OrgResourceModelSerializerMixin): 'date_expired': date_expired, 'org_id': org_id, } + return attrs class ConnectionTokenDisplaySerializer(ConnectionTokenSerializer): @@ -95,7 +94,7 @@ class SuperConnectionTokenSerializer(ConnectionTokenSerializer): ] 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): + """ User """ class Meta: model = User fields = ['id', 'name', 'username', 'email'] class ConnectionTokenAssetSerializer(serializers.ModelSerializer): - + """ Asset """ class Meta: model = Asset 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): + """ Gateway """ class Meta: model = Gateway 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): + """ Domain """ gateways = ConnectionTokenGatewaySerializer(many=True, read_only=True) class Meta: @@ -137,6 +142,7 @@ class ConnectionTokenDomainSerializer(serializers.ModelSerializer): class ConnectionTokenCmdFilterRuleSerializer(serializers.ModelSerializer): + """ Command filter rule """ class Meta: model = CommandFilterRule fields = [ @@ -148,7 +154,7 @@ class ConnectionTokenCmdFilterRuleSerializer(serializers.ModelSerializer): class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin): user = ConnectionTokenUserSerializer(read_only=True) asset = ConnectionTokenAssetSerializer(read_only=True) - remote_app = ConnectionTokenRemoteAppSerializer(read_only=True) + account = ConnectionTokenAccountSerializer(read_only=True) gateway = ConnectionTokenGatewaySerializer(read_only=True) domain = ConnectionTokenDomainSerializer(read_only=True) cmd_filter_rules = ConnectionTokenCmdFilterRuleSerializer(many=True) @@ -158,6 +164,8 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin): class Meta: model = ConnectionToken fields = [ - 'id', 'secret', 'type', 'user', 'asset', 'account', 'protocol', - 'cmd_filter_rules', 'domain', 'gateway', 'actions', 'expired_at', + 'id', 'secret', + 'user', 'asset', 'account_username', 'account', 'protocol', + 'domain', 'gateway', 'cmd_filter_rules', + 'actions', 'expired_at', ]