From 914110b64cc4fa2aed9c665d7398d25009bd3268 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 12 Nov 2024 10:26:34 +0800 Subject: [PATCH] perf: Admin connection token --- .../migrations/0007_alter_accountrisk_risk.py | 22 ++++++------- apps/authentication/api/connection_token.py | 15 +++++++-- apps/authentication/const.py | 6 ++++ .../migrations/0004_connectiontoken_type.py | 30 ++++++++++++++++++ .../authentication/models/connection_token.py | 31 +++++++++++++++++++ apps/authentication/urls/api_urls.py | 1 + 6 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 apps/authentication/migrations/0004_connectiontoken_type.py diff --git a/apps/accounts/migrations/0007_alter_accountrisk_risk.py b/apps/accounts/migrations/0007_alter_accountrisk_risk.py index d806f7f3b..e5bc75ae4 100644 --- a/apps/accounts/migrations/0007_alter_accountrisk_risk.py +++ b/apps/accounts/migrations/0007_alter_accountrisk_risk.py @@ -15,17 +15,17 @@ class Migration(migrations.Migration): name="risk", field=models.CharField( choices=[ - ("zombie", "Long time no login"), - ("ghost", "Not managed"), - ("long_time_password", "Long time no change"), - ("weak_password", "Weak password"), - ("group_changed", "Group change"), - ("sudo_changed", "Sudo changed"), - ("account_deleted", "Account delete"), - ("password_expired", "Password expired"), - ("no_admin_account", "No admin account"), - ("password_error", "Password error"), - ("others", "Others"), + ('zombie', 'Long time no login'), + ('ghost', 'Not managed'), + ('long_time_password', 'Long time no change'), + ('weak_password', 'Weak password'), + ('password_error', 'Password error'), + ('password_expired', 'Password expired'), + ('group_changed', 'Group change'), + ('sudo_changed', 'Sudo changed'), + ('account_deleted', 'Account delete'), + ('no_admin_account', 'No admin account'), + ('others', 'Others') ], max_length=128, verbose_name="Risk", diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 37fb3e968..ef2c7ad2b 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -29,14 +29,14 @@ from terminal.models import EndpointRule, Endpoint from users.const import FileNameConflictResolution from users.const import RDPSmartSize, RDPColorQuality from users.models import Preference -from ..models import ConnectionToken, date_expired_default +from ..models import ConnectionToken, AdminConnectionToken, date_expired_default from ..serializers import ( ConnectionTokenSerializer, ConnectionTokenSecretSerializer, SuperConnectionTokenSerializer, ConnectTokenAppletOptionSerializer, ConnectionTokenReusableSerializer, ConnectTokenVirtualAppOptionSerializer ) -__all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet'] +__all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet', 'AdminConnectionTokenViewSet'] logger = get_logger(__name__) @@ -558,3 +558,14 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet): else: logger.error('Release applet account error: {}'.format(lock_key)) return Response({'error': 'not found or expired'}, status=400) + + +class AdminConnectionTokenViewSet(SuperConnectionTokenViewSet): + + def check_permissions(self, request): + user = request.user + if not user.is_superuser: + self.permission_denied(request) + + def get_queryset(self): + return AdminConnectionToken.objects.all() diff --git a/apps/authentication/const.py b/apps/authentication/const.py index 1e06a4d35..341a14e24 100644 --- a/apps/authentication/const.py +++ b/apps/authentication/const.py @@ -37,3 +37,9 @@ class MFAType(TextChoices): SMS = MFASms.name, MFASms.display_name Radius = MFARadius.name, MFARadius.display_name Custom = MFACustom.name, MFACustom.display_name + + +class ConnectionTokenType(TextChoices): + ADMIN = 'admin', 'Admin' + SUPER = 'super', 'Super' + USER = 'user', 'User' diff --git a/apps/authentication/migrations/0004_connectiontoken_type.py b/apps/authentication/migrations/0004_connectiontoken_type.py new file mode 100644 index 000000000..02f22686b --- /dev/null +++ b/apps/authentication/migrations/0004_connectiontoken_type.py @@ -0,0 +1,30 @@ +# Generated by Django 4.1.13 on 2024-11-11 11:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0003_sshkey'), + ] + + operations = [ + migrations.AddField( + model_name='connectiontoken', + name='type', + field=models.CharField(choices=[('admin', 'Admin'), ('super', 'Super'), ('user', 'User')], default='user', max_length=16, verbose_name='Type'), + ), + migrations.CreateModel( + name='AdminConnectionToken', + fields=[ + ], + options={ + 'verbose_name': 'Admin connection token', + 'proxy': True, + 'indexes': [], + 'constraints': [], + }, + bases=('authentication.connectiontoken',), + ), + ] diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index fcf1d43c8..94c56b030 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -12,6 +12,7 @@ from rest_framework.exceptions import PermissionDenied from accounts.models import VirtualAccount from assets.const import Protocol from assets.const.host import GATEWAY_NAME +from authentication.const import ConnectionTokenType from common.db.fields import EncryptTextField from common.exceptions import JMSException from common.utils import lazyproperty, pretty_string, bulk_get @@ -26,6 +27,8 @@ def date_expired_default(): class ConnectionToken(JMSOrgBaseModel): + _type = ConnectionTokenType.USER + value = models.CharField(max_length=64, default='', verbose_name=_("Value")) user = models.ForeignKey( 'users.User', on_delete=models.SET_NULL, null=True, blank=True, @@ -52,6 +55,11 @@ class ConnectionToken(JMSOrgBaseModel): ) is_active = models.BooleanField(default=True, verbose_name=_("Active")) + type = models.CharField( + max_length=16, choices=ConnectionTokenType.choices, + default=ConnectionTokenType.USER, verbose_name=_('Type') + ) + class Meta: ordering = ('-date_expired',) permissions = [ @@ -60,6 +68,10 @@ class ConnectionToken(JMSOrgBaseModel): ] verbose_name = _('Connection token') + def save(self, *args, **kwargs): + self.type = self._meta.model._type + return super().save(*args, **kwargs) + @property def is_expired(self): return self.date_expired < timezone.now() @@ -268,9 +280,28 @@ class ConnectionToken(JMSOrgBaseModel): class SuperConnectionToken(ConnectionToken): + _type = ConnectionTokenType.SUPER + class Meta: proxy = True permissions = [ ('view_superconnectiontokensecret', _('Can view super connection token secret')) ] verbose_name = _("Super connection token") + + +class AdminConnectionTokenManager(models.Manager): + def get_queryset(self): + queryset = super().get_queryset() + queryset = queryset.filter(type=ConnectionTokenType.ADMIN) + return queryset + + +class AdminConnectionToken(ConnectionToken): + _type = ConnectionTokenType.ADMIN + + objects = AdminConnectionTokenManager() + + class Meta: + proxy = True + verbose_name = _("Admin connection token") diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py index 8f74f2907..c115f9e2c 100644 --- a/apps/authentication/urls/api_urls.py +++ b/apps/authentication/urls/api_urls.py @@ -13,6 +13,7 @@ router.register('sso', api.SSOViewSet, 'sso') router.register('temp-tokens', api.TempTokenViewSet, 'temp-token') router.register('connection-token', api.ConnectionTokenViewSet, 'connection-token') router.register('super-connection-token', api.SuperConnectionTokenViewSet, 'super-connection-token') +router.register('admin-connection-token', api.AdminConnectionTokenViewSet, 'admin-connection-token') router.register('confirm', api.UserConfirmationViewSet, 'confirm') router.register('ssh-key', api.SSHkeyViewSet, 'ssh-key')