pref: 暂存 客户端连接方式

pull/9129/head
ibuler 2022-11-28 22:58:43 +08:00
parent 742cac1e90
commit f6bdc7f81c
7 changed files with 96 additions and 38 deletions

View File

@ -11,6 +11,7 @@ from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from common.drf.api import JMSModelViewSet from common.drf.api import JMSModelViewSet
from common.http import is_true from common.http import is_true
@ -18,6 +19,7 @@ from common.utils import random_string
from orgs.mixins.api import RootOrgViewMixin from orgs.mixins.api import RootOrgViewMixin
from perms.models import ActionChoices from perms.models import ActionChoices
from terminal.models import EndpointRule from terminal.models import EndpointRule
from terminal.const import NativeClient
from ..models import ConnectionToken from ..models import ConnectionToken
from ..serializers import ( from ..serializers import (
ConnectionTokenSerializer, ConnectionTokenSecretSerializer, ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
@ -128,19 +130,20 @@ class RDPFileClientProtocolURLMixin:
return true_value if is_true(os.getenv(env_key, env_default)) else false_value return true_value if is_true(os.getenv(env_key, env_default)) else false_value
def get_client_protocol_data(self, token: ConnectionToken): def get_client_protocol_data(self, token: ConnectionToken):
protocol = token.protocol
username = token.user.username username = token.user.username
rdp_config = ssh_token = '' rdp_config = ssh_token = ''
if protocol == 'rdp': connect_method = token.connect_method
filename, rdp_config = self.get_rdp_file_info(token)
elif protocol == 'ssh': if connect_method == NativeClient.ssh:
filename, ssh_token = self.get_ssh_token(token) filename, ssh_token = self.get_ssh_token(token)
elif connect_method == NativeClient.mstsc:
filename, rdp_config = self.get_rdp_file_info(token)
else: else:
raise ValueError('Protocol not support: {}'.format(protocol)) raise ValueError('Protocol not support: {}'.format(connect_method))
return { return {
"filename": filename, "filename": filename,
"protocol": protocol, "protocol": token.protocol,
"username": username, "username": username,
"token": ssh_token, "token": ssh_token,
"config": rdp_config "config": rdp_config
@ -234,14 +237,25 @@ class ConnectionTokenViewSet(ExtraActionApiMixin, RootOrgViewMixin, JMSModelView
rbac_perm = 'authentication.view_connectiontokensecret' rbac_perm = 'authentication.view_connectiontokensecret'
if not request.user.has_perm(rbac_perm): if not request.user.has_perm(rbac_perm):
raise PermissionDenied('Not allow to view secret') raise PermissionDenied('Not allow to view secret')
token_id = request.data.get('token') or ''
token_id = request.data.get('id') or ''
token = get_object_or_404(ConnectionToken, pk=token_id) token = get_object_or_404(ConnectionToken, pk=token_id)
if token.is_expired:
raise ValidationError({'id': 'Token is expired'})
token.is_valid() token.is_valid()
serializer = self.get_serializer(instance=token) serializer = self.get_serializer(instance=token)
expire_now = request.data.get('expire_now', True)
if expire_now:
token.expire()
return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.data, status=status.HTTP_200_OK)
def get_queryset(self): def get_queryset(self):
return ConnectionToken.objects.filter(user=self.request.user) queryset = ConnectionToken.objects\
.filter(user=self.request.user)\
.filter(date_expired__lt=timezone.now())
return queryset
def get_user(self, serializer): def get_user(self, serializer):
return self.request.user return self.request.user
@ -299,7 +313,7 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet):
def renewal(self, request, *args, **kwargs): def renewal(self, request, *args, **kwargs):
from common.utils.timezone import as_current_tz from common.utils.timezone import as_current_tz
token_id = request.data.get('token') or '' token_id = request.data.get('id') or ''
token = get_object_or_404(ConnectionToken, pk=token_id) token = get_object_or_404(ConnectionToken, pk=token_id)
date_expired = as_current_tz(token.date_expired) date_expired = as_current_tz(token.date_expired)
if token.is_expired: if token.is_expired:

View File

@ -26,20 +26,20 @@ class Migration(migrations.Migration):
old_name='username', old_name='username',
new_name='input_username', new_name='input_username',
), ),
migrations.AddField(
model_name='connectiontoken',
name='input_secret',
field=common.db.fields.EncryptCharField(default='', max_length=128, verbose_name='Input Secret'),
),
migrations.AlterField( migrations.AlterField(
model_name='connectiontoken', model_name='connectiontoken',
name='account_name', name='account_name',
field=models.CharField(max_length=128, verbose_name='Account name'), field=models.CharField(max_length=128, verbose_name='Account name'),
), ),
migrations.AlterField(
model_name='connectiontoken',
name='input_secret',
field=common.db.fields.EncryptCharField(blank=True, default='', max_length=128, verbose_name='Input Secret'),
),
migrations.AlterField( migrations.AlterField(
model_name='connectiontoken', model_name='connectiontoken',
name='input_username', name='input_username',
field=models.CharField(default='', max_length=128, verbose_name='Input Username'), field=models.CharField(blank=True, default='', max_length=128, verbose_name='Input Username'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='connectiontoken', model_name='connectiontoken',

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.14 on 2022-11-28 13:48
import common.db.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0016_auto_20221125_2240'),
]
operations = [
migrations.AddField(
model_name='connectiontoken',
name='connect_method',
field=models.CharField(default='web_ui', max_length=32, verbose_name='Connect method'),
preserve_default=False,
),
]

View File

@ -34,6 +34,7 @@ class ConnectionToken(OrgModelMixin, JMSBaseModel):
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")
) )
connect_method = models.CharField(max_length=32, verbose_name=_("Connect method"))
user_display = models.CharField(max_length=128, default='', verbose_name=_("User display")) user_display = models.CharField(max_length=128, default='', verbose_name=_("User display"))
asset_display = models.CharField(max_length=128, default='', verbose_name=_("Asset display")) asset_display = models.CharField(max_length=128, default='', verbose_name=_("Asset display"))
date_expired = models.DateTimeField( date_expired = models.DateTimeField(

View File

@ -109,16 +109,10 @@ class ConnectionTokenGatewaySerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Asset model = Asset
fields = ['id', 'address', 'port', 'username', 'password', 'private_key'] fields = [
'id', 'address', 'port', 'username',
'password', 'private_key'
class ConnectionTokenDomainSerializer(serializers.ModelSerializer): ]
""" Domain """
gateways = ConnectionTokenGatewaySerializer(many=True, read_only=True)
class Meta:
model = Domain
fields = ['id', 'name', 'gateways']
class ConnectionTokenCmdFilterRuleSerializer(serializers.ModelSerializer): class ConnectionTokenCmdFilterRuleSerializer(serializers.ModelSerializer):
@ -143,6 +137,7 @@ class ConnectionTokenPlatform(PlatformSerializer):
class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin): class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
expire_now = serializers.BooleanField(label=_('Expired now'), default=True)
user = ConnectionTokenUserSerializer(read_only=True) user = ConnectionTokenUserSerializer(read_only=True)
asset = ConnectionTokenAssetSerializer(read_only=True) asset = ConnectionTokenAssetSerializer(read_only=True)
platform = ConnectionTokenPlatform(read_only=True) platform = ConnectionTokenPlatform(read_only=True)
@ -155,7 +150,10 @@ class ConnectionTokenSecretSerializer(OrgResourceModelSerializerMixin):
class Meta: class Meta:
model = ConnectionToken model = ConnectionToken
fields = [ fields = [
'id', 'value', 'user', 'asset', 'account', 'id', 'value', 'user', 'asset', 'platform', 'account',
'protocol', 'domain', 'gateway', 'protocol', 'gateway', 'actions', 'expire_at', 'expire_now',
'actions', 'expire_at', 'platform',
] ]
extra_kwargs = {
'value': {'read_only': True},
'expire_now': {'write_only': True},
}

View File

@ -0,0 +1,21 @@
# Generated by Django 3.2.14 on 2022-11-28 13:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('ops', '0035_jobexecution_org_id'),
]
operations = [
migrations.AlterModelOptions(
name='job',
options={'ordering': ['date_created']},
),
migrations.AlterModelOptions(
name='jobexecution',
options={'ordering': ['-date_created']},
),
]

View File

@ -96,17 +96,21 @@ class NativeClient(TextChoices):
@classmethod @classmethod
def get_launch_command(cls, name, os='windows'): def get_launch_command(cls, name, os='windows'):
commands = { commands = {
'ssh': 'ssh {username}@{hostname} -p {port}', cls.ssh: 'ssh {token.id}@{endpoint.ip} -p {endpoint.port}',
'putty': 'putty -ssh {username}@{hostname} -P {port}', cls.putty: 'putty-ssh {token.id}@{endpoint.ip} -P {endpoint.port}',
'xshell': '-url ssh://root:passwd@192.168.10.100', cls.xshell: 'xshell -url ssh://{token.id}:{token.value}@{endpoint.ip}:{endpoint.port}',
'mysql': 'mysql -h {hostname} -P {port} -u {username} -p', # 'mysql': 'mysql -h {hostname} -P {port} -u {username} -p',
'psql': { # 'psql': {
'default': 'psql -h {hostname} -p {port} -U {username} -W', # 'default': 'psql -h {hostname} -p {port} -U {username} -W',
'windows': 'psql /h {hostname} /p {port} /U {username} -W', # 'windows': 'psql /h {hostname} /p {port} /U {username} -W',
# },
# 'sqlplus': 'sqlplus {username}/{password}@{hostname}:{port}',
# 'redis': 'redis-cli -h {hostname} -p {port} -a {password}',
cls.mstsc: {
'command': "$open_file$",
'file': {
}
}, },
'sqlplus': 'sqlplus {username}/{password}@{hostname}:{port}',
'redis': 'redis-cli -h {hostname} -p {port} -a {password}',
'mstsc': 'mstsc /v:{hostname}:{port}',
} }
command = commands.get(name) command = commands.get(name)
if isinstance(command, dict): if isinstance(command, dict):