mirror of https://github.com/jumpserver/jumpserver
commit
0798e3c466
|
@ -28,6 +28,15 @@ class DomainForm(forms.ModelForm):
|
||||||
initial['assets'] = kwargs['instance'].assets.all()
|
initial['assets'] = kwargs['instance'].assets.all()
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# 前端渲染优化, 防止过多资产
|
||||||
|
assets_field = self.fields.get('assets')
|
||||||
|
if not self.data:
|
||||||
|
instance = kwargs.get('instance')
|
||||||
|
if instance:
|
||||||
|
assets_field.queryset = instance.assets.all()
|
||||||
|
else:
|
||||||
|
assets_field.queryset = Asset.objects.none()
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
instance = super().save(commit=commit)
|
instance = super().save(commit=commit)
|
||||||
assets = self.cleaned_data['assets']
|
assets = self.cleaned_data['assets']
|
||||||
|
|
|
@ -26,6 +26,15 @@ class LabelForm(forms.ModelForm):
|
||||||
initial['assets'] = kwargs['instance'].assets.all()
|
initial['assets'] = kwargs['instance'].assets.all()
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# 前端渲染优化, 防止过多资产
|
||||||
|
assets_field = self.fields.get('assets')
|
||||||
|
if not self.data:
|
||||||
|
instance = kwargs.get('instance')
|
||||||
|
if instance:
|
||||||
|
assets_field.queryset = instance.assets.all()
|
||||||
|
else:
|
||||||
|
assets_field.queryset = Asset.objects.none()
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
label = super().save(commit=commit)
|
label = super().save(commit=commit)
|
||||||
assets = self.cleaned_data['assets']
|
assets = self.cleaned_data['assets']
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import time
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.core.cache import cache
|
||||||
|
|
||||||
from common.utils import capacity_convert, \
|
from common.utils import capacity_convert, \
|
||||||
sum_capacity, encrypt_password, get_logger
|
sum_capacity, encrypt_password, get_logger
|
||||||
from ops.celery.utils import register_as_period_task, after_app_shutdown_clean, \
|
from ops.celery.utils import register_as_period_task, after_app_shutdown_clean
|
||||||
after_app_ready_start
|
|
||||||
from orgs.utils import set_to_root_org
|
|
||||||
|
|
||||||
from .models import SystemUser, AdminUser, Asset
|
from .models import SystemUser, AdminUser, Asset
|
||||||
from . import const
|
from . import const
|
||||||
|
@ -211,6 +211,12 @@ def test_admin_user_connectivity_period():
|
||||||
"""
|
"""
|
||||||
A period task that update the ansible task period
|
A period task that update the ansible task period
|
||||||
"""
|
"""
|
||||||
|
key = '_JMS_TEST_ADMIN_USER_CONNECTIVITY_PERIOD'
|
||||||
|
prev_execute_time = cache.get(key)
|
||||||
|
if prev_execute_time:
|
||||||
|
logger.debug("Test admin user connectivity, less than 40 minutes, skip")
|
||||||
|
return
|
||||||
|
cache.set(key, 1, 60*40)
|
||||||
admin_users = AdminUser.objects.all()
|
admin_users = AdminUser.objects.all()
|
||||||
for admin_user in admin_users:
|
for admin_user in admin_users:
|
||||||
task_name = _("Test admin user connectivity period: {}").format(admin_user.name)
|
task_name = _("Test admin user connectivity period: {}").format(admin_user.name)
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log($.fn.select2.defaults);
|
|
||||||
$('.select2').select2().off("select2:open");
|
$('.select2').select2().off("select2:open");
|
||||||
}).on('click', '.select2-selection__rendered', function (e) {
|
}).on('click', '.select2-selection__rendered', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -33,6 +32,18 @@ $(document).ready(function () {
|
||||||
})
|
})
|
||||||
.on('click', '#btn_asset_modal_confirm', function () {
|
.on('click', '#btn_asset_modal_confirm', function () {
|
||||||
var assets = asset_table2.selected;
|
var assets = asset_table2.selected;
|
||||||
|
var options = [];
|
||||||
|
$('#id_assets option').each(function (i, v) {
|
||||||
|
options.push(v.value)
|
||||||
|
});
|
||||||
|
asset_table2.selected_rows.forEach(function (i) {
|
||||||
|
var name = i.hostname + '(' + i.ip + ')';
|
||||||
|
var option = new Option(name, i.id, false, true);
|
||||||
|
|
||||||
|
if (options.indexOf(i.id) === -1) {
|
||||||
|
$('#id_assets').append(option).trigger('change');
|
||||||
|
}
|
||||||
|
});
|
||||||
$('.select2').val(assets).trigger('change');
|
$('.select2').val(assets).trigger('change');
|
||||||
$("#asset_list_modal").modal('hide');
|
$("#asset_list_modal").modal('hide');
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,18 @@ $(document).ready(function () {
|
||||||
})
|
})
|
||||||
.on('click', '#btn_asset_modal_confirm', function () {
|
.on('click', '#btn_asset_modal_confirm', function () {
|
||||||
var assets = asset_table2.selected;
|
var assets = asset_table2.selected;
|
||||||
|
var options = [];
|
||||||
|
$('#id_assets option').each(function (i, v) {
|
||||||
|
options.push(v.value)
|
||||||
|
});
|
||||||
|
asset_table2.selected_rows.forEach(function (i) {
|
||||||
|
var name = i.hostname + '(' + i.ip + ')';
|
||||||
|
var option = new Option(name, i.id, false, true);
|
||||||
|
|
||||||
|
if (options.indexOf(i.id) === -1) {
|
||||||
|
$('#id_assets').append(option).trigger('change');
|
||||||
|
}
|
||||||
|
});
|
||||||
$('#id_assets').val(assets).trigger('change');
|
$('#id_assets').val(assets).trigger('change');
|
||||||
$("#asset_list_modal").modal('hide');
|
$("#asset_list_modal").modal('hide');
|
||||||
})
|
})
|
||||||
|
|
|
@ -139,22 +139,23 @@ class TerminalSettingForm(BaseForm):
|
||||||
(50, 50),
|
(50, 50),
|
||||||
)
|
)
|
||||||
TERMINAL_PASSWORD_AUTH = forms.BooleanField(
|
TERMINAL_PASSWORD_AUTH = forms.BooleanField(
|
||||||
initial=True, required=False, label=_("Password auth")
|
required=False, label=_("Password auth")
|
||||||
)
|
)
|
||||||
TERMINAL_PUBLIC_KEY_AUTH = forms.BooleanField(
|
TERMINAL_PUBLIC_KEY_AUTH = forms.BooleanField(
|
||||||
initial=True, required=False, label=_("Public key auth")
|
required=False, label=_("Public key auth")
|
||||||
)
|
)
|
||||||
TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField(
|
TERMINAL_HEARTBEAT_INTERVAL = forms.IntegerField(
|
||||||
initial=5, label=_("Heartbeat interval"), help_text=_("Units: seconds")
|
min_value=5, label=_("Heartbeat interval"),
|
||||||
|
help_text=_("Units: seconds")
|
||||||
)
|
)
|
||||||
TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField(
|
TERMINAL_ASSET_LIST_SORT_BY = forms.ChoiceField(
|
||||||
choices=SORT_BY_CHOICES, initial='hostname', label=_("List sort by")
|
choices=SORT_BY_CHOICES, label=_("List sort by")
|
||||||
)
|
)
|
||||||
TERMINAL_ASSET_LIST_PAGE_SIZE = forms.ChoiceField(
|
TERMINAL_ASSET_LIST_PAGE_SIZE = forms.ChoiceField(
|
||||||
choices=PAGE_SIZE_CHOICES, initial='auto', label=_("List page size"),
|
choices=PAGE_SIZE_CHOICES, label=_("List page size"),
|
||||||
)
|
)
|
||||||
TERMINAL_SESSION_KEEP_DURATION = forms.IntegerField(
|
TERMINAL_SESSION_KEEP_DURATION = forms.IntegerField(
|
||||||
label=_("Session keep duration"),
|
min_value=1, label=_("Session keep duration"),
|
||||||
help_text=_("Units: days, Session, record, command will be delete "
|
help_text=_("Units: days, Session, record, command will be delete "
|
||||||
"if more than duration, only in database")
|
"if more than duration, only in database")
|
||||||
)
|
)
|
||||||
|
@ -167,8 +168,7 @@ class TerminalCommandStorage(BaseForm):
|
||||||
class SecuritySettingForm(BaseForm):
|
class SecuritySettingForm(BaseForm):
|
||||||
# MFA global setting
|
# MFA global setting
|
||||||
SECURITY_MFA_AUTH = forms.BooleanField(
|
SECURITY_MFA_AUTH = forms.BooleanField(
|
||||||
initial=False, required=False,
|
required=False, label=_("MFA Secondary certification"),
|
||||||
label=_("MFA Secondary certification"),
|
|
||||||
help_text=_(
|
help_text=_(
|
||||||
'After opening, the user login must use MFA secondary '
|
'After opening, the user login must use MFA secondary '
|
||||||
'authentication (valid for all users, including administrators)'
|
'authentication (valid for all users, including administrators)'
|
||||||
|
@ -176,13 +176,11 @@ class SecuritySettingForm(BaseForm):
|
||||||
)
|
)
|
||||||
# limit login count
|
# limit login count
|
||||||
SECURITY_LOGIN_LIMIT_COUNT = forms.IntegerField(
|
SECURITY_LOGIN_LIMIT_COUNT = forms.IntegerField(
|
||||||
initial=7, min_value=3,
|
min_value=3, label=_("Limit the number of login failures")
|
||||||
label=_("Limit the number of login failures")
|
|
||||||
)
|
)
|
||||||
# limit login time
|
# limit login time
|
||||||
SECURITY_LOGIN_LIMIT_TIME = forms.IntegerField(
|
SECURITY_LOGIN_LIMIT_TIME = forms.IntegerField(
|
||||||
initial=30, min_value=5,
|
min_value=5, label=_("No logon interval"),
|
||||||
label=_("No logon interval"),
|
|
||||||
help_text=_(
|
help_text=_(
|
||||||
"Tip: (unit/minute) if the user has failed to log in for a limited "
|
"Tip: (unit/minute) if the user has failed to log in for a limited "
|
||||||
"number of times, no login is allowed during this time interval."
|
"number of times, no login is allowed during this time interval."
|
||||||
|
@ -190,8 +188,7 @@ class SecuritySettingForm(BaseForm):
|
||||||
)
|
)
|
||||||
# ssh max idle time
|
# ssh max idle time
|
||||||
SECURITY_MAX_IDLE_TIME = forms.IntegerField(
|
SECURITY_MAX_IDLE_TIME = forms.IntegerField(
|
||||||
initial=30, required=False,
|
required=False, label=_("Connection max idle time"),
|
||||||
label=_("Connection max idle time"),
|
|
||||||
help_text=_(
|
help_text=_(
|
||||||
'If idle time more than it, disconnect connection(only ssh now) '
|
'If idle time more than it, disconnect connection(only ssh now) '
|
||||||
'Unit: minute'
|
'Unit: minute'
|
||||||
|
@ -199,8 +196,8 @@ class SecuritySettingForm(BaseForm):
|
||||||
)
|
)
|
||||||
# password expiration time
|
# password expiration time
|
||||||
SECURITY_PASSWORD_EXPIRATION_TIME = forms.IntegerField(
|
SECURITY_PASSWORD_EXPIRATION_TIME = forms.IntegerField(
|
||||||
initial=9999, label=_("Password expiration time"),
|
label=_("Password expiration time"),
|
||||||
min_value=1,
|
min_value=1, max_value=99999,
|
||||||
help_text=_(
|
help_text=_(
|
||||||
"Tip: (unit: day) "
|
"Tip: (unit: day) "
|
||||||
"If the user does not update the password during the time, "
|
"If the user does not update the password during the time, "
|
||||||
|
@ -211,35 +208,30 @@ class SecuritySettingForm(BaseForm):
|
||||||
)
|
)
|
||||||
# min length
|
# min length
|
||||||
SECURITY_PASSWORD_MIN_LENGTH = forms.IntegerField(
|
SECURITY_PASSWORD_MIN_LENGTH = forms.IntegerField(
|
||||||
initial=6, label=_("Password minimum length"),
|
min_value=6, label=_("Password minimum length"),
|
||||||
min_value=6
|
|
||||||
)
|
)
|
||||||
# upper case
|
# upper case
|
||||||
SECURITY_PASSWORD_UPPER_CASE = forms.BooleanField(
|
SECURITY_PASSWORD_UPPER_CASE = forms.BooleanField(
|
||||||
initial=False, required=False,
|
required=False, label=_("Must contain capital letters"),
|
||||||
label=_("Must contain capital letters"),
|
|
||||||
help_text=_(
|
help_text=_(
|
||||||
'After opening, the user password changes '
|
'After opening, the user password changes '
|
||||||
'and resets must contain uppercase letters')
|
'and resets must contain uppercase letters')
|
||||||
)
|
)
|
||||||
# lower case
|
# lower case
|
||||||
SECURITY_PASSWORD_LOWER_CASE = forms.BooleanField(
|
SECURITY_PASSWORD_LOWER_CASE = forms.BooleanField(
|
||||||
initial=False, required=False,
|
required=False, label=_("Must contain lowercase letters"),
|
||||||
label=_("Must contain lowercase letters"),
|
|
||||||
help_text=_('After opening, the user password changes '
|
help_text=_('After opening, the user password changes '
|
||||||
'and resets must contain lowercase letters')
|
'and resets must contain lowercase letters')
|
||||||
)
|
)
|
||||||
# number
|
# number
|
||||||
SECURITY_PASSWORD_NUMBER = forms.BooleanField(
|
SECURITY_PASSWORD_NUMBER = forms.BooleanField(
|
||||||
initial=False, required=False,
|
required=False, label=_("Must contain numeric characters"),
|
||||||
label=_("Must contain numeric characters"),
|
|
||||||
help_text=_('After opening, the user password changes '
|
help_text=_('After opening, the user password changes '
|
||||||
'and resets must contain numeric characters')
|
'and resets must contain numeric characters')
|
||||||
)
|
)
|
||||||
# special char
|
# special char
|
||||||
SECURITY_PASSWORD_SPECIAL_CHAR = forms.BooleanField(
|
SECURITY_PASSWORD_SPECIAL_CHAR = forms.BooleanField(
|
||||||
initial=False, required=False,
|
required=False, label=_("Must contain special characters"),
|
||||||
label=_("Must contain special characters"),
|
|
||||||
help_text=_('After opening, the user password changes '
|
help_text=_('After opening, the user password changes '
|
||||||
'and resets must contain special characters')
|
'and resets must contain special characters')
|
||||||
)
|
)
|
||||||
|
|
|
@ -320,6 +320,16 @@ defaults = {
|
||||||
'TERMINAL_ASSET_LIST_SORT_BY': 'hostname',
|
'TERMINAL_ASSET_LIST_SORT_BY': 'hostname',
|
||||||
'TERMINAL_ASSET_LIST_PAGE_SIZE': 'auto',
|
'TERMINAL_ASSET_LIST_PAGE_SIZE': 'auto',
|
||||||
'TERMINAL_SESSION_KEEP_DURATION': 9999,
|
'TERMINAL_SESSION_KEEP_DURATION': 9999,
|
||||||
|
'SECURITY_MFA_AUTH': False,
|
||||||
|
'SECURITY_LOGIN_LIMIT_COUNT': 7,
|
||||||
|
'SECURITY_LOGIN_LIMIT_TIME': 30,
|
||||||
|
'SECURITY_MAX_IDLE_TIME': 30,
|
||||||
|
'SECURITY_PASSWORD_EXPIRATION_TIME': 9999,
|
||||||
|
'SECURITY_PASSWORD_MIN_LENGTH': 6,
|
||||||
|
'SECURITY_PASSWORD_UPPER_CASE': False,
|
||||||
|
'SECURITY_PASSWORD_LOWER_CASE': False,
|
||||||
|
'SECURITY_PASSWORD_NUMBER': False,
|
||||||
|
'SECURITY_PASSWORD_SPECIAL_CHAR': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from orgs.mixins import OrgModelForm
|
from orgs.mixins import OrgModelForm
|
||||||
from orgs.utils import current_org
|
from orgs.utils import current_org
|
||||||
from .models import AssetPermission
|
from .models import AssetPermission
|
||||||
|
from assets.models import Asset
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionForm(OrgModelForm):
|
class AssetPermissionForm(OrgModelForm):
|
||||||
|
@ -15,6 +16,15 @@ class AssetPermissionForm(OrgModelForm):
|
||||||
users_field = self.fields.get('users')
|
users_field = self.fields.get('users')
|
||||||
if hasattr(users_field, 'queryset'):
|
if hasattr(users_field, 'queryset'):
|
||||||
users_field.queryset = current_org.get_org_users()
|
users_field.queryset = current_org.get_org_users()
|
||||||
|
assets_field = self.fields.get('assets')
|
||||||
|
|
||||||
|
# 前端渲染优化, 防止过多资产
|
||||||
|
if not self.data:
|
||||||
|
instance = kwargs.get('instance')
|
||||||
|
if instance:
|
||||||
|
assets_field.queryset = instance.assets.all()
|
||||||
|
else:
|
||||||
|
assets_field.queryset = Asset.objects.none()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AssetPermission
|
model = AssetPermission
|
||||||
|
|
|
@ -120,8 +120,20 @@ $(document).ready(function () {
|
||||||
.on('click', '#btn_asset_modal_confirm', function () {
|
.on('click', '#btn_asset_modal_confirm', function () {
|
||||||
var assets = asset_table2.selected;
|
var assets = asset_table2.selected;
|
||||||
|
|
||||||
$('#id_assets').val(assets).trigger('change');
|
var options = [];
|
||||||
|
$('#id_assets option').each(function (i, v) {
|
||||||
|
options.push(v.value)
|
||||||
|
});
|
||||||
|
asset_table2.selected_rows.forEach(function (i) {
|
||||||
|
var name = i.hostname + '(' + i.ip + ')';
|
||||||
|
var option = new Option(name, i.id, false, true);
|
||||||
|
|
||||||
|
if (options.indexOf(i.id) === -1) {
|
||||||
|
$('#id_assets').append(option).trigger('change');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#id_assets').val(assets).trigger('change');
|
||||||
$("#asset_list_modal").modal('hide');
|
$("#asset_list_modal").modal('hide');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -528,6 +528,7 @@ jumpserver.initServerSideDataTable = function (options) {
|
||||||
lengthMenu: [[10, 15, 25, 50], [10, 15, 25, 50]]
|
lengthMenu: [[10, 15, 25, 50], [10, 15, 25, 50]]
|
||||||
});
|
});
|
||||||
table.selected = [];
|
table.selected = [];
|
||||||
|
table.selected_rows = [];
|
||||||
table.on('select', function(e, dt, type, indexes) {
|
table.on('select', function(e, dt, type, indexes) {
|
||||||
var $node = table[ type ]( indexes ).nodes().to$();
|
var $node = table[ type ]( indexes ).nodes().to$();
|
||||||
$node.find('input.ipt_check').prop('checked', true);
|
$node.find('input.ipt_check').prop('checked', true);
|
||||||
|
@ -535,6 +536,7 @@ jumpserver.initServerSideDataTable = function (options) {
|
||||||
if (type === 'row') {
|
if (type === 'row') {
|
||||||
var rows = table.rows(indexes).data();
|
var rows = table.rows(indexes).data();
|
||||||
$.each(rows, function (id, row) {
|
$.each(rows, function (id, row) {
|
||||||
|
table.selected_rows.push(row);
|
||||||
if (row.id && $.inArray(row.id, table.selected) === -1){
|
if (row.id && $.inArray(row.id, table.selected) === -1){
|
||||||
table.selected.push(row.id)
|
table.selected.push(row.id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ class TerminalTokenApi(APIView):
|
||||||
if not terminal.user or not terminal.user.access_key:
|
if not terminal.user or not terminal.user.access_key:
|
||||||
return Response("No access key generate", status=401)
|
return Response("No access key generate", status=401)
|
||||||
|
|
||||||
access_key = terminal.user.access_key.first()
|
access_key = terminal.user.access_key()
|
||||||
data = OrderedDict()
|
data = OrderedDict()
|
||||||
data['access_key'] = {'id': access_key.id, 'secret': access_key.secret}
|
data['access_key'] = {'id': access_key.id, 'secret': access_key.secret}
|
||||||
return Response(data, status=200)
|
return Response(data, status=200)
|
||||||
|
|
|
@ -47,7 +47,7 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
def allow_bulk_destroy(self, qs, filtered):
|
def allow_bulk_destroy(self, qs, filtered):
|
||||||
return qs.count() == filtered.count()
|
return qs.count() != filtered.count()
|
||||||
|
|
||||||
|
|
||||||
class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
|
class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
|
||||||
|
|
Loading…
Reference in New Issue