Unverified Commit 1d462aea authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #1796 from jumpserver/dev

Dev
parents 9ab2f4bc 88a29c0a
......@@ -4,7 +4,7 @@ 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
from .system_user import AssetSystemUserSerializer
__all__ = [
......
......@@ -5,7 +5,6 @@ from rest_framework_bulk.routes import BulkRouter
app_name = 'assets'
router = BulkRouter()
router.register(r'assets', api.AssetViewSet, 'asset')
router.register(r'admin-user', api.AdminUserViewSet, 'admin-user')
......
......@@ -197,7 +197,6 @@ class SecuritySettingForm(BaseForm):
)
# upper case
SECURITY_PASSWORD_UPPER_CASE = forms.BooleanField(
initial=False, required=False,
label=_("Must contain capital letters"),
help_text=_(
......
......@@ -71,6 +71,7 @@ class BulkSerializerMixin(object):
ret = super(BulkSerializerMixin, self).to_internal_value(data)
id_attr = getattr(self.Meta, 'update_lookup_field', 'id')
if self.context.get('view'):
request_method = getattr(getattr(self.context.get('view'), 'request'), 'method', '')
# add update_lookup_field field back to validated data
# since super by default strips out read-only fields
......
......@@ -72,6 +72,7 @@ INSTALLED_APPS = [
'captcha',
'django_celery_beat',
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-04 16:00+0800\n"
"POT-Creation-Date: 2018-09-07 12:00+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
......@@ -759,7 +759,7 @@ msgstr "重置"
#: common/templates/common/terminal_setting.html:108
#: perms/templates/perms/asset_permission_create_update.html:70
#: terminal/templates/terminal/command_list.html:103
#: terminal/templates/terminal/session_list.html:126
#: terminal/templates/terminal/session_list.html:127
#: terminal/templates/terminal/terminal_update.html:48
#: users/templates/users/_user.html:47
#: users/templates/users/forgot_password.html:45
......@@ -1759,7 +1759,7 @@ msgid ""
"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."
msgstr ""
"提示:(单位 / 分钟)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录."
"提示: (单位: 分钟) 当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录."
#: common/forms.py:187
msgid "Connection max idle time"
......@@ -1768,47 +1768,47 @@ msgstr "SSH最大空闲时间"
#: common/forms.py:189
msgid ""
"If idle time more than it, disconnect connection(only ssh now) Unit: minute"
msgstr "如果超过该配置没有操作,连接会被断开"
msgstr "提示: (单位: 分钟) 如果超过该配置没有操作,连接会被断开(仅ssh) "
#: common/forms.py:195
msgid "Password minimum length"
msgstr "密码最小长度 "
#: common/forms.py:202
#: common/forms.py:201
msgid "Must contain capital letters"
msgstr "必须包含大写字母"
#: common/forms.py:204
#: common/forms.py:203
msgid ""
"After opening, the user password changes and resets must contain uppercase "
"letters"
msgstr "开启后,用户密码修改、重置必须包含大写字母"
#: common/forms.py:210
#: common/forms.py:209
msgid "Must contain lowercase letters"
msgstr "必须包含小写字母"
#: common/forms.py:211
#: common/forms.py:210
msgid ""
"After opening, the user password changes and resets must contain lowercase "
"letters"
msgstr "开启后,用户密码修改、重置必须包含小写字母"
#: common/forms.py:217
#: common/forms.py:216
msgid "Must contain numeric characters"
msgstr "必须包含数字字符"
#: common/forms.py:218
#: common/forms.py:217
msgid ""
"After opening, the user password changes and resets must contain numeric "
"characters"
msgstr "开启后,用户密码修改、重置必须包含数字字符"
#: common/forms.py:224
#: common/forms.py:223
msgid "Must contain special characters"
msgstr "必须包含特殊字符"
#: common/forms.py:225
#: common/forms.py:224
msgid ""
"After opening, the user password changes and resets must contain special "
"characters"
......@@ -2717,10 +2717,20 @@ msgstr "终断"
msgid "Terminate selected"
msgstr "终断所选"
#: terminal/templates/terminal/session_list.html:142
#: terminal/templates/terminal/session_list.html:123
msgid "Confirm finished"
msgstr "确认已完成"
#: terminal/templates/terminal/session_list.html:143
msgid "Terminate task send, waiting ..."
msgstr "终断任务已发送,请等待"
#: terminal/templates/terminal/session_list.html:156
#, fuzzy
#| msgid "MFA enable success"
msgid "Finish session success"
msgstr "MFA 绑定成功"
#: terminal/templates/terminal/terminal_detail.html:13
#: terminal/views/terminal.py:59
msgid "Terminal detail"
......@@ -2805,7 +2815,7 @@ msgstr "MFA认证失败"
#: users/api/user.py:134
msgid "Could not reset self otp, use profile reset instead"
msgstr ""
msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
#: users/authentication.py:56
msgid "Invalid signature header. No credentials provided."
......
......@@ -154,8 +154,10 @@ function activeNav() {
function APIUpdateAttr(props) {
// props = {url: .., body: , success: , error: , method: ,}
props = props || {};
var success_message = props.success_message || gettext('Update is successful!');
var fail_message = props.fail_message || gettext('An unknown error occurred while updating..');
var user_success_message = props.success_message;
var default_success_message = gettext('Update is successful!');
var user_fail_message = props.fail_message;
var default_failed_message = gettext('An unknown error occurred while updating..');
var flash_message = props.flash_message || true;
if (props.flash_message === false){
flash_message = false;
......@@ -169,14 +171,36 @@ function APIUpdateAttr(props) {
dataType: props.data_type || "json"
}).done(function(data, textStatue, jqXHR) {
if (flash_message) {
toastr.success(success_message);
var msg = "";
if (user_fail_message) {
msg = user_success_message;
} else {
msg = default_success_message;
}
toastr.success(msg);
}
if (typeof props.success === 'function') {
return props.success(data);
}
}).fail(function(jqXHR, textStatus, errorThrown) {
if (flash_message) {
toastr.error(fail_message);
var msg = "";
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
if (user_fail_message) {
msg = user_fail_message;
} else if (jqXHR.responseJSON) {
if (jqXHR.responseJSON.error) {
msg = jqXHR.responseJSON.error
} else if (jqXHR.responseJSON.msg) {
msg = jqXHR.responseJSON.msg
}
}
if (msg === "") {
msg = default_failed_message;
}
toastr.error(msg);
}
if (typeof props.error === 'function') {
return props.error(jqXHR.responseText, jqXHR.status);
......
......@@ -15,6 +15,7 @@ from django.conf import settings
import jms_storage
from rest_framework.pagination import LimitOffsetPagination
from rest_framework import viewsets
from rest_framework.views import APIView, Response
from rest_framework.permissions import AllowAny
......@@ -173,9 +174,10 @@ class StatusViewSet(viewsets.ModelViewSet):
return super().get_permissions()
class SessionViewSet(viewsets.ModelViewSet):
class SessionViewSet(BulkModelViewSet):
queryset = Session.objects.all()
serializer_class = SessionSerializer
pagination_class = LimitOffsetPagination
permission_classes = (IsOrgAdminOrAppUser,)
def get_queryset(self):
......@@ -183,7 +185,7 @@ class SessionViewSet(viewsets.ModelViewSet):
if terminal_id:
terminal = get_object_or_404(Terminal, id=terminal_id)
self.queryset = terminal.session_set.all()
return self.queryset
return self.queryset.all()
def perform_create(self, serializer):
if hasattr(self.request.user, 'terminal'):
......
......@@ -5,9 +5,7 @@ from django.utils import timezone
from rest_framework import serializers
from rest_framework_bulk.serializers import BulkListSerializer
from common.mixins import BulkSerializerMixin
from common.utils import get_object_or_none
from .models import Terminal, Status, Session, Task
from .backends import get_multi_command_storage
......@@ -45,7 +43,7 @@ class TerminalSerializer(serializers.ModelSerializer):
return False
class SessionSerializer(serializers.ModelSerializer):
class SessionSerializer(BulkSerializerMixin, serializers.ModelSerializer):
command_amount = serializers.SerializerMethodField()
command_store = get_multi_command_storage()
......
......@@ -120,6 +120,7 @@
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="terminate">{% trans 'Terminate selected' %}</option>
<option value="finished">{% trans 'Confirm finished' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
......@@ -149,6 +150,21 @@
success_message: success_message
});
}
function finishedSession(data) {
var the_url = "{% url 'api-terminal:session-list' %}";
var success_message = '{% trans "Finish session success" %}';
var success = function() {
location.reload();
};
APIUpdateAttr({
url: the_url,
method: 'PATCH',
body: JSON.stringify(data),
success: success,
success_message: success_message
});
}
$(document).ready(function() {
jumpserver.initStaticTable('table');
$('.select2').select2({
......@@ -183,10 +199,24 @@
function doTerminate() {
terminateSession(id_list)
}
function doFinishSession() {
var data = [];
$.each(id_list, function (i, v) {
data.push({
"pk": v,
"is_finished": true
})
});
finishedSession(data)
}
switch(action) {
case 'terminate':
doTerminate();
break;
case "finished":
doFinishSession();
break;
default:
break;
}
......
......@@ -3,20 +3,20 @@
#
from django.urls import path
from rest_framework import routers
from rest_framework_bulk.routes import BulkRouter
from .. import api
app_name = 'terminal'
router = routers.DefaultRouter()
router = BulkRouter()
router.register(r'sessions', api.SessionViewSet, 'session')
router.register(r'terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status')
router.register(r'terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions')
router.register(r'terminal', api.TerminalViewSet, 'terminal')
router.register(r'tasks', api.TaskViewSet, 'tasks')
router.register(r'command', api.CommandViewSet, 'command')
router.register(r'sessions', api.SessionViewSet, 'session')
router.register(r'status', api.StatusViewSet, 'session')
router.register(r'status', api.StatusViewSet, 'status')
urlpatterns = [
path('sessions/<uuid:pk>/replay/',
......
......@@ -3,6 +3,7 @@ import uuid
from django.core.cache import cache
from django.contrib.auth import logout
from django.utils.translation import ugettext as _
from rest_framework import generics
from rest_framework.response import Response
......@@ -132,7 +133,7 @@ class UserResetOTPApi(generics.RetrieveAPIView):
user = self.get_object() if kwargs.get('pk') else request.user
if user == request.user:
msg = _("Could not reset self otp, use profile reset instead")
return Response({"msg": msg}, status=401)
return Response({"error": msg}, status=401)
if user.otp_enabled and user.otp_secret_key:
user.otp_secret_key = ''
user.save()
......
......@@ -9,9 +9,15 @@
{% block content %}
<div class="verify">
<p style="margin:20px auto;"><strong style="color: #000000">{% trans 'Use the mobile Google Authenticator application to scan the following qr code for a 6-bit verification code' %}</strong></p>
<p style="margin:20px auto;">
<strong style="color: #000000">
{% trans 'Use the mobile Google Authenticator application to scan the following qr code for a 6-bit verification code' %}
</strong>
</p>
<div id="qr_code"></div>
<div id="qr_code">
</div>
<div style="display: block; margin: 0">Secret: {{ otp_secret_key }}</div>
<form class="" role="form" method="post" action="">
{% csrf_token %}
......
......@@ -263,7 +263,7 @@ def generate_otp_uri(request, issuer="Jumpserver"):
otp_secret_key = base64.b32encode(os.urandom(10)).decode('utf-8')
cache.set(request.session.session_key+'otp_key', otp_secret_key, 600)
totp = pyotp.TOTP(otp_secret_key)
return totp.provisioning_uri(name=user.username, issuer_name=issuer)
return totp.provisioning_uri(name=user.username, issuer_name=issuer), otp_secret_key
def check_otp_code(otp_secret_key, otp_code):
......
......@@ -505,8 +505,10 @@ class UserOtpEnableBindView(TemplateView, FormView):
def get_context_data(self, **kwargs):
user = get_user_or_tmp_user(self.request)
otp_uri, otp_secret_key = generate_otp_uri(self.request)
context = {
'otp_uri': generate_otp_uri(self.request),
'otp_uri': otp_uri,
'otp_secret_key': otp_secret_key,
'user': user
}
kwargs.update(context)
......
......@@ -31,10 +31,10 @@ ecdsa==0.13
elasticsearch==6.1.1
enum-compat==0.0.2
ephem==3.7.6.0
eventlet==0.22.1
eventlet==0.24.1
ForgeryPy==0.1
greenlet==0.4.12
gunicorn==19.7.1
greenlet==0.4.14
gunicorn==19.9.0
idna==2.6
itsdangerous==0.24
itypes==1.1.0
......
#!/usr/bin/python
import os
import sys
import django
if os.path.exists('../apps'):
sys.path.insert(0, '../apps')
elif os.path.exists('./apps'):
sys.path.insert(0, './apps')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
django.setup()
from assets.models import Node, Asset
from common.utils import get_object_or_none
from django.db import transaction
src_node = Node.objects.get(key="0:1")
target_node = Node.objects.get(key="2:1")
def sync_node(src, target, cut=False):
assets = src.get_assets()
# 同步本节点资产
for asset in assets:
if cut:
src.assets.remove(asset)
asset.org_id = target.org_id
asset.save()
new_asset = asset
else:
new_asset = get_object_or_none(Asset, hostname=asset.hostname, org_id=target.org_id)
if new_asset is None:
asset.id = None
asset.org_id = target.org_id
asset.save()
new_asset = asset
target.assets.add(new_asset)
# 同步子节点资产
for child in src.get_children():
node_new = target.create_child(child.value)
node_new.org_id = target.org_id
node_new.save()
sync_node(child, node_new)
if __name__ == '__main__':
with transaction.atomic():
sync_node(src_node, target_node)
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