Unverified Commit 84b3e29d authored by BaiJiangJie's avatar BaiJiangJie Committed by GitHub

Merge pull request #3366 from jumpserver/bugfix

Bugfix
parents 334e3bef a33d5cc5
...@@ -7,6 +7,7 @@ from rest_framework import filters ...@@ -7,6 +7,7 @@ from rest_framework import filters
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.http import Http404 from django.http import Http404
from django.conf import settings
from common.permissions import IsOrgAdminOrAppUser, NeedMFAVerify from common.permissions import IsOrgAdminOrAppUser, NeedMFAVerify
from common.utils import get_object_or_none, get_logger from common.utils import get_object_or_none, get_logger
...@@ -110,12 +111,22 @@ class AssetUserViewSet(CommonApiMixin, BulkModelViewSet): ...@@ -110,12 +111,22 @@ class AssetUserViewSet(CommonApiMixin, BulkModelViewSet):
class AssetUserExportViewSet(AssetUserViewSet): class AssetUserExportViewSet(AssetUserViewSet):
serializer_class = serializers.AssetUserExportSerializer serializer_class = serializers.AssetUserExportSerializer
http_method_names = ['get'] http_method_names = ['get']
permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify] permission_classes = [IsOrgAdminOrAppUser]
def get_permissions(self):
if settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA:
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
return super().get_permissions()
class AssetUserAuthInfoApi(generics.RetrieveAPIView): class AssetUserAuthInfoApi(generics.RetrieveAPIView):
serializer_class = serializers.AssetUserAuthInfoSerializer serializer_class = serializers.AssetUserAuthInfoSerializer
permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify] permission_classes = [IsOrgAdminOrAppUser]
def get_permissions(self):
if settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA:
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
return super().get_permissions()
def get_object(self): def get_object(self):
query_params = self.request.query_params query_params = self.request.query_params
......
...@@ -41,8 +41,8 @@ class AssetUserManager: ...@@ -41,8 +41,8 @@ class AssetUserManager:
instances_map = {} instances_map = {}
instances = [] instances = []
for name, backend in self.backends: for name, backend in self.backends:
if name != "db" and self._prefer != name: # if name != "db":
continue # continue
_instances = backend.filter( _instances = backend.filter(
username=username, assets=assets, latest=latest, username=username, assets=assets, latest=latest,
prefer=self._prefer, prefer_id=prefer_id, prefer=self._prefer, prefer_id=prefer_id,
......
...@@ -40,6 +40,7 @@ var prefer = null; ...@@ -40,6 +40,7 @@ var prefer = null;
var lastMFATime = "{{ request.session.MFA_VERIFY_TIME }}"; var lastMFATime = "{{ request.session.MFA_VERIFY_TIME }}";
var testDatetime = "{% trans 'Test datetime: ' %}"; var testDatetime = "{% trans 'Test datetime: ' %}";
var mfaVerifyTTL = "{{ SECURITY_MFA_VERIFY_TTL }}"; var mfaVerifyTTL = "{{ SECURITY_MFA_VERIFY_TTL }}";
var mfaNeedCheck = "{{ SECURITY_VIEW_AUTH_NEED_MFA }}" === "True";
function initAssetUserTable() { function initAssetUserTable() {
var options = { var options = {
...@@ -112,6 +113,10 @@ $(document).ready(function(){ ...@@ -112,6 +113,10 @@ $(document).ready(function(){
authAssetId = $(this).data("asset") ; authAssetId = $(this).data("asset") ;
authHostname = $(this).data("hostname"); authHostname = $(this).data("hostname");
authUsername = $(this).data('user'); authUsername = $(this).data('user');
if (!mfaNeedCheck){
$("#asset_user_auth_view").modal('show');
return
}
var now = new Date(); var now = new Date();
var nowTime = now.getTime() / 1000; var nowTime = now.getTime() / 1000;
if ( !lastMFATime || nowTime - lastMFATime > mfaVerifyTTL ) { if ( !lastMFATime || nowTime - lastMFATime > mfaVerifyTTL ) {
......
...@@ -70,43 +70,6 @@ function initTable() { ...@@ -70,43 +70,6 @@ function initTable() {
var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
return detail_btn.replace('{{ DEFAULT_PK }}', rowData.id); return detail_btn.replace('{{ DEFAULT_PK }}', rowData.id);
}}, }},
{#{targets: 4, createdCell: function (td, cellData) {#}
{# var innerHtml = "";#}
{# var data = cellData.reachable;#}
{# if (data !== 0) {#}
{# innerHtml = "<span class='text-navy'>" + data + "</span>";#}
{# } else {#}
{# innerHtml = "<span>" + data + "</span>";#}
{# }#}
{# $(td).html(innerHtml)#}
{#}},#}
{#{targets: 5, createdCell: function (td, cellData) {#}
{# var data = cellData.unreachable;#}
{# var innerHtml = "";#}
{# if (data !== 0) {#}
{# innerHtml = "<span class='text-danger'>" + data + "</span>";#}
{# } else {#}
{# innerHtml = "<span>" + data + "</span>";#}
{# }#}
{# $(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + data + '">' + innerHtml + '</span>');#}
{#}},#}
{#{targets: 6, createdCell: function (td, cellData, rowData) {#}
{# var val = 0;#}
{# var innerHtml = "";#}
{# var total = rowData.assets_amount;#}
{# var reachable = cellData.reachable;#}
{# if (total !== 0) {#}
{# val = reachable/total * 100;#}
{# }#}
{##}
{# if (val === 100) {#}
{# innerHtml = "<span class='text-navy'>" + val + "% </span>";#}
{# } else {#}
{# var num = new Number(val);#}
{# innerHtml = "<span class='text-danger'>" + num.toFixed(1) + "% </span>";#}
{# }#}
{# $(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');#}
{#}},#}
{targets: 5, createdCell: function (td, cellData, rowData) { {targets: 5, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var update_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_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_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
...@@ -116,7 +79,7 @@ function initTable() { ...@@ -116,7 +79,7 @@ function initTable() {
columns: [ columns: [
{data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount", orderable: false}, {data: function(){return ""}}, {data: "name"}, {data: "username" }, {data: "assets_amount", orderable: false},
{#{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"},#} {#{data: "connectivity_amount"}, {data: "connectivity_amount"}, {data: "connectivity_amount"},#}
{data: "comment"}, {data: "id", orderable: false} {data: "comment"}, {data: "id", orderable: false, width: "100px"}
] ]
}; };
admin_user_table = jumpserver.initServerSideDataTable(options); admin_user_table = jumpserver.initServerSideDataTable(options);
......
...@@ -177,7 +177,7 @@ function initTable() { ...@@ -177,7 +177,7 @@ function initTable() {
data: "connectivity", data: "connectivity",
orderable: false, orderable: false,
width: '60px' width: '60px'
}, {data: "id", orderable: false} }, {data: "id", orderable: false, width: "100px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
......
...@@ -63,7 +63,8 @@ function initTable() { ...@@ -63,7 +63,8 @@ function initTable() {
ajax_url: '{% url "api-assets:cmd-filter-list" %}', ajax_url: '{% url "api-assets:cmd-filter-list" %}',
columns: [ columns: [
{data: "id"}, {data: "name" }, {data: "rules", orderable: false}, {data: "id"}, {data: "name" }, {data: "rules", orderable: false},
{data: "system_users", orderable: false}, {data: "comment"}, {data: "id", orderable: false} {data: "system_users", orderable: false}, {data: "comment"},
{data: "id", orderable: false, width: "100px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
......
...@@ -59,7 +59,7 @@ function initTable() { ...@@ -59,7 +59,7 @@ function initTable() {
ajax_url: '{% url "api-assets:domain-list" %}', ajax_url: '{% url "api-assets:domain-list" %}',
columns: [ columns: [
{data: "id"}, {data: "name" }, {data: "asset_count", orderable: false }, {data: "id"}, {data: "name" }, {data: "asset_count", orderable: false },
{data: "gateway_count", orderable: false }, {data: "comment" }, {data: "id", orderable: false} {data: "gateway_count", orderable: false }, {data: "comment" }, {data: "id", orderable: false, width: "100px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
......
...@@ -44,7 +44,8 @@ function initTable() { ...@@ -44,7 +44,8 @@ function initTable() {
ajax_url: '{% url "api-assets:label-list" %}?sort=name', ajax_url: '{% url "api-assets:label-list" %}?sort=name',
columns: [ columns: [
{data: "id"}, {data: "name" }, {data: "value" }, {data: "id"}, {data: "name" }, {data: "value" },
{data: "asset_count", orderable: false}, {data: "id", orderable: false} {data: "asset_count", orderable: false},
{data: "id", orderable: false, width: "100px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
......
...@@ -3,10 +3,6 @@ ...@@ -3,10 +3,6 @@
{% block help_message %} {% block help_message %}
<div class="alert alert-info help-message"> <div class="alert alert-info help-message">
{# 系统用户是 Jumpserver跳转登录资产时使用的用户,可以理解为登录资产用户,如 web, sa, dba(`ssh web@some-host`), 而不是使用某个用户的用户名跳转登录服务器(`ssh xiaoming@some-host`);#}
{# 简单来说是 用户使用自己的用户名登录Jumpserver, Jumpserver使用系统用户登录资产。#}
{# 系统用户创建时,如果选择了自动推送 Jumpserver会使用ansible自动推送系统用户到资产中,如果资产(交换机、windows)不支持ansible, 请手动填写账号密码。#}
{# 目前还不支持Windows的自动推送#}
{% trans 'System user is Jumpserver jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); '%} {% trans 'System user is Jumpserver jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); '%}
{% trans 'In simple terms, users log into Jumpserver using their own username, and Jumpserver uses system users to log into assets. '%} {% trans 'In simple terms, users log into Jumpserver using their own username, and Jumpserver uses system users to log into assets. '%}
{% trans 'When system users are created, if you choose auto push Jumpserver to use Ansible push system users into the asset, if the asset (Switch) does not support ansible, please manually fill in the account password.' %} {% trans 'When system users are created, if you choose auto push Jumpserver to use Ansible push system users into the asset, if the asset (Switch) does not support ansible, please manually fill in the account password.' %}
...@@ -91,7 +87,7 @@ function initTable() { ...@@ -91,7 +87,7 @@ function initTable() {
columns: [ columns: [
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"},
{data: "login_mode"}, {data: "assets_amount", orderable: false }, {data: "login_mode"}, {data: "assets_amount", orderable: false },
{data: "comment" }, {data: "id", orderable: false } {data: "comment" }, {data: "id", orderable: false, width: "100px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
......
...@@ -182,3 +182,7 @@ def encrypt_password(password, salt=None): ...@@ -182,3 +182,7 @@ def encrypt_password(password, salt=None):
def get_signer(): def get_signer():
signer = Signer(settings.SECRET_KEY) signer = Signer(settings.SECRET_KEY)
return signer return signer
def ensure_last_char_is_ascii(data):
remain = ''
...@@ -361,6 +361,7 @@ defaults = { ...@@ -361,6 +361,7 @@ defaults = {
'TERMINAL_COMMAND_STORAGE': {}, 'TERMINAL_COMMAND_STORAGE': {},
'SECURITY_MFA_AUTH': False, 'SECURITY_MFA_AUTH': False,
'SECURITY_SERVICE_ACCOUNT_REGISTRATION': True, 'SECURITY_SERVICE_ACCOUNT_REGISTRATION': True,
'SECURITY_VIEW_AUTH_NEED_MFA': True,
'SECURITY_LOGIN_LIMIT_COUNT': 7, 'SECURITY_LOGIN_LIMIT_COUNT': 7,
'SECURITY_LOGIN_LIMIT_TIME': 30, 'SECURITY_LOGIN_LIMIT_TIME': 30,
'SECURITY_MAX_IDLE_TIME': 30, 'SECURITY_MAX_IDLE_TIME': 30,
......
...@@ -18,6 +18,7 @@ def jumpserver_processor(request): ...@@ -18,6 +18,7 @@ def jumpserver_processor(request):
'COPYRIGHT': 'FIT2CLOUD 飞致云' + ' © 2014-2019', 'COPYRIGHT': 'FIT2CLOUD 飞致云' + ' © 2014-2019',
'SECURITY_COMMAND_EXECUTION': settings.SECURITY_COMMAND_EXECUTION, 'SECURITY_COMMAND_EXECUTION': settings.SECURITY_COMMAND_EXECUTION,
'SECURITY_MFA_VERIFY_TTL': settings.SECURITY_MFA_VERIFY_TTL, 'SECURITY_MFA_VERIFY_TTL': settings.SECURITY_MFA_VERIFY_TTL,
'SECURITY_VIEW_AUTH_NEED_MFA': settings.CONFIG.SECURITY_VIEW_AUTH_NEED_MFA,
} }
return context return context
......
This diff is collapsed.
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
<title>{% trans 'Task log' %}</title> <title>{% trans 'Task log' %}</title>
<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script> <script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
<script src="{% static 'js/plugins/xterm/xterm.js' %}"></script> <script src="{% static 'js/plugins/xterm/xterm.js' %}"></script>
<script src="{% static 'js/plugins/xterm/addons/fit/fit.js' %}"></script>
<link rel="stylesheet" href="{% static 'js/plugins/xterm/xterm.css' %}" /> <link rel="stylesheet" href="{% static 'js/plugins/xterm/xterm.css' %}" />
<style> <style>
body { body {
...@@ -40,7 +41,7 @@ ...@@ -40,7 +41,7 @@
disableStdin: true disableStdin: true
}); });
term.open(document.getElementById('term')); term.open(document.getElementById('term'));
term.resize(120, 30); window.fit.fit(term);
ws = new WebSocket(wsURL); ws = new WebSocket(wsURL);
ws.onmessage = function(e) { ws.onmessage = function(e) {
...@@ -61,5 +62,7 @@ ...@@ -61,5 +62,7 @@
term.write("Connect websocket server error") term.write("Connect websocket server error")
} }
} }
}).on('resize', window, function () {
window.fit.fit(term);
}); });
</script> </script>
...@@ -80,9 +80,7 @@ ...@@ -80,9 +80,7 @@
<select class="select2 form-control" <select class="select2 form-control"
id="system-users-select"> id="system-users-select">
{% for s in system_users %} {% for s in system_users %}
{% if s.protocol == 'ssh' and s.login_mode == 'auto' %} <option value="{{ s.id }}" {% if s.protocol != 'ssh' or s.login_mode != 'auto' %}disabled{% endif %}>{{ s }}</option>
<option value="{{ s.id }}">{{ s }}</option>
{% endif %}
{% endfor %} {% endfor %}
</select> </select>
<button type="button" <button type="button"
...@@ -104,41 +102,6 @@ ...@@ -104,41 +102,6 @@
var url = null; var url = null;
var treeUrl = "{% url 'api-perms:my-nodes-with-assets-as-tree' %}?cache_policy=1"; var treeUrl = "{% url 'api-perms:my-nodes-with-assets-as-tree' %}?cache_policy=1";
function proposeGeometry(term) {
if (!term.element.parentElement) {
return null;
}
var parentElementStyle = window.getComputedStyle(term.element.parentElement);
var parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
var parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue('width')));
var elementStyle = window.getComputedStyle(term.element);
var elementPadding = {
top: parseInt(elementStyle.getPropertyValue('padding-top')),
bottom: parseInt(elementStyle.getPropertyValue('padding-bottom')),
right: parseInt(elementStyle.getPropertyValue('padding-right')),
left: parseInt(elementStyle.getPropertyValue('padding-left'))
};
var elementPaddingVer = elementPadding.top + elementPadding.bottom;
var elementPaddingHor = elementPadding.right + elementPadding.left;
var availableHeight = parentElementHeight - elementPaddingVer;
var availableWidth = parentElementWidth - elementPaddingHor - term._core.viewport.scrollBarWidth;
var geometry = {
cols: Math.floor(availableWidth / term._core.renderer.dimensions.actualCellWidth),
rows: Math.floor(availableHeight / term._core.renderer.dimensions.actualCellHeight)
};
return geometry;
}
function fit(term) {
var geometry = proposeGeometry(term);
if (geometry) {
if (term.rows !== geometry.rows || term.cols !== geometry.cols) {
term._core.renderer.clear();
term.resize(geometry.cols, geometry.rows);
}
}
}
function initTree() { function initTree() {
$('#assetTree').html("{% trans 'Loading' %}" + '..'); $('#assetTree').html("{% trans 'Loading' %}" + '..');
if (systemUserId) { if (systemUserId) {
...@@ -159,12 +122,6 @@ ...@@ -159,12 +122,6 @@
enable: true enable: true
} }
}, },
async: {
enable: true,
url: url,
autoParam: ["id=key", "name=n", "level=lv"],
type: 'get'
},
edit: { edit: {
enable: true, enable: true,
showRemoveBtn: false, showRemoveBtn: false,
...@@ -248,13 +205,15 @@ ...@@ -248,13 +205,15 @@
lineHeight: 1, lineHeight: 1,
rightClickSelectsWord: true, rightClickSelectsWord: true,
disableStdin: true, disableStdin: true,
cursorBlink: false,
theme: { theme: {
background: '#1f1b1b' background: '#1f1b1b'
} }
}); });
term.open(document.getElementById('term')); term.open(document.getElementById('term'));
var msg = "{% trans 'Select the left asset, select the running system user, execute command in batch' %}" + "\r\n"; var msg = "{% trans 'Select the left asset, select the running system user, execute command in batch' %}" + "\r\n";
fit(term); window.fit.fit(term);
{#fit(term);#}
term.write(msg); term.write(msg);
var scheme = document.location.protocol === "https:" ? "wss" : "ws"; var scheme = document.location.protocol === "https:" ? "wss" : "ws";
......
...@@ -70,7 +70,7 @@ class CommandExecutionStartView(PermissionsMixin, TemplateView): ...@@ -70,7 +70,7 @@ class CommandExecutionStartView(PermissionsMixin, TemplateView):
user = self.request.user user = self.request.user
with tmp_to_root_org(): with tmp_to_root_org():
util = AssetPermissionUtilV2(user) util = AssetPermissionUtilV2(user)
system_users = [s for s in util.get_system_users() if s.protocol == 'ssh'] system_users = util.get_system_users()
return system_users return system_users
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
......
...@@ -3,11 +3,13 @@ import os ...@@ -3,11 +3,13 @@ import os
import threading import threading
import json import json
from celery.result import AsyncResult from common.utils import get_logger
from .celery.utils import get_celery_task_log_path from .celery.utils import get_celery_task_log_path
from channels.generic.websocket import JsonWebsocketConsumer from channels.generic.websocket import JsonWebsocketConsumer
logger = get_logger(__name__)
class CeleryLogWebsocket(JsonWebsocketConsumer): class CeleryLogWebsocket(JsonWebsocketConsumer):
disconnected = False disconnected = False
...@@ -22,6 +24,7 @@ class CeleryLogWebsocket(JsonWebsocketConsumer): ...@@ -22,6 +24,7 @@ class CeleryLogWebsocket(JsonWebsocketConsumer):
self.handle_task(task_id) self.handle_task(task_id)
def handle_task(self, task_id): def handle_task(self, task_id):
logger.info("Task id: {}".format(task_id))
log_path = get_celery_task_log_path(task_id) log_path = get_celery_task_log_path(task_id)
def func(): def func():
...@@ -34,19 +37,24 @@ class CeleryLogWebsocket(JsonWebsocketConsumer): ...@@ -34,19 +37,24 @@ class CeleryLogWebsocket(JsonWebsocketConsumer):
continue continue
self.send_json({'message': '\r\n'}) self.send_json({'message': '\r\n'})
try: try:
task_log_f = open(log_path) logger.debug('Task log path: {}'.format(log_path))
task_log_f = open(log_path, 'rb')
break break
except OSError: except OSError:
return return
if not task_log_f:
return
while not self.disconnected: while not self.disconnected:
data = task_log_f.readline() data = task_log_f.readline()
if data: if data:
data = data.replace('\n', '\r\n') data = data.replace(b'\n', b'\r\n')
self.send_json({'message': data, 'task': task_id}) self.send_json({'message': data.decode(errors='ignore'), 'task': task_id})
if data.startswith('Task') and data.find('succeeded'): if data.startswith(b'Task') and data.find(b'succeeded'):
break break
time.sleep(0.2) time.sleep(0.1)
task_log_f.close() task_log_f.close()
thread = threading.Thread(target=func) thread = threading.Thread(target=func)
thread.start() thread.start()
......
...@@ -190,7 +190,7 @@ function initTable() { ...@@ -190,7 +190,7 @@ function initTable() {
{data: "id"}, {data: "name"}, {data: "users", orderable: false}, {data: "id"}, {data: "name"}, {data: "users", orderable: false},
{data: "user_groups", orderable: false}, {data: "assets", orderable: false}, {data: "user_groups", orderable: false}, {data: "assets", orderable: false},
{data: "nodes", orderable: false}, {data: "system_users", orderable: false}, {data: "nodes", orderable: false}, {data: "system_users", orderable: false},
{data: "is_valid", orderable: false}, {data: "id", orderable: false} {data: "is_valid", orderable: false}, {data: "id", orderable: false, width: "100px"}
], ],
select: {}, select: {},
op_html: $('#actions').html() op_html: $('#actions').html()
......
...@@ -62,10 +62,10 @@ asset_permission_urlpatterns = [ ...@@ -62,10 +62,10 @@ asset_permission_urlpatterns = [
path('user-groups/<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGroupGrantedAssetSystemUsersApi.as_view(), name='user-group-asset-system-users'), path('user-groups/<uuid:pk>/assets/<uuid:asset_id>/system-users/', api.UserGroupGrantedAssetSystemUsersApi.as_view(), name='user-group-asset-system-users'),
# 用户和资产授权变更 # 用户和资产授权变更
path('asset-permissions/<uuid:pk>/user/remove/', api.AssetPermissionRemoveUserApi.as_view(), name='asset-permission-remove-user'), path('asset-permissions/<uuid:pk>/users/remove/', api.AssetPermissionRemoveUserApi.as_view(), name='asset-permission-remove-user'),
path('asset-permissions/<uuid:pk>/user/add/', api.AssetPermissionAddUserApi.as_view(), name='asset-permission-add-user'), path('asset-permissions/<uuid:pk>/users/add/', api.AssetPermissionAddUserApi.as_view(), name='asset-permission-add-user'),
path('asset-permissions/<uuid:pk>/asset/remove/', api.AssetPermissionRemoveAssetApi.as_view(), name='asset-permission-remove-asset'), path('asset-permissions/<uuid:pk>/assets/remove/', api.AssetPermissionRemoveAssetApi.as_view(), name='asset-permission-remove-asset'),
path('asset-permissions/<uuid:pk>/asset/add/', api.AssetPermissionAddAssetApi.as_view(), name='asset-permission-add-asset'), path('asset-permissions/<uuid:pk>/assets/add/', api.AssetPermissionAddAssetApi.as_view(), name='asset-permission-add-asset'),
# 授权规则中授权的资产 # 授权规则中授权的资产
path('asset-permissions/<uuid:pk>/assets/', api.AssetPermissionAssetsApi.as_view(), name='asset-permission-assets'), path('asset-permissions/<uuid:pk>/assets/', api.AssetPermissionAssetsApi.as_view(), name='asset-permission-assets'),
...@@ -75,7 +75,7 @@ asset_permission_urlpatterns = [ ...@@ -75,7 +75,7 @@ asset_permission_urlpatterns = [
path('asset-permissions/user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'), path('asset-permissions/user/actions/', api.GetUserAssetPermissionActionsApi.as_view(), name='get-user-asset-permission-actions'),
# 刷新缓存 # 刷新缓存
path('asset-permissions/user/cache/refresh/', api.RefreshAssetPermissionCacheApi.as_view(), name='refresh-asset-permission-cache'), path('asset-permissions/cache/refresh/', api.RefreshAssetPermissionCacheApi.as_view(), name='refresh-asset-permission-cache'),
] ]
...@@ -99,10 +99,10 @@ remote_app_permission_urlpatterns = [ ...@@ -99,10 +99,10 @@ remote_app_permission_urlpatterns = [
path('remote-app-permissions/user/validate/', api.ValidateUserRemoteAppPermissionApi.as_view(), name='validate-user-remote-app-permission'), path('remote-app-permissions/user/validate/', api.ValidateUserRemoteAppPermissionApi.as_view(), name='validate-user-remote-app-permission'),
# 用户和RemoteApp变更 # 用户和RemoteApp变更
path('remote-app-permissions/<uuid:pk>/user/add/', api.RemoteAppPermissionAddUserApi.as_view(), name='remote-app-permission-add-user'), path('remote-app-permissions/<uuid:pk>/users/add/', api.RemoteAppPermissionAddUserApi.as_view(), name='remote-app-permission-add-user'),
path('remote-app-permissions/<uuid:pk>/user/remove/', api.RemoteAppPermissionRemoveUserApi.as_view(), name='remote-app-permission-remove-user'), path('remote-app-permissions/<uuid:pk>/users/remove/', api.RemoteAppPermissionRemoveUserApi.as_view(), name='remote-app-permission-remove-user'),
path('remote-app-permissions/<uuid:pk>/remote-app/remove/', api.RemoteAppPermissionRemoveRemoteAppApi.as_view(), name='remote-app-permission-remove-remote-app'), path('remote-app-permissions/<uuid:pk>/remote-apps/remove/', api.RemoteAppPermissionRemoveRemoteAppApi.as_view(), name='remote-app-permission-remove-remote-app'),
path('remote-app-permissions/<uuid:pk>/remote-app/add/', api.RemoteAppPermissionAddRemoteAppApi.as_view(), name='remote-app-permission-add-remote-app'), path('remote-app-permissions/<uuid:pk>/remote-apps/add/', api.RemoteAppPermissionAddRemoteAppApi.as_view(), name='remote-app-permission-add-remote-app'),
] ]
old_version_urlpatterns = [ old_version_urlpatterns = [
......
...@@ -234,6 +234,9 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): ...@@ -234,6 +234,9 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
if user_tree.contains(key): if user_tree.contains(key):
nodes_single_assets.pop(key) nodes_single_assets.pop(key)
if not nodes_single_assets:
return
# 如果要设置到ungroup中 # 如果要设置到ungroup中
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE: if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
node_key = Node.ungrouped_key node_key = Node.ungrouped_key
...@@ -336,8 +339,8 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): ...@@ -336,8 +339,8 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
self.add_direct_nodes_to_user_tree(user_tree) self.add_direct_nodes_to_user_tree(user_tree)
self.add_single_assets_node_to_user_tree(user_tree) self.add_single_assets_node_to_user_tree(user_tree)
self.parse_user_tree_to_full_tree(user_tree) self.parse_user_tree_to_full_tree(user_tree)
self.add_empty_node_if_need(user_tree)
self.add_favorite_node_if_need(user_tree) self.add_favorite_node_if_need(user_tree)
self.add_empty_node_if_need(user_tree)
self.set_user_tree_to_cache_if_need(user_tree) self.set_user_tree_to_cache_if_need(user_tree)
self.set_user_tree_to_local(user_tree) self.set_user_tree_to_local(user_tree)
return user_tree return user_tree
...@@ -487,6 +490,7 @@ class ParserNode: ...@@ -487,6 +490,7 @@ class ParserNode:
'ip': asset.ip, 'ip': asset.ip,
'protocols': asset.protocols_as_list, 'protocols': asset.protocols_as_list,
'platform': asset.platform, 'platform': asset.platform,
"org_name": asset.org_name,
}, },
} }
} }
......
...@@ -5,6 +5,7 @@ import os ...@@ -5,6 +5,7 @@ import os
import json import json
import jms_storage import jms_storage
from smtplib import SMTPSenderRefused
from rest_framework import generics from rest_framework import generics
from rest_framework.views import Response, APIView from rest_framework.views import Response, APIView
from django.conf import settings from django.conf import settings
...@@ -41,9 +42,20 @@ class MailTestingAPI(APIView): ...@@ -41,9 +42,20 @@ class MailTestingAPI(APIView):
email_from = email_from or email_host_user email_from = email_from or email_host_user
email_recipient = email_recipient or email_from email_recipient = email_recipient or email_from
send_mail(subject, message, email_from, [email_recipient]) send_mail(subject, message, email_from, [email_recipient])
except SMTPSenderRefused as e:
resp = e.smtp_error
if isinstance(resp, bytes):
for coding in ('gbk', 'utf8'):
try:
resp = resp.decode(coding)
except UnicodeDecodeError:
continue
else:
break
return Response({"error": str(resp)}, status=401)
except Exception as e: except Exception as e:
print(e)
return Response({"error": str(e)}, status=401) return Response({"error": str(e)}, status=401)
return Response({"msg": self.success_message.format(email_recipient)}) return Response({"msg": self.success_message.format(email_recipient)})
else: else:
return Response({"error": str(serializer.errors)}, status=401) return Response({"error": str(serializer.errors)}, status=401)
......
...@@ -201,7 +201,7 @@ function formSubmit(props) { ...@@ -201,7 +201,7 @@ function formSubmit(props) {
var errors = jqXHR.responseJSON; var errors = jqXHR.responseJSON;
var noneFieldErrorRef = props.form.children('.alert-danger'); var noneFieldErrorRef = props.form.children('.alert-danger');
if (noneFieldErrorRef.length !== 1) { if (noneFieldErrorRef.length !== 1) {
props.form.prepend('<div class="alert alert-danger" style="display: none"></div>'); props.form.prepend('<div class="alert alert-danger has-error" style="display: none"></div>');
noneFieldErrorRef = props.form.children('.alert-danger'); noneFieldErrorRef = props.form.children('.alert-danger');
} }
var noneFieldErrorMsg = ""; var noneFieldErrorMsg = "";
...@@ -247,6 +247,7 @@ function formSubmit(props) { ...@@ -247,6 +247,7 @@ function formSubmit(props) {
noneFieldErrorRef.css('display', 'block'); noneFieldErrorRef.css('display', 'block');
noneFieldErrorRef.html(noneFieldErrorMsg); noneFieldErrorRef.html(noneFieldErrorMsg);
} }
$('.has-error').get(0).scrollIntoView();
} }
}) })
...@@ -453,6 +454,7 @@ jumpserver.initDataTable = function (options) { ...@@ -453,6 +454,7 @@ jumpserver.initDataTable = function (options) {
{ {
targets: 0, targets: 0,
orderable: false, orderable: false,
width: "20px",
createdCell: function (td, cellData) { createdCell: function (td, cellData) {
$(td).html('<input type="checkbox" class="text-center ipt_check" id=99991937>'.replace('99991937', cellData)); $(td).html('<input type="checkbox" class="text-center ipt_check" id=99991937>'.replace('99991937', cellData));
} }
...@@ -550,6 +552,7 @@ jumpserver.initServerSideDataTable = function (options) { ...@@ -550,6 +552,7 @@ jumpserver.initServerSideDataTable = function (options) {
{ {
targets: 0, targets: 0,
orderable: false, orderable: false,
width: "20px",
createdCell: function (td, cellData) { createdCell: function (td, cellData) {
$(td).html('<input type="checkbox" class="text-center ipt_check" id=99991937>'.replace('99991937', cellData)); $(td).html('<input type="checkbox" class="text-center ipt_check" id=99991937>'.replace('99991937', cellData));
} }
......
...@@ -82,7 +82,7 @@ function initTable() { ...@@ -82,7 +82,7 @@ function initTable() {
], ],
ajax_url: '{% url "api-users:user-group-list" %}?display=1', ajax_url: '{% url "api-users:user-group-list" %}?display=1',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "users", orderable: false}, columns: [{data: function(){return ""}}, {data: "name" }, {data: "users", orderable: false},
{data: "comment"}, {data: "id", orderable: false }], {data: "comment"}, {data: "id", orderable: false, width:"100px"}],
order: [], order: [],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
......
...@@ -125,7 +125,7 @@ function initTable() { ...@@ -125,7 +125,7 @@ function initTable() {
{data: "groups_display", orderable: false}, {data: "groups_display", orderable: false},
{data: "source"}, {data: "source"},
{data: "is_valid", orderable: false}, {data: "is_valid", orderable: false},
{data: "id", orderable: false} {data: "id", orderable: false, width: "100px"}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
......
...@@ -22,21 +22,20 @@ logger = logging.getLogger('jumpserver') ...@@ -22,21 +22,20 @@ logger = logging.getLogger('jumpserver')
def construct_user_created_email_body(user): def construct_user_created_email_body(user):
default_body = _(""" default_body = _("""
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> <div>
<p style="text-indent:2em;"> <p>Your account has been created successfully</p>
<span> <div>
Username: %(username)s. Username: %(username)s
</span> <br/>
<span> Password: <a href="%(rest_password_url)s?token=%(rest_password_token)s">
<a href="%(rest_password_url)s?token=%(rest_password_token)s">click here to set your password</a> click here to set your password</a>
</span> (This link is valid for 1 hour. After it expires, <a href="%(forget_password_url)s?email=%(email)s">request new one</a>)
<span> </div>
This link is valid for 1 hour. After it expires, <a href="%(forget_password_url)s?email=%(email)s">request new one</a> <div>
</span> <p>---</p>
<span>
<a href="%(login_url)s">Login direct</a> <a href="%(login_url)s">Login direct</a>
</span> </div>
</p> </div>
""") % { """) % {
'username': user.username, 'username': user.username,
'rest_password_url': reverse('users:reset-password', external=True), 'rest_password_url': reverse('users:reset-password', external=True),
......
...@@ -65,6 +65,7 @@ logging.basicConfig( ...@@ -65,6 +65,7 @@ logging.basicConfig(
EXIT_EVENT = threading.Event() EXIT_EVENT = threading.Event()
LOCK = threading.Lock() LOCK = threading.Lock()
files_preserve = [] files_preserve = []
STOP_TIMEOUT = 10
logger = logging.getLogger() logger = logging.getLogger()
...@@ -120,7 +121,7 @@ def check_pid(pid): ...@@ -120,7 +121,7 @@ def check_pid(pid):
def get_pid_file_path(s): def get_pid_file_path(s):
return os.path.join('/tmp', '{}.pid'.format(s)) return os.path.join(TMP_DIR, '{}.pid'.format(s))
def get_log_file_path(s): def get_log_file_path(s):
...@@ -433,9 +434,19 @@ def stop_service(srv, sig=15): ...@@ -433,9 +434,19 @@ def stop_service(srv, sig=15):
if not is_running(s): if not is_running(s):
show_service_status(s) show_service_status(s)
continue continue
logging.info("Stop service: {}".format(s)) print("Stop service: {}".format(s), end='')
pid = get_pid(s) pid = get_pid(s)
os.kill(pid, sig) os.kill(pid, sig)
for i in range(STOP_TIMEOUT):
if i == STOP_TIMEOUT - 1:
print("\033[31m Error\033[0m")
if not is_running(s):
print("\033[32m Ok\033[0m")
break
else:
time.sleep(1)
continue
with LOCK: with LOCK:
processes.pop(s, None) processes.pop(s, None)
...@@ -472,9 +483,9 @@ def show_service_status(s): ...@@ -472,9 +483,9 @@ def show_service_status(s):
for ns in services_set: for ns in services_set:
if is_running(ns): if is_running(ns):
pid = get_pid(ns) pid = get_pid(ns)
logging.info("{} is running: {}".format(ns, pid)) print("{} is running: {}".format(ns, pid))
else: else:
logging.info("{} is stopped".format(ns)) print("{} is stopped".format(ns))
if __name__ == '__main__': if __name__ == '__main__':
...@@ -499,6 +510,7 @@ if __name__ == '__main__': ...@@ -499,6 +510,7 @@ if __name__ == '__main__':
) )
parser.add_argument('-d', '--daemon', nargs="?", const=1) parser.add_argument('-d', '--daemon', nargs="?", const=1)
parser.add_argument('-w', '--worker', type=int, nargs="?", const=4) parser.add_argument('-w', '--worker', type=int, nargs="?", const=4)
parser.add_argument('-f', '--force', nargs="?", const=1)
args = parser.parse_args() args = parser.parse_args()
if args.daemon: if args.daemon:
DAEMON = True DAEMON = True
...@@ -513,7 +525,10 @@ if __name__ == '__main__': ...@@ -513,7 +525,10 @@ if __name__ == '__main__':
start_services_and_watch(srv) start_services_and_watch(srv)
os._exit(0) os._exit(0)
elif action == "stop": elif action == "stop":
stop_service(srv) if args.force:
stop_service_force(srv)
else:
stop_service(srv)
elif action == "restart": elif action == "restart":
DAEMON = True DAEMON = True
stop_service(srv) stop_service(srv)
......
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