mirror of https://github.com/jumpserver/jumpserver
pref: 暂存 客户端连接方式
parent
742cac1e90
commit
f6bdc7f81c
|
@ -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:
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
|
]
|
|
@ -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(
|
||||||
|
|
|
@ -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},
|
||||||
|
}
|
||||||
|
|
|
@ -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']},
|
||||||
|
),
|
||||||
|
]
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue