Unverified Commit 311538dc authored by 老广's avatar 老广 Committed by GitHub

Bugfix (#2513)

* [Update] 用户页面添加跳转

* [Update] 网关测试支持nat, 修复创建node等id不能指定的问题, 修复settings频繁redis, 没有has_replay录像不可以播放
parent 324cf246
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
__version__ = "1.4.8" __version__ = "1.4.8"
...@@ -51,9 +51,10 @@ class GatewayTestConnectionApi(SingleObjectMixin, APIView): ...@@ -51,9 +51,10 @@ class GatewayTestConnectionApi(SingleObjectMixin, APIView):
model = Gateway model = Gateway
object = None object = None
def get(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object(Gateway.objects.all()) self.object = self.get_object(Gateway.objects.all())
ok, e = self.object.test_connective() local_port = self.request.data.get('port') or self.object.port
ok, e = self.object.test_connective(local_port=local_port)
if ok: if ok:
return Response("ok") return Response("ok")
else: else:
......
...@@ -97,24 +97,12 @@ class AssetBulkUpdateForm(OrgModelForm): ...@@ -97,24 +97,12 @@ class AssetBulkUpdateForm(OrgModelForm):
} }
) )
) )
port = forms.IntegerField(
label=_('Port'), required=False, min_value=1, max_value=65535,
)
admin_user = forms.ModelChoiceField(
required=False, queryset=AdminUser.objects,
label=_("Admin user"),
widget=forms.Select(
attrs={
'class': 'select2',
'data-placeholder': _('Admin user')
}
)
)
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'assets', 'port', 'admin_user', 'labels', 'nodes', 'platform' 'assets', 'port', 'admin_user', 'labels', 'platform',
'protocol', 'domain',
] ]
widgets = { widgets = {
'labels': forms.SelectMultiple( 'labels': forms.SelectMultiple(
...@@ -125,6 +113,13 @@ class AssetBulkUpdateForm(OrgModelForm): ...@@ -125,6 +113,13 @@ class AssetBulkUpdateForm(OrgModelForm):
), ),
} }
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 重写其他字段为不再required
for name, field in self.fields.items():
if name != 'assets':
field.required = False
def save(self, commit=True): def save(self, commit=True):
changed_fields = [] changed_fields = []
for field in self._meta.fields: for field in self._meta.fields:
......
...@@ -66,6 +66,9 @@ class GatewayForm(PasswordAndKeyAuthForm, OrgModelForm): ...@@ -66,6 +66,9 @@ class GatewayForm(PasswordAndKeyAuthForm, OrgModelForm):
'name', 'ip', 'port', 'username', 'protocol', 'domain', 'password', 'name', 'ip', 'port', 'username', 'protocol', 'domain', 'password',
'private_key_file', 'is_active', 'comment', 'private_key_file', 'is_active', 'comment',
] ]
help_texts = {
'protocol': _("SSH gateway support proxy SSH,RDP,VNC")
}
widgets = { widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}), 'username': forms.TextInput(attrs={'placeholder': _('Username')}),
......
...@@ -60,7 +60,9 @@ class Gateway(AssetUser): ...@@ -60,7 +60,9 @@ class Gateway(AssetUser):
unique_together = [('name', 'org_id')] unique_together = [('name', 'org_id')]
verbose_name = _("Gateway") verbose_name = _("Gateway")
def test_connective(self): def test_connective(self, local_port=None):
if local_port is None:
local_port = self.port
client = paramiko.SSHClient() client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy = paramiko.SSHClient() proxy = paramiko.SSHClient()
...@@ -76,12 +78,11 @@ class Gateway(AssetUser): ...@@ -76,12 +78,11 @@ class Gateway(AssetUser):
paramiko.SSHException) as e: paramiko.SSHException) as e:
return False, str(e) return False, str(e)
sock = proxy.get_transport().open_channel(
'direct-tcpip', ('127.0.0.1', self.port), ('127.0.0.1', 0)
)
try: try:
client.connect("127.0.0.1", port=self.port, sock = proxy.get_transport().open_channel(
'direct-tcpip', ('127.0.0.1', local_port), ('127.0.0.1', 0)
)
client.connect("127.0.0.1", port=local_port,
username=self.username, username=self.username,
password=self.password, password=self.password,
key_filename=self.private_key_file, key_filename=self.private_key_file,
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from rest_framework import serializers from rest_framework import serializers
from rest_framework_bulk.serializers import BulkListSerializer
from common.mixins import BulkSerializerMixin
from ..models import Asset, Node from ..models import Asset, Node
from .asset import AssetGrantedSerializer
__all__ = [ __all__ = [
...@@ -22,7 +19,7 @@ class NodeSerializer(serializers.ModelSerializer): ...@@ -22,7 +19,7 @@ class NodeSerializer(serializers.ModelSerializer):
'id', 'key', 'value', 'assets_amount', 'org_id', 'id', 'key', 'value', 'assets_amount', 'org_id',
] ]
read_only_fields = [ read_only_fields = [
'id', 'key', 'assets_amount', 'org_id', 'key', 'assets_amount', 'org_id',
] ]
def validate_value(self, data): def validate_value(self, data):
......
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}gateway_test{% endblock %}
{% block modal_title%}{% trans "Test gateway test connection" %}{% endblock %}
{% block modal_body %}
{% load bootstrap3 %}
<form method="post" class="form-horizontal" action="" id="test_gateway_form" style="padding-top: 10px">
<div class="form-group">
<input id="gateway_id" name="gateway_id" hidden>
<label for="port" class="col-sm-2 control-label">{% trans 'SSH Port' %}</label>
<div class="col-sm-9" id="select2-container">
<input id="ssh_test_port" name="port" class="form-control">
<span class="help-block">{% trans 'If use nat, set the ssh real port' %}</span>
</div>
</div>
</form>
{% endblock %}
{% block modal_confirm_id %}btn_gateway_test{% endblock %}
\ No newline at end of file
...@@ -15,10 +15,16 @@ ...@@ -15,10 +15,16 @@
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<a href="{% url 'assets:domain-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a> <a href="{% url 'assets:domain-detail' pk=object.id %}"
class="text-center"><i
class="fa fa-laptop"></i> {% trans 'Detail' %}
</a>
</li> </li>
<li class="active"> <li class="active">
<a href="{% url 'assets:domain-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Gateway' %} </a> <a href="{% url 'assets:domain-detail' pk=object.id %}"
class="text-center"><i
class="fa fa-laptop"></i> {% trans 'Gateway' %}
</a>
</li> </li>
</ul> </ul>
</div> </div>
...@@ -33,7 +39,8 @@ ...@@ -33,7 +39,8 @@
<a class="collapse-link"> <a class="collapse-link">
<i class="fa fa-chevron-up"></i> <i class="fa fa-chevron-up"></i>
</a> </a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#"> <a class="dropdown-toggle"
data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i> <i class="fa fa-wrench"></i>
</a> </a>
<ul class="dropdown-menu dropdown-user"> <ul class="dropdown-menu dropdown-user">
...@@ -45,13 +52,17 @@ ...@@ -45,13 +52,17 @@
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<div class="uc pull-left m-r-5"> <div class="uc pull-left m-r-5">
<a href="{% url 'assets:domain-gateway-create' pk=object.id %}" class="btn btn-sm btn-primary"> {% trans "Create gateway" %} </a> <a href="{% url 'assets:domain-gateway-create' pk=object.id %}"
class="btn btn-sm btn-primary"> {% trans "Create gateway" %} </a>
</div> </div>
<table class="table table-striped table-bordered table-hover " id="domain_list_table" > <table class="table table-striped table-bordered table-hover "
id="domain_list_table">
<thead> <thead>
<tr> <tr>
<th class="text-center"> <th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" > <input type="checkbox"
id="check_all"
class="ipt_check_all">
</th> </th>
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'IP' %}</th> <th class="text-center">{% trans 'IP' %}</th>
...@@ -73,6 +84,7 @@ ...@@ -73,6 +84,7 @@
</div> </div>
</div> </div>
</div> </div>
{% include 'assets/_gateway_test_modal.html' %}
{% endblock %} {% endblock %}
{% block content_bottom_left %}{% endblock %} {% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
...@@ -84,7 +96,7 @@ function initTable() { ...@@ -84,7 +96,7 @@ function initTable() {
{targets: 7, createdCell: function (td, cellData, rowData) { {targets: 7, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:domain-gateway-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var update_btn = '<a href="{% url "assets:domain-gateway-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var test_btn = '<a class="btn btn-xs btn-warning m-l-xs btn-test" data-uid="{{ DEFAULT_PK }}">{% trans "Test connection" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var test_btn = '<a class="btn btn-xs btn-warning m-l-xs btn-test" data-uid="{{ DEFAULT_PK }}" data-port="PORT">{% trans "Test connection" %}</a>'.replace('{{ DEFAULT_PK }}', cellData).replace("PORT", rowData.port);
if(rowData.protocol === 'rdp'){ if(rowData.protocol === 'rdp'){
test_btn = '<a class="btn btn-xs btn-warning m-l-xs btn-test" disabled data-uid="{{ DEFAULT_PK }}">{% trans "Test connection" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); test_btn = '<a class="btn btn-xs btn-warning m-l-xs btn-test" disabled data-uid="{{ DEFAULT_PK }}">{% trans "Test connection" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
} }
...@@ -114,15 +126,21 @@ $(document).ready(function(){ ...@@ -114,15 +126,21 @@ $(document).ready(function(){
$data_table.ajax.reload(); $data_table.ajax.reload();
}, 3000); }, 3000);
}).on('click', '.btn-test', function () { }).on('click', '.btn-test', function () {
var $this = $(this); $("#ssh_test_port").val($(this).data('port'));
var uid = $this.data('uid'); $("#gateway_id").val($(this).data('uid'));
$("#gateway_test").modal('show');
}).on('click', '#btn_gateway_test', function () {
var data = $("#test_gateway_form").serializeObject();
var uid = data.gateway_id;
var the_url = '{% url "api-assets:test-gateway-connective" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid); var the_url = '{% url "api-assets:test-gateway-connective" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
method: "GET", method: "POST",
body: JSON.stringify({'port': parseInt(data.port)}),
success_message: "{% trans 'Can be connected' %}", success_message: "{% trans 'Can be connected' %}",
fail_message: "{% trans 'The connection fails' %}" fail_message: "{% trans 'The connection fails' %}"
}) })
}); });
</script> </script>
{% endblock %} {% endblock %}
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
<th class="text-center">{% trans 'IP' %}</th> <th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Active' %}</th> <th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'System users' %}</th> <th class="text-center">{% trans 'System users' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
...@@ -94,13 +95,18 @@ function initTable() { ...@@ -94,13 +95,18 @@ function initTable() {
users.push(data.name); users.push(data.name);
}); });
$(td).html(users.join(', ')) $(td).html(users.join(', '))
}},
{targets: 5, createdCell: function (td, cellData) {
var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +'" class="btn btn-xs btn-primary">{% trans "Connect" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
$(td).html(conn_btn)
}} }}
], ],
ajax_url: url, ajax_url: url,
columns: [ columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "is_active", orderable: false }, {data: "is_active", orderable: false },
{data: "system_users_granted", orderable: false} {data: "system_users_granted", orderable: false},
{data: "id", orderable: false}
] ]
}; };
asset_table = jumpserver.initServerSideDataTable(options); asset_table = jumpserver.initServerSideDataTable(options);
......
...@@ -22,6 +22,10 @@ from .conf import load_user_config ...@@ -22,6 +22,10 @@ from .conf import load_user_config
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_DIR = os.path.dirname(BASE_DIR) PROJECT_DIR = os.path.dirname(BASE_DIR)
sys.path.append(PROJECT_DIR)
from apps import __version__
VERSION = __version__
CONFIG = load_user_config() CONFIG = load_user_config()
LOG_DIR = os.path.join(PROJECT_DIR, 'logs') LOG_DIR = os.path.join(PROJECT_DIR, 'logs')
JUMPSERVER_LOG_FILE = os.path.join(LOG_DIR, 'jumpserver.log') JUMPSERVER_LOG_FILE = os.path.join(LOG_DIR, 'jumpserver.log')
...@@ -456,8 +460,8 @@ CELERY_ACCEPT_CONTENT = ['json', 'pickle'] ...@@ -456,8 +460,8 @@ CELERY_ACCEPT_CONTENT = ['json', 'pickle']
CELERY_RESULT_EXPIRES = 3600 CELERY_RESULT_EXPIRES = 3600
# CELERY_WORKER_LOG_FORMAT = '%(asctime)s [%(module)s %(levelname)s] %(message)s' # CELERY_WORKER_LOG_FORMAT = '%(asctime)s [%(module)s %(levelname)s] %(message)s'
# CELERY_WORKER_LOG_FORMAT = '%(message)s' # CELERY_WORKER_LOG_FORMAT = '%(message)s'
CELERY_WORKER_TASK_LOG_FORMAT = '%(task_id)s %(task_name)s %(message)s' # CELERY_WORKER_TASK_LOG_FORMAT = '%(task_id)s %(task_name)s %(message)s'
# CELERY_WORKER_TASK_LOG_FORMAT = '%(message)s' CELERY_WORKER_TASK_LOG_FORMAT = '%(message)s'
# CELERY_WORKER_LOG_FORMAT = '%(asctime)s [%(module)s %(levelname)s] %(message)s' # CELERY_WORKER_LOG_FORMAT = '%(asctime)s [%(module)s %(levelname)s] %(message)s'
CELERY_WORKER_LOG_FORMAT = '%(message)s' CELERY_WORKER_LOG_FORMAT = '%(message)s'
CELERY_TASK_EAGER_PROPAGATES = True CELERY_TASK_EAGER_PROPAGATES = True
......
...@@ -60,7 +60,7 @@ urlpatterns = [ ...@@ -60,7 +60,7 @@ urlpatterns = [
path('', IndexView.as_view(), name='index'), path('', IndexView.as_view(), name='index'),
path('', include(api_v2_patterns)), path('', include(api_v2_patterns)),
path('', include(api_v1_patterns)), path('', include(api_v1_patterns)),
path('luna/', LunaView.as_view(), name='luna-error'), path('luna/', LunaView.as_view(), name='luna-view'),
path('i18n/<str:lang>/', I18NView.as_view(), name='i18n-switch'), path('i18n/<str:lang>/', I18NView.as_view(), name='i18n-switch'),
path('settings/', include('settings.urls.view_urls', namespace='settings')), path('settings/', include('settings.urls.view_urls', namespace='settings')),
# path('api/v2/', include(api_v2_patterns)), # path('api/v2/', include(api_v2_patterns)),
......
This diff is collapsed.
...@@ -31,7 +31,7 @@ class CommandExecution(models.Model): ...@@ -31,7 +31,7 @@ class CommandExecution(models.Model):
@property @property
def inventory(self): def inventory(self):
return JMSInventory(self.hosts.all(), run_as=self.run_as) return JMSInventory(self.hosts.all(), run_as=self.run_as.username)
@property @property
def result(self): def result(self):
......
...@@ -75,7 +75,7 @@ class CommandExecutionSerializer(serializers.ModelSerializer): ...@@ -75,7 +75,7 @@ class CommandExecutionSerializer(serializers.ModelSerializer):
'is_finished', 'date_created', 'date_finished' 'is_finished', 'date_created', 'date_finished'
] ]
read_only_fields = [ read_only_fields = [
'id', 'result', 'is_finished', 'log_url', 'date_created', 'result', 'is_finished', 'log_url', 'date_created',
'date_finished' 'date_finished'
] ]
......
...@@ -16,7 +16,7 @@ class OrgSerializer(ModelSerializer): ...@@ -16,7 +16,7 @@ class OrgSerializer(ModelSerializer):
model = Organization model = Organization
list_serializer_class = BulkListSerializer list_serializer_class = BulkListSerializer
fields = '__all__' fields = '__all__'
read_only_fields = ['id', 'created_by', 'date_created'] read_only_fields = ['created_by', 'date_created']
class OrgReadSerializer(ModelSerializer): class OrgReadSerializer(ModelSerializer):
......
...@@ -4,7 +4,7 @@ import json ...@@ -4,7 +4,7 @@ import json
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import post_save, pre_save from django.db.models.signals import post_save, pre_save
from django.conf import LazySettings, empty from django.conf import LazySettings, empty, global_settings
from django.db.utils import ProgrammingError, OperationalError from django.db.utils import ProgrammingError, OperationalError
from django.core.cache import cache from django.core.cache import cache
...@@ -25,11 +25,18 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs): ...@@ -25,11 +25,18 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs):
@receiver(django_ready, dispatch_uid="my_unique_identifier") @receiver(django_ready, dispatch_uid="my_unique_identifier")
def monkey_patch_settings(sender, **kwargs): def monkey_patch_settings(sender, **kwargs):
cache_key_prefix = '_SETTING_' cache_key_prefix = '_SETTING_'
uncached_settings = [ custom_need_cache_settings = [
'CACHES', 'DEBUG', 'SECRET_KEY', 'INSTALLED_APPS', 'AUTHENTICATION_BACKENDS'
'ROOT_URLCONF', 'TEMPLATES', 'DATABASES', '_wrapped',
'CELERY_LOG_DIR'
] ]
custom_no_cache_settings = [
'BASE_DIR', 'VERSION', 'AUTH_OPENID'
]
django_settings = dir(global_settings)
uncached_settings = [i for i in django_settings if i.isupper()]
uncached_settings = [i for i in uncached_settings if not i.startswith('EMAIL')]
uncached_settings = [i for i in uncached_settings if not i.startswith('SESSION_REDIS')]
uncached_settings = [i for i in uncached_settings if i not in custom_need_cache_settings]
uncached_settings.extend(custom_no_cache_settings)
def monkey_patch_getattr(self, name): def monkey_patch_getattr(self, name):
if name not in uncached_settings: if name not in uncached_settings:
......
...@@ -188,6 +188,14 @@ class Session(OrgModelMixin): ...@@ -188,6 +188,14 @@ class Session(OrgModelMixin):
local_path = rel_path local_path = rel_path
return local_path return local_path
def can_replay(self):
if self.has_replay:
return True
version = settings.VERSION.split('.')
if [int(i) for i in version] > [1, 4, 8]:
return False
return True
def save_to_storage(self, f): def save_to_storage(self, f):
local_path = self.get_local_path() local_path = self.get_local_path()
try: try:
......
...@@ -21,7 +21,7 @@ class TerminalSerializer(serializers.ModelSerializer): ...@@ -21,7 +21,7 @@ class TerminalSerializer(serializers.ModelSerializer):
'replay_storage', 'user', 'is_accepted', 'is_deleted', 'replay_storage', 'user', 'is_accepted', 'is_deleted',
'date_created', 'comment' 'date_created', 'comment'
] ]
read_only_fields = ['id', 'remote_addr', 'user', 'date_created'] read_only_fields = ['remote_addr', 'user', 'date_created']
def is_valid(self, raise_exception=False): def is_valid(self, raise_exception=False):
valid = super().is_valid(raise_exception=raise_exception) valid = super().is_valid(raise_exception=raise_exception)
......
...@@ -101,9 +101,8 @@ ...@@ -101,9 +101,8 @@
<td class="text-center">{{ session.date_start|time_util_with_seconds:session.date_end }}</td> <td class="text-center">{{ session.date_start|time_util_with_seconds:session.date_end }}</td>
<td> <td>
{% if session.is_finished %} {% if session.is_finished %}
<a onclick="window.open('/luna/replay/{{ session.id }}','luna', 'height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a> <a {% if not session.can_replay %} disabled="" {% endif %} onclick="window.open('/luna/replay/{{ session.id }}','luna', 'height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a>
{% else %} {% else %}
<!--<a onclick="window.open('/luna/monitor/{{ session.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-monitor" >{% trans "Monitor" %}</a>-->
{% if session.protocol == 'ssh' %} {% if session.protocol == 'ssh' %}
<a class="btn btn-xs btn-danger btn-term" value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a> <a class="btn btn-xs btn-danger btn-term" value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a>
{% else %} {% else %}
......
...@@ -54,7 +54,7 @@ class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): ...@@ -54,7 +54,7 @@ class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
model = UserGroup model = UserGroup
list_serializer_class = BulkListSerializer list_serializer_class = BulkListSerializer
fields = '__all__' fields = '__all__'
read_only_fields = ['id', 'created_by'] read_only_fields = ['created_by']
@staticmethod @staticmethod
def get_users(obj): def get_users(obj):
......
...@@ -15,7 +15,7 @@ class ServiceAccountSerializer(serializers.ModelSerializer): ...@@ -15,7 +15,7 @@ class ServiceAccountSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ['id', 'name', 'access_key'] fields = ['id', 'name', 'access_key']
read_only_fields = ['id', 'access_key'] read_only_fields = ['access_key']
def get_username(self): def get_username(self):
return self.initial_data.get('name') return self.initial_data.get('name')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment