Commit a1905ecf authored by oldseven's avatar oldseven

Merge branch 'master' of https://github.com/jumpserver/jumpserver

Conflicts:
	apps/terminal/api.py
parents 0d8a6002 f2216274
......@@ -19,14 +19,8 @@ Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点
----
### 功能
- 统一认证
- 资产管理
- 统一授权
- 审计
- 支持LDAP认证
- Web terminal
- SSH Server
- 支持Windows RDP
![Jumpserver功能](https://jumpserver-release.oss-cn-hangzhou.aliyuncs.com/Jumpserver13.jpg "Jumpserver功能")
### 开始使用
......
......@@ -2,4 +2,4 @@
# -*- coding: utf-8 -*-
#
__version__ = "1.2.1"
__version__ = "1.3.1"
......@@ -43,6 +43,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
queryset = super().get_queryset()
admin_user_id = self.request.query_params.get('admin_user_id')
node_id = self.request.query_params.get("node_id")
show_current_asset = self.request.query_params.get("show_current_asset")
if admin_user_id:
admin_user = get_object_or_404(AdminUser, id=admin_user_id)
......@@ -51,8 +52,11 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
node = get_object_or_404(Node, id=node_id)
if not node.is_root():
queryset = queryset.filter(
nodes__key__regex='{}(:[0-9]+)*$'.format(node.key),
nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key),
).distinct()
if show_current_asset and node_id:
queryset = queryset.filter(nodes=node_id).distinct()
return queryset
......
......@@ -14,6 +14,7 @@
# limitations under the License.
from rest_framework import generics, mixins
from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet
......@@ -41,7 +42,14 @@ __all__ = [
class NodeViewSet(BulkModelViewSet):
queryset = Node.objects.all()
permission_classes = (IsSuperUser,)
serializer_class = serializers.NodeSerializer
# serializer_class = serializers.NodeSerializer
def get_serializer_class(self):
show_current_asset = self.request.query_params.get('show_current_asset')
if show_current_asset:
return serializers.NodeCurrentSerializer
else:
return serializers.NodeSerializer
def perform_create(self, serializer):
child_key = Node.root().get_next_child_key()
......@@ -83,16 +91,29 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
serializer_class = serializers.NodeSerializer
instance = None
def counter(self):
values = [
child.value[child.value.rfind(' '):]
for child in self.get_object().get_children()
if child.value.startswith("新节点 ")
]
values = [int(value) for value in values if value.strip().isdigit()]
count = max(values)+1 if values else 1
return count
def post(self, request, *args, **kwargs):
if not request.data.get("value"):
request.data["value"] = _("New node {}").format(
Node.root().get_next_child_key().split(":")[-1]
)
request.data["value"] = _("New node {}").format(self.counter())
return super().post(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
instance = self.get_object()
value = request.data.get("value")
values = [child.value for child in instance.get_children()]
if value in values:
raise ValidationError(
'The same level node name cannot be the same'
)
node = instance.create_child(value=value)
return Response(
{"id": node.id, "key": node.key, "value": node.value},
......@@ -127,8 +148,9 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
node_fake.id = asset.id
node_fake.parent = node
node_fake.value = asset.hostname
node_fake.is_asset = True
node_fake.is_node = False
queryset.append(node_fake)
queryset = sorted(queryset, key=lambda x: x.is_node, reverse=True)
return queryset
def get(self, request, *args, **kwargs):
......@@ -162,8 +184,9 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
for node in children:
if not node:
continue
node.parent = instance
node.save()
# node.parent = instance
# node.save()
node.set_parent(instance)
return Response("OK")
......@@ -190,6 +213,9 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
instance = self.get_object()
if instance != Node.root():
instance.assets.remove(*tuple(assets))
else:
assets = [asset for asset in assets if asset.nodes.count() > 1]
instance.assets.remove(*tuple(assets))
class NodeReplaceAssetsApi(generics.UpdateAPIView):
......
......@@ -40,7 +40,7 @@ class SystemUserViewSet(BulkModelViewSet):
permission_classes = (IsSuperUserOrAppUser,)
class SystemUserAuthInfoApi(generics.RetrieveUpdateAPIView):
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
"""
Get system user auth info
"""
......@@ -48,6 +48,11 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateAPIView):
permission_classes = (IsSuperUserOrAppUser,)
serializer_class = serializers.SystemUserAuthSerializer
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.clear_auth()
return Response(status=204)
class SystemUserPushApi(generics.RetrieveAPIView):
"""
......@@ -58,6 +63,9 @@ class SystemUserPushApi(generics.RetrieveAPIView):
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
nodes = system_user.nodes.all()
for node in nodes:
system_user.assets.add(*tuple(node.get_all_assets()))
task = push_system_user_to_assets_manual.delay(system_user)
return Response({"task": task.id})
......
......@@ -4,7 +4,6 @@
import uuid
import logging
import random
from django.db import models
from django.utils.translation import ugettext_lazy as _
......@@ -35,6 +34,19 @@ def default_node():
return None
class AssetQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True)
def valid(self):
return self.active()
class AssetManager(models.Manager):
def get_queryset(self):
return AssetQuerySet(self.model, using=self._db)
class Asset(models.Model):
# Important
PLATFORM_CHOICES = (
......@@ -83,6 +95,8 @@ class Asset(models.Model):
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
objects = AssetManager()
def __str__(self):
return '{0.hostname}({0.ip})'.format(self)
......@@ -103,7 +117,8 @@ class Asset(models.Model):
def get_nodes(self):
from .node import Node
return self.nodes.all() or [Node.root()]
nodes = self.nodes.all() or [Node.root()]
return nodes
@property
def hardware_info(self):
......
......@@ -10,6 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from common.utils import get_signer, ssh_key_string_to_obj, ssh_key_gen
from common.validators import alphanumeric
from .utils import private_key_validator
signer = get_signer()
......@@ -18,7 +19,7 @@ signer = get_signer()
class AssetUser(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=128, verbose_name=_('Username'))
username = models.CharField(max_length=32, verbose_name=_('Username'), validators=[alphanumeric])
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
......@@ -103,10 +104,16 @@ class AssetUser(models.Model):
if update_fields:
self.save(update_fields=update_fields)
def clear_auth(self):
self._password = ''
self._private_key = ''
self._public_key = ''
self.save()
def auto_gen_auth(self):
password = str(uuid.uuid4())
private_key, public_key = ssh_key_gen(
username=self.username, password=password
username=self.username
)
self.set_auth(password=password,
private_key=private_key,
......
......@@ -2,7 +2,8 @@
#
import uuid
from django.db import models
from django.db import models, transaction
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
......@@ -12,11 +13,14 @@ __all__ = ['Node']
class Node(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
value = models.CharField(max_length=128, unique=True, verbose_name=_("Value"))
# value = models.CharField(
# max_length=128, unique=True, verbose_name=_("Value")
# )
value = models.CharField(max_length=128, verbose_name=_("Value"))
child_mark = models.IntegerField(default=0)
date_create = models.DateTimeField(auto_now_add=True)
is_asset = False
is_node = True
def __str__(self):
return self.full_value
......@@ -36,6 +40,16 @@ class Node(models.Model):
def level(self):
return len(self.key.split(':'))
def set_parent(self, instance):
children = self.get_all_children()
old_key = self.key
with transaction.atomic():
self.parent = instance
for child in children:
child.key = child.key.replace(old_key, self.key, 1)
child.save()
self.save()
def get_next_child_key(self):
mark = self.child_mark
self.child_mark += 1
......@@ -48,56 +62,77 @@ class Node(models.Model):
return child
def get_children(self):
return self.__class__.objects.filter(key__regex=r'{}:[0-9]+$'.format(self.key))
return self.__class__.objects.filter(
key__regex=r'^{}:[0-9]+$'.format(self.key)
)
def get_children_with_self(self):
return self.__class__.objects.filter(
key__regex=r'^{0}$|^{0}:[0-9]+$'.format(self.key)
)
def get_all_children(self):
return self.__class__.objects.filter(key__startswith='{}:'.format(self.key))
return self.__class__.objects.filter(
key__startswith='{}:'.format(self.key)
)
def get_all_children_with_self(self):
return self.__class__.objects.filter(
key__regex=r'^{0}$|^{0}:'.format(self.key)
)
def get_family(self):
children = list(self.get_all_children())
children.append(self)
return children
ancestor = self.ancestor
children = self.get_all_children()
return [*tuple(ancestor), self, *tuple(children)]
def get_assets(self):
from .asset import Asset
if self.is_root():
assets = Asset.objects.filter(
Q(nodes__id=self.id) | Q(nodes__isnull=True)
)
else:
assets = Asset.objects.filter(nodes__id=self.id)
return assets
def get_active_assets(self):
return self.get_assets().filter(is_active=True)
def get_valid_assets(self):
return self.get_assets().valid()
def get_all_assets(self):
from .asset import Asset
if self.is_root():
assets = Asset.objects.all()
else:
nodes = self.get_family()
nodes = self.get_all_children_with_self()
assets = Asset.objects.filter(nodes__in=nodes).distinct()
return assets
def get_current_assets(self):
from .asset import Asset
assets = Asset.objects.filter(nodes=self).distinct()
return assets
def has_assets(self):
return self.get_all_assets()
def get_all_active_assets(self):
return self.get_all_assets().filter(is_active=True)
def get_all_valid_assets(self):
return self.get_all_assets().valid()
def is_root(self):
return self.key == '0'
@property
def parent(self):
if self.key == "0":
return self.__class__.root()
elif not self.key.startswith("0"):
if self.key == "0" or not self.key.startswith("0"):
return self.__class__.root()
parent_key = ":".join(self.key.split(":")[:-1])
try:
parent = self.__class__.objects.get(key=parent_key)
return parent
except Node.DoesNotExist:
return self.__class__.root()
else:
return parent
@parent.setter
def parent(self, parent):
......@@ -105,14 +140,20 @@ class Node(models.Model):
@property
def ancestor(self):
if self.parent == self.__class__.root():
_key = self.key.split(':')
ancestor_keys = []
if self.is_root():
return [self.__class__.root()]
else:
return [self.parent, *tuple(self.parent.ancestor)]
for i in range(len(_key)-1):
_key.pop()
ancestor_keys.append(':'.join(_key))
return self.__class__.objects.filter(key__in=ancestor_keys)
@property
def ancestor_with_node(self):
ancestor = self.ancestor
def ancestor_with_self(self):
ancestor = list(self.ancestor)
ancestor.insert(0, self)
return ancestor
......
......@@ -18,8 +18,7 @@ class NodeTMPSerializer(serializers.ModelSerializer):
class Meta:
model = Node
fields = ['id', 'key', 'value', 'parent', 'assets_amount',
'is_asset']
fields = ['id', 'key', 'value', 'parent', 'assets_amount', 'is_node']
list_serializer_class = BulkListSerializer
@staticmethod
......@@ -62,13 +61,13 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
"""
system_users_granted = AssetSystemUserSerializer(many=True, read_only=True)
system_users_join = serializers.SerializerMethodField()
nodes = NodeTMPSerializer(many=True, read_only=True)
# nodes = NodeTMPSerializer(many=True, read_only=True)
class Meta:
model = Asset
fields = (
"id", "hostname", "ip", "port", "system_users_granted",
"is_active", "system_users_join", "os", 'domain', "nodes",
"is_active", "system_users_join", "os", 'domain',
"platform", "comment"
)
......
......@@ -9,7 +9,7 @@ from .asset import AssetGrantedSerializer
__all__ = [
'NodeSerializer', "NodeGrantedSerializer", "NodeAddChildrenSerializer",
"NodeAssetsSerializer",
"NodeAssetsSerializer", "NodeCurrentSerializer",
]
......@@ -48,9 +48,20 @@ class NodeSerializer(serializers.ModelSerializer):
class Meta:
model = Node
fields = ['id', 'key', 'value', 'parent', 'assets_amount', 'is_asset']
fields = ['id', 'key', 'value', 'parent', 'assets_amount', 'is_node']
list_serializer_class = BulkListSerializer
def validate(self, data):
value = data.get('value')
instance = self.instance if self.instance else Node.root()
children = instance.parent.get_children().exclude(key=instance.key)
values = [child.value for child in children]
if value in values:
raise serializers.ValidationError(
'The same level node name cannot be the same'
)
return data
@staticmethod
def get_parent(obj):
return obj.parent.id
......@@ -66,6 +77,12 @@ class NodeSerializer(serializers.ModelSerializer):
return fields
class NodeCurrentSerializer(NodeSerializer):
@staticmethod
def get_assets_amount(obj):
return obj.get_current_assets().count()
class NodeAssetsSerializer(serializers.ModelSerializer):
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
......
......@@ -22,7 +22,7 @@ TIMEOUT = 60
logger = get_logger(__file__)
CACHE_MAX_TIME = 60*60*60
disk_pattern = re.compile(r'^hd|sd|xvd|vd')
PERIOD_TASK = os.environ.get("PERIOD_TASK", "on")
PERIOD_TASK = os.environ.get("PERIOD_TASK", "off")
@shared_task
......
......@@ -98,7 +98,10 @@ function initTree2() {
$.get("{% url 'api-assets:node-list' %}", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
{#value["open"] = true;#}
if (value["key"] === "0") {
value["open"] = true;
}
value["name"] = value["value"] + ' (' + value['assets_amount'] + ')';
value['value'] = value['value'];
});
......
......@@ -55,7 +55,7 @@
{% bootstrap_field form.private_key_file layout="horizontal" %}
</div>
<div class="form-group">
<label for="{{ form.as_push.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto push' %}</label>
<label for="{{ form.auto_push.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto push' %}</label>
<div class="col-sm-8">
{{ form.auto_push}}
</div>
......@@ -79,43 +79,50 @@
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var auto_generate_key = '#'+'{{ form.auto_generate_key.id_for_label }}';
var protocol_id = '#' + '{{ form.protocol.id_for_label }}';
var password_id = '#' + '{{ form.password.id_for_label }}';
var private_key_id = '#' + '{{ form.private_key_file.id_for_label }}';
var sudo_id = '#' + '{{ form.sudo.id_for_label }}';
var shell_id = '#' + '{{ form.shell.id_for_label }}';
<script>
var auto_generate_key = '#'+'{{ form.auto_generate_key.id_for_label }}';
var protocol_id = '#' + '{{ form.protocol.id_for_label }}';
var private_key_id = '#' + '{{ form.private_key_file.id_for_label }}';
var auto_push_id = '#' + '{{ form.auto_push.id_for_label }}';
var sudo_id = '#' + '{{ form.sudo.id_for_label }}';
var shell_id = '#' + '{{ form.shell.id_for_label }}';
var need_change_field = [
auto_generate_key, private_key_id, auto_push_id, sudo_id, shell_id
];
var need_change_field = [auto_generate_key, private_key_id, sudo_id, shell_id] ;
function authFieldsDisplay() {
if ($(auto_generate_key).prop('checked')) {
$('.auth-fields').addClass('hidden');
} else {
function protocolChange() {
if ($(protocol_id + " option:selected").text() === 'rdp') {
$('.auth-fields').removeClass('hidden');
}
}
function protocolChange() {
if ($(protocol_id).attr('value') === 'rdp') {
$.each(need_change_field, function (index, value) {
$(value).addClass('hidden')
$(value).closest('.form-group').addClass('hidden')
});
$(password_id).removeClass('hidden')
} else {
authFieldsDisplay();
$.each(need_change_field, function (index, value) {
$(value).removeClass('hidden')
$(value).closest('.form-group').removeClass('hidden')
});
}
}
function authFieldsDisplay() {
if ($(auto_generate_key).prop('checked')) {
$('.auth-fields').addClass('hidden');
} else {
$('.auth-fields').removeClass('hidden');
}
$(document).ready(function () {
}
$(document).ready(function () {
$('.select2').select2();
authFieldsDisplay();
protocolChange();
$(auto_generate_key).change(function () {
})
.on('change', protocol_id, function(){
protocolChange();
})
.on('change', auto_generate_key, function(){
authFieldsDisplay();
});
})
</script>
});
</script>
{% endblock %}
\ No newline at end of file
......@@ -124,7 +124,7 @@ $(document).ready(function () {
var success = function (data) {
var task_id = data.task;
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600')
window.open(url, '', 'width=800,height=600,left=400,top=400')
};
APIUpdateAttr({
url: the_url,
......
......@@ -190,7 +190,7 @@
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Nodes' %}" id="groups_selected" class="select2 groups" style="width: 100%" multiple="" tabindex="4">
{% for node in nodes_remain %}
<option value="{{ node.id }}" id="opt_{{ node.id }}" >{{ node.name }}</option>
<option value="{{ node.id }}" id="opt_{{ node.id }}" >{{ node }}</option>
{% endfor %}
</select>
</td>
......@@ -204,7 +204,7 @@
{% for node in asset.nodes.all %}
<tr>
<td ><b class="bdg_node" data-gid={{ node.id }}>{{ node.name }}</b></td>
<td ><b class="bdg_node" data-gid={{ node.id }}>{{ node }}</b></td>
<td>
<button class="btn btn-danger pull-right btn-xs btn-leave-node" type="button"><i class="fa fa-minus"></i></button>
</td>
......
......@@ -17,11 +17,12 @@
position:absolute;
visibility:hidden;
text-align: left;
top: 100%;
{#top: 100%;#}
top: 0;
left: 0;
z-index: 1000;
float: left;
padding: 5px 0;
{#float: left;#}
padding: 0 0;
margin: 2px 0 0;
list-style: none;
background-clip: padding-box;
......@@ -29,7 +30,7 @@
div#rMenu li{
margin: 1px 0;
cursor: pointer;
{#list-style: none outside none;#}
list-style: none outside none;
}
.dropdown a:hover {
background-color: #f1f1f1
......@@ -47,7 +48,6 @@
<div class="file-manager ">
<div id="assetTree" class="ztree">
</div>
<div class="clearfix"></div>
</div>
</div>
......@@ -87,7 +87,7 @@
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Hardware' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Reachable' %}</th>
{# <th class="text-center">{% trans 'Reachable' %}</th>#}
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
......@@ -127,6 +127,9 @@
<li class="divider"></li>
<li id="menu_refresh_hardware_info" class="btn-refresh-hardware" tabindex="-1"><a><i class="fa fa-refresh"></i> {% trans 'Refresh node hardware info' %}</a></li>
<li id="menu_test_connective" class="btn-test-connective" tabindex="-1"><a><i class="fa fa-chain"></i> {% trans 'Test node connective' %}</a></li>
<li class="divider"></li>
<li id="show_current_asset" class="btn-show-current-asset" style="display: none;" tabindex="-1"><a><i class="fa fa-hand-o-up"></i> {% trans 'Display only current node assets' %}</a></li>
<li id="show_all_asset" class="btn-show-all-asset" style="display: none;" tabindex="-1"><a><i class="fa fa-th"></i> {% trans 'Displays all child node assets' %}</a></li>
</ul>
</div>
......@@ -157,26 +160,35 @@ function initTable() {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 5, createdCell: function (td, cellData) {
if (cellData === 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>')
} else if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
{#{targets: 5, createdCell: function (td, cellData) {#}
{# if (cellData === 'Unknown'){#}
{# $(td).html('<i class="fa fa-circle text-warning"></i>')#}
{# } else if (!cellData) {#}
{# $(td).html('<i class="fa fa-circle text-danger"></i>')#}
{# } else {#}
{# $(td).html('<i class="fa fa-circle text-navy"></i>')#}
{# }#}
{# }},#}
{targets: 5, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:asset-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_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-assets:asset-list" %}',
{#columns: [#}
{# {data: "id"}, {data: "hostname" }, {data: "ip" },#}
{# {data: "cpu_cores"}, {data: "is_active", orderable: false },#}
{# {data: "is_connective", orderable: false}, {data: "id", orderable: false }#}
{#],#}
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "cpu_cores"}, {data: "is_active", orderable: false },
{data: "is_connective", orderable: false}, {data: "id", orderable: false }
{data: "id", orderable: false }
],
op_html: $('#actions').html()
};
......@@ -200,6 +212,8 @@ function addTreeNode() {
};
newNode.checked = zTree.getSelectedNodes()[0].checked;
zTree.addNodes(parentNode, 0, newNode);
var node = zTree.getNodeByParam('id', newNode.id, parentNode)
zTree.editName(node);
} else {
alert("{% trans 'Create node failed' %}")
}
......@@ -253,6 +267,8 @@ function OnRightClick(event, treeId, treeNode) {
function showRMenu(type, x, y) {
$("#rMenu ul").show();
x -= 220;
x += document.body.scrollLeft;
y += document.body.scrollTop+document.documentElement.scrollTop;
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
$("body").bind("mousedown", onBodyMouseDown);
......@@ -290,6 +306,7 @@ function onRename(event, treeId, treeNode, isCancel){
function onSelected(event, treeNode) {
var url = asset_table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
url = setUrlParam(url, "show_current_asset", getCookie('show_current_asset'));
setCookie('node_selected', treeNode.id);
asset_table.ajax.url(url);
asset_table.ajax.reload();
......@@ -382,12 +399,13 @@ function initTree() {
};
var zNodes = [];
$.get("{% url 'api-assets:node-list' %}", function(data, status){
var query_params = {'show_current_asset': getCookie('show_current_asset')};
$.get("{% url 'api-assets:node-list' %}", query_params, function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
{#if (value["key"] === "0") {#}
if (value["key"] === "0") {
value["open"] = true;
{# }#}
}
value["name"] = value["value"] + ' (' + value['assets_amount'] + ')';
value['value'] = value['value'];
});
......@@ -417,6 +435,13 @@ function toggle() {
$(document).ready(function(){
initTable();
initTree();
if(getCookie('show_current_asset') === 'yes'){
$('#show_all_asset').css('display', 'inline-block');
}
else{
$('#show_current_asset').css('display', 'inline-block');
}
})
.on('click', '.labels li', function () {
var val = $(this).text();
......@@ -535,6 +560,20 @@ $(document).ready(function(){
flash_message: false
});
})
.on('click', '.btn-show-current-asset', function(){
hideRMenu();
$(this).css('display', 'none');
$('#show_all_asset').css('display', 'inline-block');
setCookie('show_current_asset', 'yes');
location.reload();
})
.on('click', '.btn-show-all-asset', function(){
hideRMenu();
$(this).css('display', 'none');
$('#show_current_asset').css('display', 'inline-block');
setCookie('show_current_asset', '');
location.reload();
})
.on('click', '.btn_asset_delete', function () {
var $this = $(this);
var $data_table = $("#asset_list_table").DataTable();
......
......@@ -85,6 +85,9 @@ function initTable() {
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 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);
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);
}
$(td).html(update_btn + test_btn + del_btn)
}}
],
......@@ -120,7 +123,6 @@ $(document).ready(function(){
success_message: "可连接",
fail_message: "连接失败"
})
})
});
</script>
{% endblock %}
......@@ -66,3 +66,28 @@
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
var protocol_id = '#' + '{{ form.protocol.id_for_label }}';
var private_key_id = '#' + '{{ form.private_key_file.id_for_label }}';
var port = '#' + '{{ form.port.id_for_label }}';
function protocolChange() {
if ($(protocol_id + " option:selected").text() === 'rdp') {
$(port).val(3389);
$(private_key_id).closest('.form-group').addClass('hidden')
} else {
$(port).val(22);
$(private_key_id).closest('.form-group').removeClass('hidden')
}
}
$(document).ready(function(){
protocolChange();
})
.on('change', protocol_id, function(){
protocolChange();
});
</script>
{% endblock %}
\ No newline at end of file
......@@ -64,14 +64,14 @@
</tr>
<tr>
<td>{% trans 'Protocol' %}:</td>
<td><b>{{ system_user.protocol }}</b></td>
<td><b id="id_protocol_type">{{ system_user.protocol }}</b></td>
</tr>
<tr>
<tr class="only-ssh">
<td>{% trans 'Sudo' %}:</td>
<td><b>{{ system_user.sudo }}</b></td>
</tr>
{% if system_user.shell %}
<tr>
<tr class="only-ssh">
<td>{% trans 'Shell' %}:</td>
<td><b>{{ system_user.shell }}</b></td>
</tr>
......@@ -107,14 +107,14 @@
</div>
<div class="col-sm-4" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel panel-primary ">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<tr class="only-ssh">
<td width="50%">{% trans 'Auto push' %}:</td>
<td>
<span class="pull-right">
......@@ -130,8 +130,8 @@
</span>
</td>
</tr>
<tr class="no-borders-tr">
{% if system_user.auto_push %}
<tr class="only-ssh">
<td width="50%">{% trans 'Push system user now' %}:</td>
<td>
<span style="float: right">
......@@ -139,8 +139,8 @@
</span>
</td>
</tr>
<tr>
{% endif %}
<tr class="only-ssh">
<td width="50%">{% trans 'Test assets connective' %}:</td>
<td>
<span style="float: right">
......@@ -149,6 +149,15 @@
</td>
</tr>
<tr>
<td width="50%">{% trans 'Clear auth' %}:</td>
<td>
<span style="float: right">
<button type="button" class="btn btn-primary btn-xs btn-clear-auth" style="width: 54px">{% trans 'Clear' %}</button>
</span>
</td>
</tr>
{# <tr>#}
{# <td width="50%">{% trans 'Change auth period' %}:</td>#}
{# <td>#}
......@@ -236,6 +245,10 @@ function updateSystemUserNode(nodes) {
}
jumpserver.nodes_selected = {};
$(document).ready(function () {
if($('#id_protocol_type').text() === 'rdp'){
$('.only-ssh').addClass('hidden')
}
$(".panel-body .table tr:visible:first").addClass('no-borders-tr');
$('.select2').select2()
.on('select2:select', function(evt) {
var data = evt.params.data;
......@@ -296,7 +309,7 @@ $(document).ready(function () {
var success = function (data) {
var task_id = data.task;
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600')
window.open(url, '', 'width=800,height=600,left=400,top=400')
};
APIUpdateAttr({
url: the_url,
......@@ -318,6 +331,13 @@ $(document).ready(function () {
success: success,
flash_message: false
});
}).on('click', '.btn-clear-auth', function () {
var the_url = '{% url "api-assets:system-user-auth-info" pk=system_user.id %}';
APIUpdateAttr({
url: the_url,
method: 'DELETE',
success_message: "{% trans 'Clear auth' %}" + " {% trans 'success' %}"
});
})
</script>
{% endblock %}
......@@ -15,10 +15,3 @@
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
})
</script>
{% endblock %}
\ No newline at end of file
......@@ -96,14 +96,7 @@ class LDAPTestingAPI(APIView):
class DjangoSettingsAPI(APIView):
def get(self, request):
if not settings.DEBUG:
return Response('Only debug mode support')
configs = {}
for i in dir(settings):
if i.isupper():
configs[i] = str(getattr(settings, i))
return Response(configs)
return Response('Danger, Close now')
# -*- coding: utf-8 -*-
#
from django.core.validators import RegexValidator
from django.utils.translation import ugettext_lazy as _
alphanumeric = RegexValidator(r'^[0-9a-zA-Z_@\-\.]*$', _('Special char not allowed'))
\ No newline at end of file
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-23 19:51+0800\n"
"POT-Creation-Date: 2018-05-25 18:11+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"
......@@ -17,27 +17,27 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: assets/api/node.py:88
#: assets/api/node.py:106
msgid "New node {}"
msgstr "新节点 {}"
#: assets/api/node.py:216
#: assets/api/node.py:242
msgid "更新节点资产硬件信息: {}"
msgstr ""
#: assets/api/node.py:229
#: assets/api/node.py:255
msgid "测试节点下资产是否可连接: {}"
msgstr ""
#: assets/forms/asset.py:24 assets/models/asset.py:54 assets/models/user.py:103
#: assets/forms/asset.py:24 assets/models/asset.py:66 assets/models/user.py:103
#: assets/templates/assets/asset_detail.html:183
#: assets/templates/assets/asset_detail.html:191
#: assets/templates/assets/system_user_detail.html:166 perms/models.py:23
#: assets/templates/assets/system_user_detail.html:175 perms/models.py:33
msgid "Nodes"
msgstr "节点管理"
#: assets/forms/asset.py:27 assets/forms/asset.py:66 assets/forms/asset.py:109
#: assets/forms/asset.py:113 assets/models/asset.py:58
#: assets/forms/asset.py:113 assets/models/asset.py:70
#: assets/models/cluster.py:19 assets/models/user.py:72
#: assets/templates/assets/asset_detail.html:73 templates/_nav.html:25
msgid "Admin user"
......@@ -46,14 +46,14 @@ msgstr "管理用户"
#: assets/forms/asset.py:30 assets/forms/asset.py:69 assets/forms/asset.py:125
#: assets/templates/assets/asset_create.html:35
#: assets/templates/assets/asset_create.html:37
#: assets/templates/assets/asset_list.html:75
#: assets/templates/assets/asset_list.html:74
#: assets/templates/assets/asset_update.html:40
#: assets/templates/assets/asset_update.html:42
#: assets/templates/assets/user_asset_list.html:34
msgid "Label"
msgstr "标签"
#: assets/forms/asset.py:34 assets/forms/asset.py:73 assets/models/asset.py:53
#: assets/forms/asset.py:34 assets/forms/asset.py:73 assets/models/asset.py:65
#: assets/models/domain.py:46
msgid "Domain"
msgstr "网域"
......@@ -61,7 +61,7 @@ msgstr "网域"
#: assets/forms/asset.py:38 assets/forms/asset.py:63 assets/forms/asset.py:77
#: assets/forms/asset.py:128 assets/templates/assets/asset_create.html:29
#: assets/templates/assets/asset_update.html:34 perms/forms.py:40
#: perms/forms.py:47 perms/models.py:67
#: perms/forms.py:47 perms/models.py:76
#: perms/templates/perms/asset_permission_list.html:57
#: perms/templates/perms/asset_permission_list.html:142
msgid "Node"
......@@ -90,7 +90,7 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,
msgid "Select assets"
msgstr "选择资产"
#: assets/forms/asset.py:105 assets/models/asset.py:51
#: assets/forms/asset.py:105 assets/models/asset.py:63
#: assets/models/domain.py:44 assets/templates/assets/admin_user_assets.html:53
#: assets/templates/assets/asset_detail.html:69
#: assets/templates/assets/domain_gateway_list.html:58
......@@ -99,18 +99,18 @@ msgid "Port"
msgstr "端口"
#: assets/forms/domain.py:14 assets/forms/label.py:13
#: assets/models/asset.py:169 assets/templates/assets/admin_user_list.html:25
#: assets/models/asset.py:183 assets/templates/assets/admin_user_list.html:25
#: assets/templates/assets/domain_detail.html:60
#: assets/templates/assets/domain_list.html:15
#: assets/templates/assets/label_list.html:16
#: assets/templates/assets/system_user_list.html:29 audits/models.py:11
#: audits/templates/audits/ftp_log_list.html:41
#: audits/templates/audits/ftp_log_list.html:72 perms/forms.py:37
#: perms/models.py:22
#: perms/models.py:32
#: perms/templates/perms/asset_permission_create_update.html:40
#: perms/templates/perms/asset_permission_list.html:56
#: perms/templates/perms/asset_permission_list.html:139
#: terminal/backends/command/models.py:11 terminal/models.py:123
#: terminal/backends/command/models.py:11 terminal/models.py:127
#: terminal/templates/terminal/command_list.html:40
#: terminal/templates/terminal/command_list.html:73
#: terminal/templates/terminal/session_list.html:41
......@@ -119,7 +119,7 @@ msgid "Asset"
msgstr "资产"
#: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:120
#: assets/models/base.py:20 assets/models/cluster.py:18
#: assets/models/base.py:21 assets/models/cluster.py:18
#: assets/models/domain.py:17 assets/models/group.py:20
#: assets/models/label.py:17 assets/templates/assets/admin_user_detail.html:56
#: assets/templates/assets/admin_user_list.html:23
......@@ -132,10 +132,10 @@ msgstr "资产"
#: common/templates/common/terminal_setting.html:67
#: common/templates/common/terminal_setting.html:85 ops/models/adhoc.py:36
#: ops/templates/ops/task_detail.html:59 ops/templates/ops/task_list.html:35
#: perms/models.py:19 perms/templates/perms/asset_permission_detail.html:62
#: perms/models.py:29 perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_list.html:53
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:16
#: terminal/models.py:149 terminal/templates/terminal/terminal_detail.html:43
#: terminal/models.py:154 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:42 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:63
......@@ -148,14 +148,14 @@ msgid "Name"
msgstr "名称"
#: assets/forms/domain.py:55 assets/forms/user.py:80 assets/forms/user.py:121
#: assets/models/base.py:21 assets/templates/assets/admin_user_detail.html:60
#: assets/models/base.py:22 assets/templates/assets/admin_user_detail.html:60
#: assets/templates/assets/admin_user_list.html:24
#: assets/templates/assets/domain_gateway_list.html:60
#: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_list.html:27
#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13
#: users/forms.py:22 users/models/authentication.py:45 users/models/user.py:40
#: users/templates/users/_select_user_modal.html:14
#: users/forms.py:21 users/forms.py:30 users/models/authentication.py:45
#: users/models/user.py:40 users/templates/users/_select_user_modal.html:14
#: users/templates/users/login.html:56
#: users/templates/users/login_log_list.html:49
#: users/templates/users/user_detail.html:67
......@@ -168,8 +168,8 @@ msgstr "用户名"
msgid "Password or private key passphrase"
msgstr "密码或密钥密码"
#: assets/forms/user.py:25 assets/models/base.py:22 common/forms.py:113
#: users/forms.py:15 users/forms.py:24 users/forms.py:36
#: assets/forms/user.py:25 assets/models/base.py:23 common/forms.py:113
#: users/forms.py:15 users/forms.py:23 users/forms.py:32 users/forms.py:44
#: users/templates/users/login.html:59
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:10
......@@ -202,11 +202,11 @@ msgid ""
"than 2 system user"
msgstr "高优先级的系统用户将会作为默认登录用户"
#: assets/models/asset.py:49 assets/models/domain.py:43
#: assets/models/asset.py:61 assets/models/domain.py:43
#: assets/templates/assets/_asset_list_modal.html:46
#: assets/templates/assets/admin_user_assets.html:52
#: assets/templates/assets/asset_detail.html:61
#: assets/templates/assets/asset_list.html:87
#: assets/templates/assets/asset_list.html:86
#: assets/templates/assets/domain_gateway_list.html:57
#: assets/templates/assets/system_user_asset.html:50
#: assets/templates/assets/user_asset_list.html:46 common/forms.py:144
......@@ -217,10 +217,10 @@ msgstr "高优先级的系统用户将会作为默认登录用户"
msgid "IP"
msgstr "IP"
#: assets/models/asset.py:50 assets/templates/assets/_asset_list_modal.html:45
#: assets/models/asset.py:62 assets/templates/assets/_asset_list_modal.html:45
#: assets/templates/assets/admin_user_assets.html:51
#: assets/templates/assets/asset_detail.html:57
#: assets/templates/assets/asset_list.html:86
#: assets/templates/assets/asset_list.html:85
#: assets/templates/assets/system_user_asset.html:49
#: assets/templates/assets/user_asset_list.html:45 common/forms.py:143
#: perms/templates/perms/asset_permission_asset.html:54
......@@ -229,107 +229,107 @@ msgstr "IP"
msgid "Hostname"
msgstr "主机名"
#: assets/models/asset.py:52 assets/templates/assets/asset_detail.html:97
#: assets/models/asset.py:64 assets/templates/assets/asset_detail.html:97
msgid "Platform"
msgstr "系统平台"
#: assets/models/asset.py:55 assets/models/domain.py:48
#: assets/models/asset.py:67 assets/models/domain.py:48
#: assets/models/label.py:20 assets/templates/assets/asset_detail.html:105
msgid "Is active"
msgstr "激活"
#: assets/models/asset.py:61 assets/templates/assets/asset_detail.html:65
#: assets/models/asset.py:73 assets/templates/assets/asset_detail.html:65
msgid "Public IP"
msgstr "公网IP"
#: assets/models/asset.py:62 assets/templates/assets/asset_detail.html:113
#: assets/models/asset.py:74 assets/templates/assets/asset_detail.html:113
msgid "Asset number"
msgstr "资产编号"
#: assets/models/asset.py:65 assets/templates/assets/asset_detail.html:77
#: assets/models/asset.py:77 assets/templates/assets/asset_detail.html:77
msgid "Vendor"
msgstr "制造商"
#: assets/models/asset.py:66 assets/templates/assets/asset_detail.html:81
#: assets/models/asset.py:78 assets/templates/assets/asset_detail.html:81
msgid "Model"
msgstr "型号"
#: assets/models/asset.py:67 assets/templates/assets/asset_detail.html:109
#: assets/models/asset.py:79 assets/templates/assets/asset_detail.html:109
msgid "Serial number"
msgstr "序列号"
#: assets/models/asset.py:69
#: assets/models/asset.py:81
msgid "CPU model"
msgstr "CPU型号"
#: assets/models/asset.py:70
#: assets/models/asset.py:82
msgid "CPU count"
msgstr "CPU数量"
#: assets/models/asset.py:71
#: assets/models/asset.py:83
msgid "CPU cores"
msgstr "CPU核数"
#: assets/models/asset.py:72 assets/templates/assets/asset_detail.html:89
#: assets/models/asset.py:84 assets/templates/assets/asset_detail.html:89
msgid "Memory"
msgstr "内存"
#: assets/models/asset.py:73
#: assets/models/asset.py:85
msgid "Disk total"
msgstr "硬盘大小"
#: assets/models/asset.py:74
#: assets/models/asset.py:86
msgid "Disk info"
msgstr "硬盘信息"
#: assets/models/asset.py:76 assets/templates/assets/asset_detail.html:101
#: assets/models/asset.py:88 assets/templates/assets/asset_detail.html:101
msgid "OS"
msgstr "操作系统"
#: assets/models/asset.py:77
#: assets/models/asset.py:89
msgid "OS version"
msgstr "系统版本"
#: assets/models/asset.py:78
#: assets/models/asset.py:90
msgid "OS arch"
msgstr "系统架构"
#: assets/models/asset.py:79
#: assets/models/asset.py:91
msgid "Hostname raw"
msgstr "主机名原始"
#: assets/models/asset.py:81 assets/templates/assets/asset_create.html:33
#: assets/models/asset.py:93 assets/templates/assets/asset_create.html:33
#: assets/templates/assets/asset_detail.html:220
#: assets/templates/assets/asset_update.html:38 templates/_nav.html:27
msgid "Labels"
msgstr "标签管理"
#: assets/models/asset.py:82 assets/models/base.py:28
#: assets/models/asset.py:94 assets/models/base.py:29
#: assets/models/cluster.py:28 assets/models/group.py:21
#: assets/templates/assets/admin_user_detail.html:68
#: assets/templates/assets/asset_detail.html:117
#: assets/templates/assets/domain_detail.html:72
#: assets/templates/assets/system_user_detail.html:96
#: ops/templates/ops/adhoc_detail.html:86 perms/models.py:28 perms/models.py:72
#: ops/templates/ops/adhoc_detail.html:86 perms/models.py:38 perms/models.py:81
#: perms/templates/perms/asset_permission_detail.html:98
#: users/models/user.py:83 users/templates/users/user_detail.html:107
msgid "Created by"
msgstr "创建者"
#: assets/models/asset.py:83 assets/models/cluster.py:26
#: assets/models/asset.py:95 assets/models/cluster.py:26
#: assets/models/domain.py:20 assets/models/group.py:22
#: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:64
#: assets/templates/assets/domain_detail.html:68
#: assets/templates/assets/system_user_detail.html:92
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:63
#: perms/models.py:29 perms/models.py:73
#: perms/models.py:39 perms/models.py:82
#: perms/templates/perms/asset_permission_detail.html:94
#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17
#: users/templates/users/user_group_detail.html:63
msgid "Date created"
msgstr "创建日期"
#: assets/models/asset.py:84 assets/models/base.py:25
#: assets/models/asset.py:96 assets/models/base.py:26
#: assets/models/cluster.py:29 assets/models/domain.py:18
#: assets/models/domain.py:47 assets/models/group.py:23
#: assets/models/label.py:21 assets/templates/assets/admin_user_detail.html:72
......@@ -340,7 +340,7 @@ msgstr "创建日期"
#: assets/templates/assets/domain_list.html:17
#: assets/templates/assets/system_user_detail.html:100
#: assets/templates/assets/system_user_list.html:33 common/models.py:30
#: ops/models/adhoc.py:42 perms/models.py:30 perms/models.py:74
#: ops/models/adhoc.py:42 perms/models.py:40 perms/models.py:83
#: perms/templates/perms/asset_permission_detail.html:102 terminal/models.py:26
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
#: users/models/user.py:75 users/templates/users/user_detail.html:119
......@@ -350,11 +350,11 @@ msgstr "创建日期"
msgid "Comment"
msgstr "备注"
#: assets/models/base.py:23
#: assets/models/base.py:24
msgid "SSH private key"
msgstr "ssh密钥"
#: assets/models/base.py:24
#: assets/models/base.py:25
msgid "SSH public key"
msgstr "ssh公钥"
......@@ -408,6 +408,7 @@ msgstr "集群"
#: assets/templates/assets/domain_gateway_list.html:59
#: assets/templates/assets/system_user_detail.html:66
#: assets/templates/assets/system_user_list.html:28
#: terminal/templates/terminal/session_list.html:75
msgid "Protocol"
msgstr "协议"
......@@ -422,22 +423,22 @@ msgstr "默认资产组"
#: assets/models/label.py:14 audits/models.py:9
#: audits/templates/audits/ftp_log_list.html:33
#: audits/templates/audits/ftp_log_list.html:71 perms/forms.py:14
#: perms/forms.py:31 perms/models.py:20
#: perms/forms.py:31 perms/models.py:30
#: perms/templates/perms/asset_permission_create_update.html:36
#: perms/templates/perms/asset_permission_list.html:54
#: perms/templates/perms/asset_permission_list.html:133
#: terminal/backends/command/models.py:10 terminal/models.py:122
#: terminal/backends/command/models.py:10 terminal/models.py:126
#: terminal/templates/terminal/command_list.html:32
#: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/forms.py:273
#: terminal/templates/terminal/session_list.html:71 users/forms.py:281
#: users/models/user.py:30 users/models/user.py:318
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:339
msgid "User"
msgstr "用户"
#: assets/models/label.py:18 assets/models/node.py:15
#: assets/models/label.py:18 assets/models/node.py:18
#: assets/templates/assets/label_list.html:15 common/models.py:27
msgid "Value"
msgstr "值"
......@@ -489,11 +490,11 @@ msgstr "Shell"
#: assets/models/user.py:149 audits/models.py:12
#: audits/templates/audits/ftp_log_list.html:49
#: audits/templates/audits/ftp_log_list.html:73 perms/forms.py:43
#: perms/models.py:24 perms/models.py:69
#: perms/models.py:34 perms/models.py:78
#: perms/templates/perms/asset_permission_detail.html:140
#: perms/templates/perms/asset_permission_list.html:58
#: perms/templates/perms/asset_permission_list.html:145 templates/_nav.html:26
#: terminal/backends/command/models.py:12 terminal/models.py:124
#: terminal/backends/command/models.py:12 terminal/models.py:128
#: terminal/templates/terminal/command_list.html:48
#: terminal/templates/terminal/command_list.html:74
#: terminal/templates/terminal/session_list.html:49
......@@ -561,7 +562,6 @@ msgid "Select System Users"
msgstr "选择系统用户"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:34
#, fuzzy
msgid "Enable-MFA"
msgstr "启用MFA"
......@@ -650,7 +650,7 @@ msgstr "重置"
#: assets/templates/assets/admin_user_create_update.html:46
#: assets/templates/assets/asset_bulk_update.html:24
#: assets/templates/assets/asset_create.html:67
#: assets/templates/assets/asset_list.html:108
#: assets/templates/assets/asset_list.html:107
#: assets/templates/assets/asset_update.html:71
#: assets/templates/assets/domain_create_update.html:17
#: assets/templates/assets/gateway_create_update.html:59
......@@ -660,7 +660,7 @@ msgstr "重置"
#: common/templates/common/ldap_setting.html:60
#: common/templates/common/terminal_setting.html:103
#: perms/templates/perms/asset_permission_create_update.html:70
#: terminal/templates/terminal/session_list.html:120
#: terminal/templates/terminal/session_list.html:124
#: terminal/templates/terminal/terminal_update.html:48
#: users/templates/users/_user.html:47
#: users/templates/users/forgot_password.html:44
......@@ -699,7 +699,6 @@ msgstr "资产列表"
#: assets/templates/assets/admin_user_assets.html:54
#: assets/templates/assets/admin_user_list.html:26
#: assets/templates/assets/asset_list.html:90
#: assets/templates/assets/system_user_asset.html:52
#: assets/templates/assets/system_user_list.html:30
#: users/templates/users/user_group_granted_asset.html:47
......@@ -728,7 +727,7 @@ msgstr "测试"
#: assets/templates/assets/admin_user_detail.html:24
#: assets/templates/assets/admin_user_list.html:85
#: assets/templates/assets/asset_detail.html:24
#: assets/templates/assets/asset_list.html:170
#: assets/templates/assets/asset_list.html:174
#: assets/templates/assets/domain_detail.html:24
#: assets/templates/assets/domain_detail.html:103
#: assets/templates/assets/domain_gateway_list.html:85
......@@ -752,7 +751,7 @@ msgstr "更新"
#: assets/templates/assets/admin_user_detail.html:28
#: assets/templates/assets/admin_user_list.html:86
#: assets/templates/assets/asset_detail.html:28
#: assets/templates/assets/asset_list.html:171
#: assets/templates/assets/asset_list.html:175
#: assets/templates/assets/domain_detail.html:28
#: assets/templates/assets/domain_detail.html:104
#: assets/templates/assets/domain_gateway_list.html:86
......@@ -783,8 +782,8 @@ msgstr "选择节点"
#: assets/templates/assets/admin_user_detail.html:100
#: assets/templates/assets/asset_detail.html:200
#: assets/templates/assets/asset_list.html:600
#: assets/templates/assets/system_user_detail.html:183
#: assets/templates/assets/asset_list.html:636
#: assets/templates/assets/system_user_detail.html:192
#: assets/templates/assets/system_user_list.html:138 templates/_modal.html:22
#: terminal/templates/terminal/session_detail.html:108
#: users/templates/users/user_detail.html:362
......@@ -792,7 +791,7 @@ msgstr "选择节点"
#: users/templates/users/user_detail.html:410
#: users/templates/users/user_group_create_update.html:32
#: users/templates/users/user_group_list.html:86
#: users/templates/users/user_list.html:196
#: users/templates/users/user_list.html:199
#: users/templates/users/user_profile.html:215
msgid "Confirm"
msgstr "确认"
......@@ -815,7 +814,7 @@ msgid "Ratio"
msgstr "比例"
#: assets/templates/assets/admin_user_list.html:30
#: assets/templates/assets/asset_list.html:91
#: assets/templates/assets/asset_list.html:90
#: assets/templates/assets/domain_gateway_list.html:62
#: assets/templates/assets/domain_list.html:18
#: assets/templates/assets/label_list.html:17
......@@ -856,9 +855,9 @@ msgid "Quick modify"
msgstr "快速修改"
#: assets/templates/assets/asset_detail.html:143
#: assets/templates/assets/asset_list.html:89
#: assets/templates/assets/user_asset_list.html:47 perms/models.py:25
#: perms/models.py:70
#: assets/templates/assets/asset_list.html:88
#: assets/templates/assets/user_asset_list.html:47 perms/models.py:35
#: perms/models.py:79
#: perms/templates/perms/asset_permission_create_update.html:47
#: perms/templates/perms/asset_permission_detail.html:120
#: perms/templates/perms/asset_permission_list.html:59
......@@ -886,111 +885,119 @@ msgstr "刷新"
msgid "Update successfully!"
msgstr "更新成功"
#: assets/templates/assets/asset_list.html:63 assets/views/asset.py:97
#: assets/templates/assets/asset_list.html:62 assets/views/asset.py:97
msgid "Create asset"
msgstr "创建资产"
#: assets/templates/assets/asset_list.html:67
#: assets/templates/assets/asset_list.html:66
#: users/templates/users/user_list.html:7
msgid "Import"
msgstr "导入"
#: assets/templates/assets/asset_list.html:70
#: assets/templates/assets/asset_list.html:69
#: users/templates/users/user_list.html:10
msgid "Export"
msgstr "导出"
#: assets/templates/assets/asset_list.html:88
#: assets/templates/assets/asset_list.html:87
msgid "Hardware"
msgstr "硬件"
#: assets/templates/assets/asset_list.html:100
#: assets/templates/assets/asset_list.html:99
#: users/templates/users/user_list.html:37
msgid "Delete selected"
msgstr "批量删除"
#: assets/templates/assets/asset_list.html:101
#: assets/templates/assets/asset_list.html:100
#: users/templates/users/user_list.html:38
msgid "Update selected"
msgstr "批量更新"
#: assets/templates/assets/asset_list.html:102
#: assets/templates/assets/asset_list.html:101
msgid "Remove from this node"
msgstr "从节点移除"
#: assets/templates/assets/asset_list.html:103
#: assets/templates/assets/asset_list.html:102
#: users/templates/users/user_list.html:39
msgid "Deactive selected"
msgstr "禁用所选"
#: assets/templates/assets/asset_list.html:104
#: assets/templates/assets/asset_list.html:103
#: users/templates/users/user_list.html:40
msgid "Active selected"
msgstr "激活所选"
#: assets/templates/assets/asset_list.html:121
#: assets/templates/assets/asset_list.html:120
msgid "Add node"
msgstr "新建节点"
#: assets/templates/assets/asset_list.html:122
#: assets/templates/assets/asset_list.html:121
msgid "Rename node"
msgstr "重命名节点"
#: assets/templates/assets/asset_list.html:123
#: assets/templates/assets/asset_list.html:122
msgid "Delete node"
msgstr "删除节点"
#: assets/templates/assets/asset_list.html:125
#: assets/templates/assets/asset_list.html:124
msgid "Add assets to node"
msgstr "添加资产到节点"
#: assets/templates/assets/asset_list.html:126
#: assets/templates/assets/asset_list.html:125
msgid "Move assets to node"
msgstr "移动资产到节点"
#: assets/templates/assets/asset_list.html:128
#: assets/templates/assets/asset_list.html:127
msgid "Refresh node hardware info"
msgstr "更新节点资产硬件信息"
#: assets/templates/assets/asset_list.html:129
#: assets/templates/assets/asset_list.html:128
msgid "Test node connective"
msgstr "测试节点资产可连接性"
#: assets/templates/assets/asset_list.html:204
#: assets/templates/assets/asset_list.html:130
msgid "Display only current node assets"
msgstr "仅显示当前节点资产"
#: assets/templates/assets/asset_list.html:131
msgid "Displays all child node assets"
msgstr "显示所有子节点资产"
#: assets/templates/assets/asset_list.html:217
msgid "Create node failed"
msgstr "创建节点失败"
#: assets/templates/assets/asset_list.html:216
#: assets/templates/assets/asset_list.html:229
msgid "Have child node, cancel"
msgstr "存在子节点,不能删除"
#: assets/templates/assets/asset_list.html:218
#: assets/templates/assets/asset_list.html:231
msgid "Have assets, cancel"
msgstr "存在资产,不能删除"
#: assets/templates/assets/asset_list.html:595
#: assets/templates/assets/asset_list.html:631
#: assets/templates/assets/system_user_list.html:133
#: users/templates/users/user_detail.html:357
#: users/templates/users/user_detail.html:382
#: users/templates/users/user_group_list.html:81
#: users/templates/users/user_list.html:191
#: users/templates/users/user_list.html:194
msgid "Are you sure?"
msgstr "你确认吗?"
#: assets/templates/assets/asset_list.html:596
#: assets/templates/assets/asset_list.html:632
msgid "This will delete the selected assets !!!"
msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:604
#: assets/templates/assets/asset_list.html:640
msgid "Asset Deleted."
msgstr "已被删除"
#: assets/templates/assets/asset_list.html:605
#: assets/templates/assets/asset_list.html:610
#: assets/templates/assets/asset_list.html:641
#: assets/templates/assets/asset_list.html:646
msgid "Asset Delete"
msgstr "删除"
#: assets/templates/assets/asset_list.html:609
#: assets/templates/assets/asset_list.html:645
msgid "Asset Deleting failed."
msgstr "删除失败"
......@@ -1025,6 +1032,7 @@ msgid "Create gateway"
msgstr "创建网关"
#: assets/templates/assets/domain_gateway_list.html:87
#: assets/templates/assets/domain_gateway_list.html:89
#: common/templates/common/email_setting.html:58
#: common/templates/common/ldap_setting.html:58
msgid "Test connection"
......@@ -1073,10 +1081,23 @@ msgstr "家目录"
msgid "Uid"
msgstr "Uid"
#: assets/templates/assets/system_user_detail.html:174
#: assets/templates/assets/system_user_detail.html:153
#: assets/templates/assets/system_user_detail.html:339
msgid "Clear auth"
msgstr "清除认证信息"
#: assets/templates/assets/system_user_detail.html:156
msgid "Clear"
msgstr "清除"
#: assets/templates/assets/system_user_detail.html:183
msgid "Add to node"
msgstr "添加到节点"
#: assets/templates/assets/system_user_detail.html:339
msgid "success"
msgstr "成功"
#: assets/templates/assets/system_user_list.html:18
#: assets/views/system_user.py:45
msgid "Create system user"
......@@ -1123,7 +1144,7 @@ msgstr "批量更新资产"
msgid "Update asset"
msgstr "更新资产"
#: assets/views/asset.py:311
#: assets/views/asset.py:314
msgid "already exists"
msgstr "已经存在"
......@@ -1176,7 +1197,7 @@ msgid "System user asset"
msgstr "系统用户集群资产"
#: audits/models.py:10 audits/templates/audits/ftp_log_list.html:74
#: terminal/models.py:126 terminal/templates/terminal/session_list.html:74
#: terminal/models.py:130 terminal/templates/terminal/session_list.html:74
#: terminal/templates/terminal/terminal_detail.html:47
msgid "Remote addr"
msgstr "远端地址"
......@@ -1198,8 +1219,8 @@ msgstr "成功"
#: audits/templates/audits/ftp_log_list.html:78
#: ops/templates/ops/adhoc_history.html:52
#: ops/templates/ops/adhoc_history_detail.html:61
#: ops/templates/ops/task_history.html:58 perms/models.py:26
#: perms/templates/perms/asset_permission_detail.html:86 terminal/models.py:132
#: ops/templates/ops/task_history.html:58 perms/models.py:36
#: perms/templates/perms/asset_permission_detail.html:86 terminal/models.py:137
#: terminal/templates/terminal/session_list.html:77
msgid "Date start"
msgstr "开始日期"
......@@ -1424,6 +1445,10 @@ msgstr "终端设置"
msgid "Type"
msgstr "类型"
#: common/validators.py:7
msgid "Special char not allowed"
msgstr "不能包含特殊字符"
#: common/views.py:21 common/views.py:47 common/views.py:73 common/views.py:103
#: templates/_nav.html:81
msgid "Settings"
......@@ -1703,12 +1728,12 @@ msgstr "任务列表"
msgid "Task run history"
msgstr "执行历史"
#: perms/forms.py:18 users/forms.py:230 users/forms.py:235 users/forms.py:247
#: users/forms.py:277
#: perms/forms.py:18 users/forms.py:238 users/forms.py:243 users/forms.py:255
#: users/forms.py:285
msgid "Select users"
msgstr "选择用户"
#: perms/forms.py:34 perms/models.py:21 perms/models.py:68
#: perms/forms.py:34 perms/models.py:31 perms/models.py:77
#: perms/templates/perms/asset_permission_list.html:55
#: perms/templates/perms/asset_permission_list.html:136 templates/_nav.html:14
#: users/models/group.py:25 users/models/user.py:48
......@@ -1726,14 +1751,14 @@ msgstr ""
msgid "Asset or group at least one required"
msgstr ""
#: perms/models.py:27 perms/models.py:71
#: perms/models.py:37 perms/models.py:80
#: perms/templates/perms/asset_permission_detail.html:90
#: users/models/user.py:80 users/templates/users/user_detail.html:103
#: users/templates/users/user_profile.html:105
msgid "Date expired"
msgstr "失效日期"
#: perms/models.py:81 templates/_nav.html:34
#: perms/models.py:90 templates/_nav.html:34
msgid "Asset permission"
msgstr "资产授权"
......@@ -1770,6 +1795,10 @@ msgstr "添加节点"
msgid "Join"
msgstr "加入"
#: perms/templates/perms/asset_permission_create_update.html:53
msgid "Validity period"
msgstr "有效期"
#: perms/templates/perms/asset_permission_detail.html:66
msgid "User count"
msgstr "用户数量"
......@@ -1852,7 +1881,7 @@ msgstr "商业支持"
msgid "Docs"
msgstr "文档"
#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:113
#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:121
#: users/templates/users/_user.html:39
#: users/templates/users/first_login.html:39
#: users/templates/users/user_password_update.html:37
......@@ -1916,7 +1945,7 @@ msgstr "关闭"
#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44
#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95
#: users/views/login.py:241 users/views/login.py:289 users/views/user.py:64
#: users/views/login.py:263 users/views/login.py:321 users/views/user.py:64
#: users/views/user.py:79 users/views/user.py:99 users/views/user.py:155
#: users/views/user.py:310 users/views/user.py:357 users/views/user.py:379
msgid "Users"
......@@ -1950,11 +1979,10 @@ msgstr "命令记录"
msgid "Web terminal"
msgstr "Web终端"
#: templates/_nav.html:51 terminal/templates/terminal/session_list.html:75
#: terminal/views/command.py:47 terminal/views/session.py:75
#: terminal/views/session.py:93 terminal/views/session.py:115
#: terminal/views/terminal.py:31 terminal/views/terminal.py:46
#: terminal/views/terminal.py:58
#: templates/_nav.html:51 terminal/views/command.py:47
#: terminal/views/session.py:75 terminal/views/session.py:93
#: terminal/views/session.py:115 terminal/views/terminal.py:31
#: terminal/views/terminal.py:46 terminal/views/terminal.py:58
msgid "Terminal"
msgstr "终端管理"
......@@ -2033,26 +2061,26 @@ msgstr "线程数"
msgid "Boot Time"
msgstr "运行时间"
#: terminal/models.py:128 terminal/templates/terminal/session_list.html:102
#: terminal/models.py:132 terminal/templates/terminal/session_list.html:102
msgid "Replay"
msgstr "回放"
#: terminal/models.py:129 terminal/templates/terminal/command_list.html:55
#: terminal/models.py:133 terminal/templates/terminal/command_list.html:55
#: terminal/templates/terminal/command_list.html:71
#: terminal/templates/terminal/session_detail.html:48
#: terminal/templates/terminal/session_list.html:76
msgid "Command"
msgstr "命令"
#: terminal/models.py:131
#: terminal/models.py:136
msgid "Date last active"
msgstr "最后活跃日期"
#: terminal/models.py:133
#: terminal/models.py:138
msgid "Date end"
msgstr "结束日期"
#: terminal/models.py:150
#: terminal/models.py:155
msgid "Args"
msgstr "参数"
......@@ -2099,15 +2127,16 @@ msgstr "时长"
msgid "Monitor"
msgstr "监控"
#: terminal/templates/terminal/session_list.html:105
#: terminal/templates/terminal/session_list.html:106
#: terminal/templates/terminal/session_list.html:108
msgid "Terminate"
msgstr "终断"
#: terminal/templates/terminal/session_list.html:116
#: terminal/templates/terminal/session_list.html:120
msgid "Terminate selected"
msgstr "终断所选"
#: terminal/templates/terminal/session_list.html:136
#: terminal/templates/terminal/session_list.html:140
msgid "Terminate task send, waiting ..."
msgstr "终断任务已发送,请等待"
......@@ -2228,11 +2257,11 @@ msgstr ""
msgid "Invalid token or cache refreshed."
msgstr ""
#: users/forms.py:30
#: users/forms.py:38
msgid "MFA code"
msgstr "MFA 验证码"
#: users/forms.py:41 users/models/user.py:52
#: users/forms.py:49 users/models/user.py:52
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25
......@@ -2240,31 +2269,31 @@ msgstr "MFA 验证码"
msgid "Role"
msgstr "角色"
#: users/forms.py:44 users/forms.py:193
#: users/forms.py:52 users/forms.py:201
msgid "ssh public key"
msgstr "ssh公钥"
#: users/forms.py:45 users/forms.py:194
#: users/forms.py:53 users/forms.py:202
msgid "ssh-rsa AAAA..."
msgstr ""
#: users/forms.py:46
#: users/forms.py:54
msgid "Paste user id_rsa.pub here."
msgstr "复制用户公钥到这里"
#: users/forms.py:64 users/templates/users/user_detail.html:196
#: users/forms.py:72 users/templates/users/user_detail.html:196
msgid "Join user groups"
msgstr "添加到用户组"
#: users/forms.py:75 users/forms.py:208
#: users/forms.py:83 users/forms.py:216
msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同"
#: users/forms.py:79 users/forms.py:212 users/serializers.py:45
#: users/forms.py:87 users/forms.py:220 users/serializers.py:45
msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法"
#: users/forms.py:119
#: users/forms.py:127
msgid ""
"Tip: when enabled, you will enter the MFA binding process the next time you "
"log in. you can also directly bind in \"personal information -> quick "
......@@ -2273,16 +2302,16 @@ msgstr ""
"提示:启用之后您将会在下次登录时进入MFA绑定流程;您也可以在(个人信息->快速修"
"改->更改MFA设置)中直接绑定!"
#: users/forms.py:129
#: users/forms.py:137
msgid "* Enable MFA authentication to make the account more secure."
msgstr "* 启用MFA认证,使账号更加安全."
#: users/forms.py:134 users/models/user.py:64
#: users/forms.py:142 users/models/user.py:64
#: users/templates/users/first_login.html:45
msgid "MFA"
msgstr "MFA"
#: users/forms.py:139
#: users/forms.py:147
msgid ""
"In order to protect you and your company, please keep your account, password "
"and key sensitive information properly. (for example: setting complex "
......@@ -2291,40 +2320,41 @@ msgstr ""
"为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:"
"设置复杂密码,启用MFA认证)"
#: users/forms.py:146 users/templates/users/first_login.html:48
#: users/templates/users/first_login.html:110
#: users/forms.py:154 users/templates/users/first_login.html:48
#: users/templates/users/first_login.html:107
#: users/templates/users/first_login.html:130
msgid "Finish"
msgstr "完成"
#: users/forms.py:152
#: users/forms.py:160
msgid "Old password"
msgstr "原来密码"
#: users/forms.py:157
#: users/forms.py:165
msgid "New password"
msgstr "新密码"
#: users/forms.py:162
#: users/forms.py:170
msgid "Confirm password"
msgstr "确认密码"
#: users/forms.py:172
#: users/forms.py:180
msgid "Old password error"
msgstr "原来密码错误"
#: users/forms.py:180
#: users/forms.py:188
msgid "Password does not match"
msgstr "密码不一致"
#: users/forms.py:191
#: users/forms.py:199
msgid "Automatically configure and download the SSH key"
msgstr "自动配置并下载SSH密钥"
#: users/forms.py:195
#: users/forms.py:203
msgid "Paste your id_rsa.pub here."
msgstr "复制你的公钥到这里"
#: users/forms.py:223 users/models/user.py:72
#: users/forms.py:231 users/models/user.py:72
#: users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:43
#: users/templates/users/user_profile.html:68
......@@ -2443,11 +2473,15 @@ msgstr "首次登陆"
msgid "I agree with the terms and conditions."
msgstr "我同意条款和条件"
#: users/templates/users/first_login.html:100
#: users/templates/users/first_login.html:73
msgid "Please choose the terms and conditions."
msgstr "请选择同意条款和条件"
#: users/templates/users/first_login.html:101
msgid "Previous"
msgstr "上一步"
#: users/templates/users/first_login.html:108
#: users/templates/users/first_login.html:105
#: users/templates/users/login_otp.html:66
#: users/templates/users/user_otp_authentication.html:22
#: users/templates/users/user_otp_enable_bind.html:19
......@@ -2506,7 +2540,7 @@ msgid "Can't provide security? Please contact the administrator!"
msgstr "如果不能提供MFA验证码,请联系管理员!"
#: users/templates/users/reset_password.html:45
#: users/templates/users/user_detail.html:348 users/utils.py:73
#: users/templates/users/user_detail.html:348 users/utils.py:76
msgid "Reset password"
msgstr "重置密码"
......@@ -2643,20 +2677,20 @@ msgstr "用户组删除"
msgid "UserGroup Deleting failed."
msgstr "用户组删除失败"
#: users/templates/users/user_list.html:192
#: users/templates/users/user_list.html:195
msgid "This will delete the selected users !!!"
msgstr "删除选中用户 !!!"
#: users/templates/users/user_list.html:200
#: users/templates/users/user_list.html:203
msgid "User Deleted."
msgstr "已被删除"
#: users/templates/users/user_list.html:201
#: users/templates/users/user_list.html:206
#: users/templates/users/user_list.html:204
#: users/templates/users/user_list.html:209
msgid "User Delete"
msgstr "删除"
#: users/templates/users/user_list.html:205
#: users/templates/users/user_list.html:208
msgid "User Deleting failed."
msgstr "用户删除失败"
......@@ -2710,13 +2744,15 @@ msgid "Create account successfully"
msgstr "创建账户成功"
#: users/utils.py:39
#, python-format
#, fuzzy, python-format
msgid ""
"\n"
" Hello %(name)s:\n"
" </br>\n"
" Your account has been created successfully\n"
" </br>\n"
" Username: %(username)s\n"
" </br>\n"
" <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">click "
"here to set your password</a>\n"
" </br>\n"
......@@ -2736,6 +2772,8 @@ msgstr ""
" 你好 %(name)s:\n"
" </br>\n"
" 恭喜您,您的账号已经创建成功 </br>\n"
" 用户名: %(username)s\n"
" </br>\n"
" <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">请点击这"
"里设置密码</a> </br>\n"
" 这个链接有效期1小时, 超过时间您可以 <a href=\"%(forget_password_url)s?"
......@@ -2750,7 +2788,7 @@ msgstr ""
" </br>\n"
" "
#: users/utils.py:75
#: users/utils.py:78
#, python-format
msgid ""
"\n"
......@@ -2794,11 +2832,11 @@ msgstr ""
" </br>\n"
" "
#: users/utils.py:106
#: users/utils.py:109
msgid "SSH Key Reset"
msgstr "重置ssh密钥"
#: users/utils.py:108
#: users/utils.py:111
#, python-format
msgid ""
"\n"
......@@ -2823,15 +2861,15 @@ msgstr ""
" </br>\n"
" "
#: users/utils.py:141
#: users/utils.py:144
msgid "User not exist"
msgstr "用户不存在"
#: users/utils.py:143
#: users/utils.py:146
msgid "Disabled or expired"
msgstr "禁用或失效"
#: users/utils.py:156
#: users/utils.py:159
msgid "Password or SSH public key invalid"
msgstr "密码或密钥不合法"
......@@ -2847,56 +2885,56 @@ msgstr "更新用户组"
msgid "User group granted asset"
msgstr "用户组授权资产"
#: users/views/login.py:56
#: users/views/login.py:59
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: users/views/login.py:107 users/views/user.py:464 users/views/user.py:489
#: users/views/login.py:125 users/views/user.py:464 users/views/user.py:489
msgid "MFA code invalid"
msgstr "MFA码认证失败"
#: users/views/login.py:133
#: users/views/login.py:151
msgid "Logout success"
msgstr "退出登录成功"
#: users/views/login.py:134
#: users/views/login.py:152
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
#: users/views/login.py:150
#: users/views/login.py:168
msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入"
#: users/views/login.py:163
#: users/views/login.py:181
msgid "Send reset password message"
msgstr "发送重置密码邮件"
#: users/views/login.py:164
#: users/views/login.py:182
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
#: users/views/login.py:177
#: users/views/login.py:195
msgid "Reset password success"
msgstr "重置密码成功"
#: users/views/login.py:178
#: users/views/login.py:196
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
#: users/views/login.py:195 users/views/login.py:208
#: users/views/login.py:213 users/views/login.py:226
msgid "Token invalid or expired"
msgstr "Token错误或失效"
#: users/views/login.py:204
#: users/views/login.py:222
msgid "Password not same"
msgstr "密码不一致"
#: users/views/login.py:241
#: users/views/login.py:263
msgid "First login"
msgstr "首次登陆"
#: users/views/login.py:290
#: users/views/login.py:322
msgid "Login log list"
msgstr "登录日志"
......@@ -2943,9 +2981,3 @@ msgstr "MFA 解绑成功"
#: users/views/user.py:519
msgid "MFA disable success, return login page"
msgstr "MFA 解绑成功,返回登录页面"
#~ msgid "Step"
#~ msgstr "Step"
#~ msgid "Add asset"
#~ msgstr "添加资产到节点"
......@@ -229,7 +229,11 @@ LOGGING = {
'django_auth_ldap': {
'handlers': ['console', 'ansible_logs'],
'level': "INFO",
}
},
# 'django.db': {
# 'handlers': ['console', 'file'],
# 'level': 'DEBUG'
# }
}
}
......@@ -329,6 +333,9 @@ AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
AUTH_LDAP_GROUP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_GROUP_SEARCH_FILTER
)
AUTH_LDAP_CONNECTION_OPTIONS = {
ldap.OPT_TIMEOUT: 5
}
AUTH_LDAP_ALWAYS_UPDATE_USER = True
AUTH_LDAP_BACKEND = 'django_auth_ldap.backend.LDAPBackend'
......
......@@ -72,7 +72,7 @@ class CeleryTaskLogApi(generics.RetrieveAPIView):
def get(self, request, *args, **kwargs):
mark = request.query_params.get("mark") or str(uuid.uuid4())
task = super().get_object()
task = self.get_object()
log_path = task.full_log_path
if not log_path or not os.path.isfile(log_path):
......
......@@ -19,7 +19,7 @@
<a href="{% url 'ops:adhoc-history-detail' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Run history detail' %} </a>
</li>
<li>
<a class="text-center celery-task-log" onclick="window.open('{% url 'ops:celery-task-log' pk=object.pk %}','', 'width=800,height=600')"><i class="fa fa-laptop"></i> {% trans 'Output' %} </a>
<a class="text-center celery-task-log" onclick="window.open('{% url 'ops:celery-task-log' pk=object.pk %}','', 'width=800,height=600,left=400,top=400')"><i class="fa fa-laptop"></i> {% trans 'Output' %} </a>
</li>
</ul>
</div>
......
......@@ -2,38 +2,25 @@
<head>
<title>term.js</title>
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
<script src="{% static 'js/plugins/xterm/xterm.js' %}"></script>
<link rel="stylesheet" href="{% static 'js/plugins/xterm/xterm.css' %}" />
<style>
html {
background: #000;
body {
background-color: black;
}
h1 {
margin-bottom: 20px;
font: 20px/1.5 sans-serif;
}
.terminal {
float: left;
font-family: 'Monaco', 'Consolas', "DejaVu Sans Mono", "Liberation Mono", monospace;
font-size: 12px;
color: #f0f0f0;
background-color: #555;
padding: 20px 20px 20px;
}
.terminal-cursor {
color: #000;
background: #f0f0f0;
.xterm-rows {
{#padding: 15px;#}
font-family: "Bitstream Vera Sans Mono", Monaco, "Consolas", Courier, monospace;
font-size: 13px;
}
</style>
</head>
<div class="container">
<div id="term">
<div id="term" style="height: 100%;width: 100%">
</div>
</div>
<script src="{% static 'js/term.js' %}"></script>
<script>
var rowHeight = 1;
var colWidth = 1;
var rowHeight = 18;
var colWidth = 10;
var mark = '';
var url = "{% url 'api-ops:celery-task-log' pk=object.id %}";
var term;
......@@ -42,13 +29,16 @@
var interval = 200;
function calWinSize() {
var t = $('.terminal');
rowHeight = 1.00 * t.height() / 24;
colWidth = 1.00 * t.width() / 80;
var t = $('#marker');
{#rowHeight = 1.00 * t.height();#}
{#colWidth = 1.00 * t.width() / 6;#}
}
function resize() {
var rows = Math.floor(window.innerHeight / rowHeight) - 2;
var cols = Math.floor(window.innerWidth / colWidth) - 10;
{#console.log(rowHeight, window.innerHeight);#}
{#console.log(colWidth, window.innerWidth);#}
var rows = Math.floor(window.innerHeight / rowHeight) - 1;
var cols = Math.floor(window.innerWidth / colWidth) - 2;
console.log(rows, cols);
term.resize(cols, rows);
}
function requestAndWrite() {
......@@ -74,21 +64,18 @@
}
}
$(document).ready(function () {
term = new Terminal({
cols: 80,
rows: 24,
useStyle: true,
screenKeys: false,
convertEol: false,
cursorBlink: false
});
term.open();
term = new Terminal();
term.open(document.getElementById('term'));
term.resize(80, 24);
resize();
term.on('data', function (data) {
term.write(data.replace('\r', '\r\n'))
{#term.write(data.replace('\r', '\r\n'))#}
term.write(data);
});
calWinSize();
resize();
$('.terminal').detach().appendTo('#term');
window.onresize = function () {
resize()
};
{#$('.terminal').detach().appendTo('#term');#}
setInterval(function () {
requestAndWrite()
}, interval)
......
......@@ -25,7 +25,7 @@
<a href="{% url 'ops:task-history' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Run history' %} </a>
</li>
<li>
<a class="text-center celery-task-log" onclick="window.open('{% url 'ops:celery-task-log' pk=object.latest_history.pk %}','', 'width=800,height=600')"><i class="fa fa-laptop"></i> {% trans 'Last run output' %} </a>
<a class="text-center celery-task-log" onclick="window.open('{% url 'ops:celery-task-log' pk=object.latest_history.pk %}','', 'width=800,height=600,left=400,top=400')"><i class="fa fa-laptop"></i> {% trans 'Last run output' %} </a>
</li>
</ul>
</div>
......
......@@ -25,7 +25,7 @@
<a href="{% url 'ops:task-history' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Run history' %} </a>
</li>
<li>
<a class="text-center celery-task-log" onclick="window.open('{% url 'ops:celery-task-log' pk=object.latest_history.pk %}','', 'width=800,height=600')"><i class="fa fa-laptop"></i> {% trans 'Last run output' %} </a>
<a class="text-center celery-task-log" onclick="window.open('{% url 'ops:celery-task-log' pk=object.latest_history.pk %}','', 'width=800,height=600,left=400,top=400')"><i class="fa fa-laptop"></i> {% trans 'Last run output' %} </a>
</li>
</ul>
</div>
......
......@@ -25,7 +25,7 @@
<a href="{% url 'ops:task-history' pk=object.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Run history' %} </a>
</li>
<li>
<a class="text-center celery-task-log" onclick="window.open('{% url 'ops:celery-task-log' pk=object.latest_history.pk %}','', 'width=800,height=600')"><i class="fa fa-laptop"></i> {% trans 'Last run output' %} </a>
<a class="text-center celery-task-log" onclick="window.open('{% url 'ops:celery-task-log' pk=object.latest_history.pk %}','', 'width=800,height=600,left=400,top=400')"><i class="fa fa-laptop"></i> {% trans 'Last run output' %} </a>
</li>
</ul>
</div>
......
......@@ -115,7 +115,7 @@ $(document).ready(function() {
var success = function(data) {
var task_id = data.task;
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600')
window.open(url, '', 'width=800,height=600,left=400,top=400')
};
APIUpdateAttr({
url: the_url,
......
......@@ -6,7 +6,7 @@ from rest_framework.views import APIView, Response
from rest_framework.generics import ListAPIView, get_object_or_404, RetrieveUpdateAPIView
from rest_framework import viewsets
from common.utils import set_or_append_attr_bulk
from common.utils import set_or_append_attr_bulk, get_object_or_none
from users.permissions import IsValidUser, IsSuperUser, IsSuperUserOrAppUser
from .utils import AssetPermissionUtil
from .models import AssetPermission
......@@ -41,7 +41,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
asset = get_object_or_404(Asset, pk=asset_id)
permissions = set(queryset.filter(assets=asset))
for node in asset.nodes.all():
inherit_nodes.update(set(node.ancestor_with_node))
inherit_nodes.update(set(node.ancestor_with_self))
elif node_id:
node = get_object_or_404(Node, pk=node_id)
permissions = set(queryset.filter(nodes=node))
......@@ -147,8 +147,13 @@ class UserGrantedNodeAssetsApi(ListAPIView):
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
node = get_object_or_404(Node, id=node_id)
nodes = AssetPermissionUtil.get_user_nodes_with_assets(user)
node = get_object_or_none(Node, id=node_id)
if not node:
unnode = [node for node in nodes if node.name == 'Unnode']
node = unnode[0] if unnode else None
assets = nodes.get(node, [])
for asset, system_users in assets.items():
asset.system_users_granted = system_users
......
......@@ -7,13 +7,23 @@ from django.utils import timezone
from common.utils import date_expired_default, set_or_append_attr_bulk
class ValidManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True) \
.filter(date_start__lt=timezone.now())\
class AssetPermissionQuerySet(models.QuerySet):
def active(self):
return self.filter(is_active=True)
def valid(self):
return self.active().filter(date_start__lt=timezone.now())\
.filter(date_expired__gt=timezone.now())
class AssetPermissionManager(models.Manager):
def get_queryset(self):
return AssetPermissionQuerySet(self.model, using=self._db)
def valid(self):
return self.get_queryset().valid()
class AssetPermission(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
......@@ -23,14 +33,13 @@ class AssetPermission(models.Model):
nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes"))
system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_permissions', verbose_name=_("System user"))
is_active = models.BooleanField(default=True, verbose_name=_('Active'))
date_start = models.DateTimeField(default=timezone.now, verbose_name=_("Date start"))
date_expired = models.DateTimeField(default=date_expired_default, verbose_name=_('Date expired'))
date_start = models.DateTimeField(default=timezone.now, db_index=True, verbose_name=_("Date start"))
date_expired = models.DateTimeField(default=date_expired_default, db_index=True, verbose_name=_('Date expired'))
created_by = models.CharField(max_length=128, blank=True, verbose_name=_('Created by'))
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
comment = models.TextField(verbose_name=_('Comment'), blank=True)
objects = models.Manager()
valid = ValidManager()
objects = AssetPermissionManager()
def __str__(self):
return self.name
......
......@@ -9,7 +9,7 @@ from common.fields import StringManyToManyField
class AssetPermissionCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = AssetPermission
exclude = ('id', 'created_by', 'date_created')
exclude = ('created_by', 'date_created')
class AssetPermissionListSerializer(serializers.ModelSerializer):
......
......@@ -50,7 +50,7 @@
</div>
</div>
<div class="form-group {% if form.date_expired.errors or form.date_start.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{% trans 'Validity period' %}</label>
<div class="col-sm-9">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
......
......@@ -78,12 +78,12 @@ var zTree, table, show = 0;
function onSelected(event, treeNode) {
setCookie('node_selected', treeNode.id);
var url = table.ajax.url();
if (treeNode.is_asset) {
url = setUrlParam(url, 'node', "");
url = setUrlParam(url, 'asset', treeNode.id)
} else {
if (treeNode.is_node) {
url = setUrlParam(url, 'asset', "");
url = setUrlParam(url, 'node', treeNode.id)
} else {
url = setUrlParam(url, 'node', "");
url = setUrlParam(url, 'asset', treeNode.id)
}
setCookie('node_selected', treeNode.id);
table.ajax.url(url);
......@@ -113,14 +113,14 @@ function filter(treeId, parentNode, childNodes) {
$.each(childNodes, function (index, value) {
value["pId"] = value["parent"];
value["name"] = value["value"];
value["isParent"] = value["assets_amount"] !== 0;
value["iconSkin"] = value["is_asset"] ? "file" : null;
value["isParent"] = value["is_node"];
value["iconSkin"] = value["is_node"] ? null : 'file';
});
return childNodes;
}
function beforeAsync(treeId, treeNode) {
return true;
return treeNode.is_node
}
function makeLabel(data) {
......@@ -226,7 +226,7 @@ function initTree() {
},
async: {
enable: true,
url: "{% url 'api-assets:node-children-2' %}?assets=1",
url: "{% url 'api-assets:node-children-2' %}?assets=1&all=",
autoParam:["id", "name=n", "level=lv"],
dataFilter: filter,
type: 'get'
......@@ -238,18 +238,19 @@ function initTree() {
};
var zNodes = [];
$.get("{% url 'api-assets:node-children-2' %}", function(data, status){
$.get("{% url 'api-assets:node-children-2' %}?assets=1&all=", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
value["isParent"] = value["assets_amount"] !== 0;
value["name"] = value["value"];
value["open"] = value["key"] === "0";
value["isParent"] = value["is_node"];
value["iconSkin"] = value["is_node"] ? null : 'file';
});
zNodes = data;
{#$.fn.zTree.init($("#assetTree"), setting);#}
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
selectQueryNode();
{#selectQueryNode();#}
});
}
......@@ -286,10 +287,10 @@ $(document).ready(function(){
var _nodes = [];
var _assets = [];
$.each(nodes, function (id, node) {
if (node.is_asset) {
_assets.push(node.id)
} else {
if (node.is_node) {
_nodes.push(node.id)
} else {
_assets.push(node.id)
}
});
url += "?assets=" + _assets.join(",") + "&nodes=" + _nodes.join(",");
......@@ -303,6 +304,7 @@ $(document).ready(function(){
if (row.child.isShown()) {
tr.removeClass('details');
$(this).children('i:first-child').removeClass('fa-angle-down').addClass('fa-angle-right');
row.child.hide();
// Remove from the 'open' array
......@@ -310,7 +312,7 @@ $(document).ready(function(){
}
else {
tr.addClass('details');
$('.toggle i').removeClass('fa-angle-right').addClass('fa-angle-down');
$(this).children('i:first-child').removeClass('fa-angle-right').addClass('fa-angle-down');
row.child(format(row.data())).show();
// Add to the 'open' array
if ( idx === -1 ) {
......
......@@ -8,31 +8,75 @@ import copy
from common.utils import set_or_append_attr_bulk, get_logger
from .models import AssetPermission
from .hands import Node
logger = get_logger(__file__)
class AssetPermissionUtil:
class Tree:
def __init__(self):
self.__all_nodes = list(Node.objects.all())
self.__node_asset_map = defaultdict(set)
self.nodes = defaultdict(dict)
self.root = Node.root()
self.init_node_asset_map()
def init_node_asset_map(self):
for node in self.__all_nodes:
assets = node.get_assets().values_list('id', flat=True)
for asset in assets:
self.__node_asset_map[str(asset)].add(node)
def add_asset(self, asset, system_users):
nodes = self.__node_asset_map.get(str(asset.id), [])
self.add_nodes(nodes)
for node in nodes:
self.nodes[node][asset].update(system_users)
def add_node(self, node):
if node in self.nodes:
return
else:
self.nodes[node] = defaultdict(set)
if node.key == self.root.key:
return
parent_key = ':'.join(node.key.split(':')[:-1])
for n in self.__all_nodes:
if n.key == parent_key:
self.add_node(n)
break
def add_nodes(self, nodes):
for node in nodes:
self.add_node(node)
class AssetPermissionUtil:
@staticmethod
def get_user_permissions(user):
return AssetPermission.valid.all().filter(users=user)
return AssetPermission.objects.all().valid().filter(users=user)
@staticmethod
def get_user_group_permissions(user_group):
return AssetPermission.valid.all().filter(user_groups=user_group)
return AssetPermission.objects.all().valid().filter(
user_groups=user_group
)
@staticmethod
def get_asset_permissions(asset):
return AssetPermission.valid.all().filter(assets=asset)
return AssetPermission.objects.all().valid().filter(
assets=asset
)
@staticmethod
def get_node_permissions(node):
return AssetPermission.valid.all().filter(nodes=node)
return AssetPermission.objects.all().valid().filter(nodes=node)
@staticmethod
def get_system_user_permissions(system_user):
return AssetPermission.objects.all().filter(system_users=system_user)
return AssetPermission.objects.valid().all().filter(
system_users=system_user
)
@classmethod
def get_user_group_nodes(cls, group):
......@@ -51,7 +95,7 @@ class AssetPermissionUtil:
assets = defaultdict(set)
permissions = cls.get_user_group_permissions(group)
for perm in permissions:
_assets = perm.assets.all()
_assets = perm.assets.all().valid()
_system_users = perm.system_users.all()
set_or_append_attr_bulk(_assets, 'permission', perm.id)
for asset in _assets:
......@@ -63,7 +107,7 @@ class AssetPermissionUtil:
assets = defaultdict(set)
nodes = cls.get_user_group_nodes(group)
for node, _system_users in nodes.items():
_assets = node.get_all_assets()
_assets = node.get_all_valid_assets()
set_or_append_attr_bulk(_assets, 'inherit_node', node.id)
set_or_append_attr_bulk(_assets, 'permission', getattr(node, 'permission', None))
for asset in _assets:
......@@ -82,28 +126,26 @@ class AssetPermissionUtil:
return assets
@classmethod
def get_user_group_nodes_with_assets(cls, user):
def get_user_group_nodes_with_assets(cls, group):
"""
:param user:
:param group:
:return: {node: {asset: set(su1, su2)}}
"""
nodes = defaultdict(dict)
_assets = cls.get_user_group_assets(user)
_assets = cls.get_user_group_assets(group)
tree = Tree()
for asset, _system_users in _assets.items():
_nodes = asset.get_nodes()
tree.add_nodes(_nodes)
for node in _nodes:
if asset in nodes[node]:
nodes[node][asset].update(_system_users)
else:
nodes[node][asset] = _system_users
return nodes
tree.nodes[node][asset].update(_system_users)
return tree.nodes
@classmethod
def get_user_assets_direct(cls, user):
assets = defaultdict(set)
permissions = list(cls.get_user_permissions(user))
for perm in permissions:
_assets = perm.assets.all()
_assets = perm.assets.all().valid()
_system_users = perm.system_users.all()
set_or_append_attr_bulk(_assets, 'permission', perm.id)
for asset in _assets:
......@@ -122,12 +164,30 @@ class AssetPermissionUtil:
nodes[node].update(set(_system_users))
return nodes
@classmethod
def get_user_nodes_inherit_group(cls, user):
nodes = defaultdict(set)
groups = user.groups.all()
for group in groups:
_nodes = cls.get_user_group_nodes(group)
for node, system_users in _nodes.items():
nodes[node].update(set(system_users))
return nodes
@classmethod
def get_user_nodes(cls, user):
nodes = cls.get_user_nodes_direct(user)
nodes_inherit = cls.get_user_nodes_inherit_group(user)
for node, system_users in nodes_inherit.items():
nodes[node].update(set(system_users))
return nodes
@classmethod
def get_user_nodes_assets_direct(cls, user):
assets = defaultdict(set)
nodes = cls.get_user_nodes_direct(user)
for node, _system_users in nodes.items():
_assets = node.get_all_assets()
_assets = node.get_all_valid_assets()
set_or_append_attr_bulk(_assets, 'inherit_node', node.id)
set_or_append_attr_bulk(_assets, 'permission', getattr(node, 'permission', None))
for asset in _assets:
......@@ -164,26 +224,25 @@ class AssetPermissionUtil:
:param user:
:return: {node: {asset: set(su1, su2)}}
"""
nodes = defaultdict(dict)
tree = Tree()
_assets = cls.get_user_assets(user)
for asset, _system_users in _assets.items():
_nodes = asset.get_nodes()
for node in _nodes:
if asset in nodes[node]:
nodes[node][asset].update(_system_users)
else:
nodes[node][asset] = _system_users
return nodes
tree.add_asset(asset, _system_users)
# _nodes = asset.get_nodes()
# tree.add_nodes(_nodes)
# for node in _nodes:
# tree.nodes[node][asset].update(_system_users)
return tree.nodes
@classmethod
def get_system_user_assets(cls, system_user):
assets = set()
permissions = cls.get_system_user_permissions(system_user)
for perm in permissions:
assets.update(set(perm.assets.all()))
assets.update(set(perm.assets.all().valid()))
nodes = perm.nodes.all()
for node in nodes:
assets.update(set(node.get_all_assets()))
assets.update(set(node.get_all_valid_assets()))
return assets
@classmethod
......@@ -228,7 +287,7 @@ class NodePermissionUtil:
nodes = copy.deepcopy(nodes_directed)
for node, system_users in nodes_directed.items():
for child in node.get_family():
for child in node.get_all_children_with_self():
nodes[child].update(system_users)
return nodes
......@@ -243,7 +302,7 @@ class NodePermissionUtil:
nodes_with_assets = dict()
for node, system_users in nodes.items():
nodes_with_assets[node] = {
'assets': node.get_active_assets(),
'assets': node.get_valid_assets(),
'system_users': system_users
}
return nodes_with_assets
......@@ -274,7 +333,7 @@ class NodePermissionUtil:
nodes_with_assets = dict()
for node, system_users in nodes.items():
nodes_with_assets[node] = {
'assets': node.get_active_assets(),
'assets': node.get_valid_assets(),
'system_users': system_users
}
return nodes_with_assets
......
......@@ -42,7 +42,7 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
if nodes_id:
nodes_id = nodes_id.split(",")
nodes = Node.objects.filter(id__in=nodes_id)
nodes = Node.objects.filter(id__in=nodes_id).exclude(id=Node.root().id)
form['nodes'].initial = nodes
if assets_id:
assets_id = assets_id.split(",")
......
/**
* xterm.js: xterm, in the browser
* Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* https://github.com/chjj/term.js
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*/
/*
* Default style for xterm.js
*/
.terminal {
background-color: #000;
color: #fff;
font-family: courier-new, courier, monospace;
font-feature-settings: "liga" 0;
position: relative;
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.terminal.focus,
.terminal:focus {
outline: none;
}
.terminal .xterm-helpers {
position: absolute;
top: 0;
}
.terminal .xterm-helper-textarea {
/*
* HACK: to fix IE's blinking cursor
* Move textarea out of the screen to the far left, so that the cursor is not visible.
*/
position: absolute;
opacity: 0;
left: -9999em;
top: 0;
width: 0;
height: 0;
z-index: -10;
/** Prevent wrapping so the IME appears against the textarea at the correct position */
white-space: nowrap;
overflow: hidden;
resize: none;
}
.terminal a {
color: inherit;
text-decoration: none;
}
.terminal a:hover {
cursor: pointer;
text-decoration: underline;
}
.terminal a.xterm-invalid-link:hover {
cursor: text;
text-decoration: none;
}
.terminal .terminal-cursor {
position: relative;
}
.terminal:not(.focus) .terminal-cursor {
outline: 1px solid #fff;
outline-offset: -1px;
}
.terminal.xterm-cursor-style-block.focus:not(.xterm-cursor-blink-on) .terminal-cursor {
background-color: #fff;
color: #000;
}
.terminal.focus.xterm-cursor-style-bar:not(.xterm-cursor-blink-on) .terminal-cursor::before,
.terminal.focus.xterm-cursor-style-underline:not(.xterm-cursor-blink-on) .terminal-cursor::before {
content: '';
position: absolute;
background-color: #fff;
}
.terminal.focus.xterm-cursor-style-bar:not(.xterm-cursor-blink-on) .terminal-cursor::before {
top: 0;
left: 0;
bottom: 0;
width: 1px;
}
.terminal.focus.xterm-cursor-style-underline:not(.xterm-cursor-blink-on) .terminal-cursor::before {
bottom: 0;
left: 0;
right: 0;
height: 1px;
}
.terminal .composition-view {
background: #000;
color: #FFF;
display: none;
position: absolute;
white-space: nowrap;
z-index: 1;
}
.terminal .composition-view.active {
display: block;
}
.terminal .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
overflow-y: scroll;
}
.terminal .xterm-wide-char,
.terminal .xterm-normal-char {
display: inline-block;
}
.terminal .xterm-rows {
position: absolute;
left: 0;
top: 0;
}
.terminal .xterm-rows > div {
/* Lines containing spans and text nodes ocassionally wrap despite being the same width (#327) */
white-space: nowrap;
}
.terminal .xterm-scroll-area {
visibility: hidden;
}
.terminal .xterm-char-measure-element {
display: inline-block;
visibility: hidden;
position: absolute;
left: -9999em;
}
.terminal.enable-mouse-events {
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
cursor: default;
}
.terminal .xterm-selection {
position: absolute;
top: 0;
left: 0;
z-index: 1;
opacity: 0.3;
pointer-events: none;
}
.terminal .xterm-selection div {
position: absolute;
background-color: #fff;
}
/*
* Determine default colors for xterm.js
*/
.terminal .xterm-bold {
font-weight: bold;
}
.terminal .xterm-underline {
text-decoration: underline;
}
.terminal .xterm-blink {
text-decoration: blink;
}
.terminal .xterm-blink.xterm-underline {
text-decoration: blink underline;
}
.terminal .xterm-hidden {
visibility: hidden;
}
.terminal .xterm-color-0 {
color: #2e3436;
}
.terminal .xterm-bg-color-0 {
background-color: #2e3436;
}
.terminal .xterm-color-1 {
color: #cc0000;
}
.terminal .xterm-bg-color-1 {
background-color: #cc0000;
}
.terminal .xterm-color-2 {
color: #4e9a06;
}
.terminal .xterm-bg-color-2 {
background-color: #4e9a06;
}
.terminal .xterm-color-3 {
color: #c4a000;
}
.terminal .xterm-bg-color-3 {
background-color: #c4a000;
}
.terminal .xterm-color-4 {
color: #3465a4;
}
.terminal .xterm-bg-color-4 {
background-color: #3465a4;
}
.terminal .xterm-color-5 {
color: #75507b;
}
.terminal .xterm-bg-color-5 {
background-color: #75507b;
}
.terminal .xterm-color-6 {
color: #06989a;
}
.terminal .xterm-bg-color-6 {
background-color: #06989a;
}
.terminal .xterm-color-7 {
color: #d3d7cf;
}
.terminal .xterm-bg-color-7 {
background-color: #d3d7cf;
}
.terminal .xterm-color-8 {
color: #555753;
}
.terminal .xterm-bg-color-8 {
background-color: #555753;
}
.terminal .xterm-color-9 {
color: #ef2929;
}
.terminal .xterm-bg-color-9 {
background-color: #ef2929;
}
.terminal .xterm-color-10 {
color: #8ae234;
}
.terminal .xterm-bg-color-10 {
background-color: #8ae234;
}
.terminal .xterm-color-11 {
color: #fce94f;
}
.terminal .xterm-bg-color-11 {
background-color: #fce94f;
}
.terminal .xterm-color-12 {
color: #729fcf;
}
.terminal .xterm-bg-color-12 {
background-color: #729fcf;
}
.terminal .xterm-color-13 {
color: #ad7fa8;
}
.terminal .xterm-bg-color-13 {
background-color: #ad7fa8;
}
.terminal .xterm-color-14 {
color: #34e2e2;
}
.terminal .xterm-bg-color-14 {
background-color: #34e2e2;
}
.terminal .xterm-color-15 {
color: #eeeeec;
}
.terminal .xterm-bg-color-15 {
background-color: #eeeeec;
}
.terminal .xterm-color-16 {
color: #000000;
}
.terminal .xterm-bg-color-16 {
background-color: #000000;
}
.terminal .xterm-color-17 {
color: #00005f;
}
.terminal .xterm-bg-color-17 {
background-color: #00005f;
}
.terminal .xterm-color-18 {
color: #000087;
}
.terminal .xterm-bg-color-18 {
background-color: #000087;
}
.terminal .xterm-color-19 {
color: #0000af;
}
.terminal .xterm-bg-color-19 {
background-color: #0000af;
}
.terminal .xterm-color-20 {
color: #0000d7;
}
.terminal .xterm-bg-color-20 {
background-color: #0000d7;
}
.terminal .xterm-color-21 {
color: #0000ff;
}
.terminal .xterm-bg-color-21 {
background-color: #0000ff;
}
.terminal .xterm-color-22 {
color: #005f00;
}
.terminal .xterm-bg-color-22 {
background-color: #005f00;
}
.terminal .xterm-color-23 {
color: #005f5f;
}
.terminal .xterm-bg-color-23 {
background-color: #005f5f;
}
.terminal .xterm-color-24 {
color: #005f87;
}
.terminal .xterm-bg-color-24 {
background-color: #005f87;
}
.terminal .xterm-color-25 {
color: #005faf;
}
.terminal .xterm-bg-color-25 {
background-color: #005faf;
}
.terminal .xterm-color-26 {
color: #005fd7;
}
.terminal .xterm-bg-color-26 {
background-color: #005fd7;
}
.terminal .xterm-color-27 {
color: #005fff;
}
.terminal .xterm-bg-color-27 {
background-color: #005fff;
}
.terminal .xterm-color-28 {
color: #008700;
}
.terminal .xterm-bg-color-28 {
background-color: #008700;
}
.terminal .xterm-color-29 {
color: #00875f;
}
.terminal .xterm-bg-color-29 {
background-color: #00875f;
}
.terminal .xterm-color-30 {
color: #008787;
}
.terminal .xterm-bg-color-30 {
background-color: #008787;
}
.terminal .xterm-color-31 {
color: #0087af;
}
.terminal .xterm-bg-color-31 {
background-color: #0087af;
}
.terminal .xterm-color-32 {
color: #0087d7;
}
.terminal .xterm-bg-color-32 {
background-color: #0087d7;
}
.terminal .xterm-color-33 {
color: #0087ff;
}
.terminal .xterm-bg-color-33 {
background-color: #0087ff;
}
.terminal .xterm-color-34 {
color: #00af00;
}
.terminal .xterm-bg-color-34 {
background-color: #00af00;
}
.terminal .xterm-color-35 {
color: #00af5f;
}
.terminal .xterm-bg-color-35 {
background-color: #00af5f;
}
.terminal .xterm-color-36 {
color: #00af87;
}
.terminal .xterm-bg-color-36 {
background-color: #00af87;
}
.terminal .xterm-color-37 {
color: #00afaf;
}
.terminal .xterm-bg-color-37 {
background-color: #00afaf;
}
.terminal .xterm-color-38 {
color: #00afd7;
}
.terminal .xterm-bg-color-38 {
background-color: #00afd7;
}
.terminal .xterm-color-39 {
color: #00afff;
}
.terminal .xterm-bg-color-39 {
background-color: #00afff;
}
.terminal .xterm-color-40 {
color: #00d700;
}
.terminal .xterm-bg-color-40 {
background-color: #00d700;
}
.terminal .xterm-color-41 {
color: #00d75f;
}
.terminal .xterm-bg-color-41 {
background-color: #00d75f;
}
.terminal .xterm-color-42 {
color: #00d787;
}
.terminal .xterm-bg-color-42 {
background-color: #00d787;
}
.terminal .xterm-color-43 {
color: #00d7af;
}
.terminal .xterm-bg-color-43 {
background-color: #00d7af;
}
.terminal .xterm-color-44 {
color: #00d7d7;
}
.terminal .xterm-bg-color-44 {
background-color: #00d7d7;
}
.terminal .xterm-color-45 {
color: #00d7ff;
}
.terminal .xterm-bg-color-45 {
background-color: #00d7ff;
}
.terminal .xterm-color-46 {
color: #00ff00;
}
.terminal .xterm-bg-color-46 {
background-color: #00ff00;
}
.terminal .xterm-color-47 {
color: #00ff5f;
}
.terminal .xterm-bg-color-47 {
background-color: #00ff5f;
}
.terminal .xterm-color-48 {
color: #00ff87;
}
.terminal .xterm-bg-color-48 {
background-color: #00ff87;
}
.terminal .xterm-color-49 {
color: #00ffaf;
}
.terminal .xterm-bg-color-49 {
background-color: #00ffaf;
}
.terminal .xterm-color-50 {
color: #00ffd7;
}
.terminal .xterm-bg-color-50 {
background-color: #00ffd7;
}
.terminal .xterm-color-51 {
color: #00ffff;
}
.terminal .xterm-bg-color-51 {
background-color: #00ffff;
}
.terminal .xterm-color-52 {
color: #5f0000;
}
.terminal .xterm-bg-color-52 {
background-color: #5f0000;
}
.terminal .xterm-color-53 {
color: #5f005f;
}
.terminal .xterm-bg-color-53 {
background-color: #5f005f;
}
.terminal .xterm-color-54 {
color: #5f0087;
}
.terminal .xterm-bg-color-54 {
background-color: #5f0087;
}
.terminal .xterm-color-55 {
color: #5f00af;
}
.terminal .xterm-bg-color-55 {
background-color: #5f00af;
}
.terminal .xterm-color-56 {
color: #5f00d7;
}
.terminal .xterm-bg-color-56 {
background-color: #5f00d7;
}
.terminal .xterm-color-57 {
color: #5f00ff;
}
.terminal .xterm-bg-color-57 {
background-color: #5f00ff;
}
.terminal .xterm-color-58 {
color: #5f5f00;
}
.terminal .xterm-bg-color-58 {
background-color: #5f5f00;
}
.terminal .xterm-color-59 {
color: #5f5f5f;
}
.terminal .xterm-bg-color-59 {
background-color: #5f5f5f;
}
.terminal .xterm-color-60 {
color: #5f5f87;
}
.terminal .xterm-bg-color-60 {
background-color: #5f5f87;
}
.terminal .xterm-color-61 {
color: #5f5faf;
}
.terminal .xterm-bg-color-61 {
background-color: #5f5faf;
}
.terminal .xterm-color-62 {
color: #5f5fd7;
}
.terminal .xterm-bg-color-62 {
background-color: #5f5fd7;
}
.terminal .xterm-color-63 {
color: #5f5fff;
}
.terminal .xterm-bg-color-63 {
background-color: #5f5fff;
}
.terminal .xterm-color-64 {
color: #5f8700;
}
.terminal .xterm-bg-color-64 {
background-color: #5f8700;
}
.terminal .xterm-color-65 {
color: #5f875f;
}
.terminal .xterm-bg-color-65 {
background-color: #5f875f;
}
.terminal .xterm-color-66 {
color: #5f8787;
}
.terminal .xterm-bg-color-66 {
background-color: #5f8787;
}
.terminal .xterm-color-67 {
color: #5f87af;
}
.terminal .xterm-bg-color-67 {
background-color: #5f87af;
}
.terminal .xterm-color-68 {
color: #5f87d7;
}
.terminal .xterm-bg-color-68 {
background-color: #5f87d7;
}
.terminal .xterm-color-69 {
color: #5f87ff;
}
.terminal .xterm-bg-color-69 {
background-color: #5f87ff;
}
.terminal .xterm-color-70 {
color: #5faf00;
}
.terminal .xterm-bg-color-70 {
background-color: #5faf00;
}
.terminal .xterm-color-71 {
color: #5faf5f;
}
.terminal .xterm-bg-color-71 {
background-color: #5faf5f;
}
.terminal .xterm-color-72 {
color: #5faf87;
}
.terminal .xterm-bg-color-72 {
background-color: #5faf87;
}
.terminal .xterm-color-73 {
color: #5fafaf;
}
.terminal .xterm-bg-color-73 {
background-color: #5fafaf;
}
.terminal .xterm-color-74 {
color: #5fafd7;
}
.terminal .xterm-bg-color-74 {
background-color: #5fafd7;
}
.terminal .xterm-color-75 {
color: #5fafff;
}
.terminal .xterm-bg-color-75 {
background-color: #5fafff;
}
.terminal .xterm-color-76 {
color: #5fd700;
}
.terminal .xterm-bg-color-76 {
background-color: #5fd700;
}
.terminal .xterm-color-77 {
color: #5fd75f;
}
.terminal .xterm-bg-color-77 {
background-color: #5fd75f;
}
.terminal .xterm-color-78 {
color: #5fd787;
}
.terminal .xterm-bg-color-78 {
background-color: #5fd787;
}
.terminal .xterm-color-79 {
color: #5fd7af;
}
.terminal .xterm-bg-color-79 {
background-color: #5fd7af;
}
.terminal .xterm-color-80 {
color: #5fd7d7;
}
.terminal .xterm-bg-color-80 {
background-color: #5fd7d7;
}
.terminal .xterm-color-81 {
color: #5fd7ff;
}
.terminal .xterm-bg-color-81 {
background-color: #5fd7ff;
}
.terminal .xterm-color-82 {
color: #5fff00;
}
.terminal .xterm-bg-color-82 {
background-color: #5fff00;
}
.terminal .xterm-color-83 {
color: #5fff5f;
}
.terminal .xterm-bg-color-83 {
background-color: #5fff5f;
}
.terminal .xterm-color-84 {
color: #5fff87;
}
.terminal .xterm-bg-color-84 {
background-color: #5fff87;
}
.terminal .xterm-color-85 {
color: #5fffaf;
}
.terminal .xterm-bg-color-85 {
background-color: #5fffaf;
}
.terminal .xterm-color-86 {
color: #5fffd7;
}
.terminal .xterm-bg-color-86 {
background-color: #5fffd7;
}
.terminal .xterm-color-87 {
color: #5fffff;
}
.terminal .xterm-bg-color-87 {
background-color: #5fffff;
}
.terminal .xterm-color-88 {
color: #870000;
}
.terminal .xterm-bg-color-88 {
background-color: #870000;
}
.terminal .xterm-color-89 {
color: #87005f;
}
.terminal .xterm-bg-color-89 {
background-color: #87005f;
}
.terminal .xterm-color-90 {
color: #870087;
}
.terminal .xterm-bg-color-90 {
background-color: #870087;
}
.terminal .xterm-color-91 {
color: #8700af;
}
.terminal .xterm-bg-color-91 {
background-color: #8700af;
}
.terminal .xterm-color-92 {
color: #8700d7;
}
.terminal .xterm-bg-color-92 {
background-color: #8700d7;
}
.terminal .xterm-color-93 {
color: #8700ff;
}
.terminal .xterm-bg-color-93 {
background-color: #8700ff;
}
.terminal .xterm-color-94 {
color: #875f00;
}
.terminal .xterm-bg-color-94 {
background-color: #875f00;
}
.terminal .xterm-color-95 {
color: #875f5f;
}
.terminal .xterm-bg-color-95 {
background-color: #875f5f;
}
.terminal .xterm-color-96 {
color: #875f87;
}
.terminal .xterm-bg-color-96 {
background-color: #875f87;
}
.terminal .xterm-color-97 {
color: #875faf;
}
.terminal .xterm-bg-color-97 {
background-color: #875faf;
}
.terminal .xterm-color-98 {
color: #875fd7;
}
.terminal .xterm-bg-color-98 {
background-color: #875fd7;
}
.terminal .xterm-color-99 {
color: #875fff;
}
.terminal .xterm-bg-color-99 {
background-color: #875fff;
}
.terminal .xterm-color-100 {
color: #878700;
}
.terminal .xterm-bg-color-100 {
background-color: #878700;
}
.terminal .xterm-color-101 {
color: #87875f;
}
.terminal .xterm-bg-color-101 {
background-color: #87875f;
}
.terminal .xterm-color-102 {
color: #878787;
}
.terminal .xterm-bg-color-102 {
background-color: #878787;
}
.terminal .xterm-color-103 {
color: #8787af;
}
.terminal .xterm-bg-color-103 {
background-color: #8787af;
}
.terminal .xterm-color-104 {
color: #8787d7;
}
.terminal .xterm-bg-color-104 {
background-color: #8787d7;
}
.terminal .xterm-color-105 {
color: #8787ff;
}
.terminal .xterm-bg-color-105 {
background-color: #8787ff;
}
.terminal .xterm-color-106 {
color: #87af00;
}
.terminal .xterm-bg-color-106 {
background-color: #87af00;
}
.terminal .xterm-color-107 {
color: #87af5f;
}
.terminal .xterm-bg-color-107 {
background-color: #87af5f;
}
.terminal .xterm-color-108 {
color: #87af87;
}
.terminal .xterm-bg-color-108 {
background-color: #87af87;
}
.terminal .xterm-color-109 {
color: #87afaf;
}
.terminal .xterm-bg-color-109 {
background-color: #87afaf;
}
.terminal .xterm-color-110 {
color: #87afd7;
}
.terminal .xterm-bg-color-110 {
background-color: #87afd7;
}
.terminal .xterm-color-111 {
color: #87afff;
}
.terminal .xterm-bg-color-111 {
background-color: #87afff;
}
.terminal .xterm-color-112 {
color: #87d700;
}
.terminal .xterm-bg-color-112 {
background-color: #87d700;
}
.terminal .xterm-color-113 {
color: #87d75f;
}
.terminal .xterm-bg-color-113 {
background-color: #87d75f;
}
.terminal .xterm-color-114 {
color: #87d787;
}
.terminal .xterm-bg-color-114 {
background-color: #87d787;
}
.terminal .xterm-color-115 {
color: #87d7af;
}
.terminal .xterm-bg-color-115 {
background-color: #87d7af;
}
.terminal .xterm-color-116 {
color: #87d7d7;
}
.terminal .xterm-bg-color-116 {
background-color: #87d7d7;
}
.terminal .xterm-color-117 {
color: #87d7ff;
}
.terminal .xterm-bg-color-117 {
background-color: #87d7ff;
}
.terminal .xterm-color-118 {
color: #87ff00;
}
.terminal .xterm-bg-color-118 {
background-color: #87ff00;
}
.terminal .xterm-color-119 {
color: #87ff5f;
}
.terminal .xterm-bg-color-119 {
background-color: #87ff5f;
}
.terminal .xterm-color-120 {
color: #87ff87;
}
.terminal .xterm-bg-color-120 {
background-color: #87ff87;
}
.terminal .xterm-color-121 {
color: #87ffaf;
}
.terminal .xterm-bg-color-121 {
background-color: #87ffaf;
}
.terminal .xterm-color-122 {
color: #87ffd7;
}
.terminal .xterm-bg-color-122 {
background-color: #87ffd7;
}
.terminal .xterm-color-123 {
color: #87ffff;
}
.terminal .xterm-bg-color-123 {
background-color: #87ffff;
}
.terminal .xterm-color-124 {
color: #af0000;
}
.terminal .xterm-bg-color-124 {
background-color: #af0000;
}
.terminal .xterm-color-125 {
color: #af005f;
}
.terminal .xterm-bg-color-125 {
background-color: #af005f;
}
.terminal .xterm-color-126 {
color: #af0087;
}
.terminal .xterm-bg-color-126 {
background-color: #af0087;
}
.terminal .xterm-color-127 {
color: #af00af;
}
.terminal .xterm-bg-color-127 {
background-color: #af00af;
}
.terminal .xterm-color-128 {
color: #af00d7;
}
.terminal .xterm-bg-color-128 {
background-color: #af00d7;
}
.terminal .xterm-color-129 {
color: #af00ff;
}
.terminal .xterm-bg-color-129 {
background-color: #af00ff;
}
.terminal .xterm-color-130 {
color: #af5f00;
}
.terminal .xterm-bg-color-130 {
background-color: #af5f00;
}
.terminal .xterm-color-131 {
color: #af5f5f;
}
.terminal .xterm-bg-color-131 {
background-color: #af5f5f;
}
.terminal .xterm-color-132 {
color: #af5f87;
}
.terminal .xterm-bg-color-132 {
background-color: #af5f87;
}
.terminal .xterm-color-133 {
color: #af5faf;
}
.terminal .xterm-bg-color-133 {
background-color: #af5faf;
}
.terminal .xterm-color-134 {
color: #af5fd7;
}
.terminal .xterm-bg-color-134 {
background-color: #af5fd7;
}
.terminal .xterm-color-135 {
color: #af5fff;
}
.terminal .xterm-bg-color-135 {
background-color: #af5fff;
}
.terminal .xterm-color-136 {
color: #af8700;
}
.terminal .xterm-bg-color-136 {
background-color: #af8700;
}
.terminal .xterm-color-137 {
color: #af875f;
}
.terminal .xterm-bg-color-137 {
background-color: #af875f;
}
.terminal .xterm-color-138 {
color: #af8787;
}
.terminal .xterm-bg-color-138 {
background-color: #af8787;
}
.terminal .xterm-color-139 {
color: #af87af;
}
.terminal .xterm-bg-color-139 {
background-color: #af87af;
}
.terminal .xterm-color-140 {
color: #af87d7;
}
.terminal .xterm-bg-color-140 {
background-color: #af87d7;
}
.terminal .xterm-color-141 {
color: #af87ff;
}
.terminal .xterm-bg-color-141 {
background-color: #af87ff;
}
.terminal .xterm-color-142 {
color: #afaf00;
}
.terminal .xterm-bg-color-142 {
background-color: #afaf00;
}
.terminal .xterm-color-143 {
color: #afaf5f;
}
.terminal .xterm-bg-color-143 {
background-color: #afaf5f;
}
.terminal .xterm-color-144 {
color: #afaf87;
}
.terminal .xterm-bg-color-144 {
background-color: #afaf87;
}
.terminal .xterm-color-145 {
color: #afafaf;
}
.terminal .xterm-bg-color-145 {
background-color: #afafaf;
}
.terminal .xterm-color-146 {
color: #afafd7;
}
.terminal .xterm-bg-color-146 {
background-color: #afafd7;
}
.terminal .xterm-color-147 {
color: #afafff;
}
.terminal .xterm-bg-color-147 {
background-color: #afafff;
}
.terminal .xterm-color-148 {
color: #afd700;
}
.terminal .xterm-bg-color-148 {
background-color: #afd700;
}
.terminal .xterm-color-149 {
color: #afd75f;
}
.terminal .xterm-bg-color-149 {
background-color: #afd75f;
}
.terminal .xterm-color-150 {
color: #afd787;
}
.terminal .xterm-bg-color-150 {
background-color: #afd787;
}
.terminal .xterm-color-151 {
color: #afd7af;
}
.terminal .xterm-bg-color-151 {
background-color: #afd7af;
}
.terminal .xterm-color-152 {
color: #afd7d7;
}
.terminal .xterm-bg-color-152 {
background-color: #afd7d7;
}
.terminal .xterm-color-153 {
color: #afd7ff;
}
.terminal .xterm-bg-color-153 {
background-color: #afd7ff;
}
.terminal .xterm-color-154 {
color: #afff00;
}
.terminal .xterm-bg-color-154 {
background-color: #afff00;
}
.terminal .xterm-color-155 {
color: #afff5f;
}
.terminal .xterm-bg-color-155 {
background-color: #afff5f;
}
.terminal .xterm-color-156 {
color: #afff87;
}
.terminal .xterm-bg-color-156 {
background-color: #afff87;
}
.terminal .xterm-color-157 {
color: #afffaf;
}
.terminal .xterm-bg-color-157 {
background-color: #afffaf;
}
.terminal .xterm-color-158 {
color: #afffd7;
}
.terminal .xterm-bg-color-158 {
background-color: #afffd7;
}
.terminal .xterm-color-159 {
color: #afffff;
}
.terminal .xterm-bg-color-159 {
background-color: #afffff;
}
.terminal .xterm-color-160 {
color: #d70000;
}
.terminal .xterm-bg-color-160 {
background-color: #d70000;
}
.terminal .xterm-color-161 {
color: #d7005f;
}
.terminal .xterm-bg-color-161 {
background-color: #d7005f;
}
.terminal .xterm-color-162 {
color: #d70087;
}
.terminal .xterm-bg-color-162 {
background-color: #d70087;
}
.terminal .xterm-color-163 {
color: #d700af;
}
.terminal .xterm-bg-color-163 {
background-color: #d700af;
}
.terminal .xterm-color-164 {
color: #d700d7;
}
.terminal .xterm-bg-color-164 {
background-color: #d700d7;
}
.terminal .xterm-color-165 {
color: #d700ff;
}
.terminal .xterm-bg-color-165 {
background-color: #d700ff;
}
.terminal .xterm-color-166 {
color: #d75f00;
}
.terminal .xterm-bg-color-166 {
background-color: #d75f00;
}
.terminal .xterm-color-167 {
color: #d75f5f;
}
.terminal .xterm-bg-color-167 {
background-color: #d75f5f;
}
.terminal .xterm-color-168 {
color: #d75f87;
}
.terminal .xterm-bg-color-168 {
background-color: #d75f87;
}
.terminal .xterm-color-169 {
color: #d75faf;
}
.terminal .xterm-bg-color-169 {
background-color: #d75faf;
}
.terminal .xterm-color-170 {
color: #d75fd7;
}
.terminal .xterm-bg-color-170 {
background-color: #d75fd7;
}
.terminal .xterm-color-171 {
color: #d75fff;
}
.terminal .xterm-bg-color-171 {
background-color: #d75fff;
}
.terminal .xterm-color-172 {
color: #d78700;
}
.terminal .xterm-bg-color-172 {
background-color: #d78700;
}
.terminal .xterm-color-173 {
color: #d7875f;
}
.terminal .xterm-bg-color-173 {
background-color: #d7875f;
}
.terminal .xterm-color-174 {
color: #d78787;
}
.terminal .xterm-bg-color-174 {
background-color: #d78787;
}
.terminal .xterm-color-175 {
color: #d787af;
}
.terminal .xterm-bg-color-175 {
background-color: #d787af;
}
.terminal .xterm-color-176 {
color: #d787d7;
}
.terminal .xterm-bg-color-176 {
background-color: #d787d7;
}
.terminal .xterm-color-177 {
color: #d787ff;
}
.terminal .xterm-bg-color-177 {
background-color: #d787ff;
}
.terminal .xterm-color-178 {
color: #d7af00;
}
.terminal .xterm-bg-color-178 {
background-color: #d7af00;
}
.terminal .xterm-color-179 {
color: #d7af5f;
}
.terminal .xterm-bg-color-179 {
background-color: #d7af5f;
}
.terminal .xterm-color-180 {
color: #d7af87;
}
.terminal .xterm-bg-color-180 {
background-color: #d7af87;
}
.terminal .xterm-color-181 {
color: #d7afaf;
}
.terminal .xterm-bg-color-181 {
background-color: #d7afaf;
}
.terminal .xterm-color-182 {
color: #d7afd7;
}
.terminal .xterm-bg-color-182 {
background-color: #d7afd7;
}
.terminal .xterm-color-183 {
color: #d7afff;
}
.terminal .xterm-bg-color-183 {
background-color: #d7afff;
}
.terminal .xterm-color-184 {
color: #d7d700;
}
.terminal .xterm-bg-color-184 {
background-color: #d7d700;
}
.terminal .xterm-color-185 {
color: #d7d75f;
}
.terminal .xterm-bg-color-185 {
background-color: #d7d75f;
}
.terminal .xterm-color-186 {
color: #d7d787;
}
.terminal .xterm-bg-color-186 {
background-color: #d7d787;
}
.terminal .xterm-color-187 {
color: #d7d7af;
}
.terminal .xterm-bg-color-187 {
background-color: #d7d7af;
}
.terminal .xterm-color-188 {
color: #d7d7d7;
}
.terminal .xterm-bg-color-188 {
background-color: #d7d7d7;
}
.terminal .xterm-color-189 {
color: #d7d7ff;
}
.terminal .xterm-bg-color-189 {
background-color: #d7d7ff;
}
.terminal .xterm-color-190 {
color: #d7ff00;
}
.terminal .xterm-bg-color-190 {
background-color: #d7ff00;
}
.terminal .xterm-color-191 {
color: #d7ff5f;
}
.terminal .xterm-bg-color-191 {
background-color: #d7ff5f;
}
.terminal .xterm-color-192 {
color: #d7ff87;
}
.terminal .xterm-bg-color-192 {
background-color: #d7ff87;
}
.terminal .xterm-color-193 {
color: #d7ffaf;
}
.terminal .xterm-bg-color-193 {
background-color: #d7ffaf;
}
.terminal .xterm-color-194 {
color: #d7ffd7;
}
.terminal .xterm-bg-color-194 {
background-color: #d7ffd7;
}
.terminal .xterm-color-195 {
color: #d7ffff;
}
.terminal .xterm-bg-color-195 {
background-color: #d7ffff;
}
.terminal .xterm-color-196 {
color: #ff0000;
}
.terminal .xterm-bg-color-196 {
background-color: #ff0000;
}
.terminal .xterm-color-197 {
color: #ff005f;
}
.terminal .xterm-bg-color-197 {
background-color: #ff005f;
}
.terminal .xterm-color-198 {
color: #ff0087;
}
.terminal .xterm-bg-color-198 {
background-color: #ff0087;
}
.terminal .xterm-color-199 {
color: #ff00af;
}
.terminal .xterm-bg-color-199 {
background-color: #ff00af;
}
.terminal .xterm-color-200 {
color: #ff00d7;
}
.terminal .xterm-bg-color-200 {
background-color: #ff00d7;
}
.terminal .xterm-color-201 {
color: #ff00ff;
}
.terminal .xterm-bg-color-201 {
background-color: #ff00ff;
}
.terminal .xterm-color-202 {
color: #ff5f00;
}
.terminal .xterm-bg-color-202 {
background-color: #ff5f00;
}
.terminal .xterm-color-203 {
color: #ff5f5f;
}
.terminal .xterm-bg-color-203 {
background-color: #ff5f5f;
}
.terminal .xterm-color-204 {
color: #ff5f87;
}
.terminal .xterm-bg-color-204 {
background-color: #ff5f87;
}
.terminal .xterm-color-205 {
color: #ff5faf;
}
.terminal .xterm-bg-color-205 {
background-color: #ff5faf;
}
.terminal .xterm-color-206 {
color: #ff5fd7;
}
.terminal .xterm-bg-color-206 {
background-color: #ff5fd7;
}
.terminal .xterm-color-207 {
color: #ff5fff;
}
.terminal .xterm-bg-color-207 {
background-color: #ff5fff;
}
.terminal .xterm-color-208 {
color: #ff8700;
}
.terminal .xterm-bg-color-208 {
background-color: #ff8700;
}
.terminal .xterm-color-209 {
color: #ff875f;
}
.terminal .xterm-bg-color-209 {
background-color: #ff875f;
}
.terminal .xterm-color-210 {
color: #ff8787;
}
.terminal .xterm-bg-color-210 {
background-color: #ff8787;
}
.terminal .xterm-color-211 {
color: #ff87af;
}
.terminal .xterm-bg-color-211 {
background-color: #ff87af;
}
.terminal .xterm-color-212 {
color: #ff87d7;
}
.terminal .xterm-bg-color-212 {
background-color: #ff87d7;
}
.terminal .xterm-color-213 {
color: #ff87ff;
}
.terminal .xterm-bg-color-213 {
background-color: #ff87ff;
}
.terminal .xterm-color-214 {
color: #ffaf00;
}
.terminal .xterm-bg-color-214 {
background-color: #ffaf00;
}
.terminal .xterm-color-215 {
color: #ffaf5f;
}
.terminal .xterm-bg-color-215 {
background-color: #ffaf5f;
}
.terminal .xterm-color-216 {
color: #ffaf87;
}
.terminal .xterm-bg-color-216 {
background-color: #ffaf87;
}
.terminal .xterm-color-217 {
color: #ffafaf;
}
.terminal .xterm-bg-color-217 {
background-color: #ffafaf;
}
.terminal .xterm-color-218 {
color: #ffafd7;
}
.terminal .xterm-bg-color-218 {
background-color: #ffafd7;
}
.terminal .xterm-color-219 {
color: #ffafff;
}
.terminal .xterm-bg-color-219 {
background-color: #ffafff;
}
.terminal .xterm-color-220 {
color: #ffd700;
}
.terminal .xterm-bg-color-220 {
background-color: #ffd700;
}
.terminal .xterm-color-221 {
color: #ffd75f;
}
.terminal .xterm-bg-color-221 {
background-color: #ffd75f;
}
.terminal .xterm-color-222 {
color: #ffd787;
}
.terminal .xterm-bg-color-222 {
background-color: #ffd787;
}
.terminal .xterm-color-223 {
color: #ffd7af;
}
.terminal .xterm-bg-color-223 {
background-color: #ffd7af;
}
.terminal .xterm-color-224 {
color: #ffd7d7;
}
.terminal .xterm-bg-color-224 {
background-color: #ffd7d7;
}
.terminal .xterm-color-225 {
color: #ffd7ff;
}
.terminal .xterm-bg-color-225 {
background-color: #ffd7ff;
}
.terminal .xterm-color-226 {
color: #ffff00;
}
.terminal .xterm-bg-color-226 {
background-color: #ffff00;
}
.terminal .xterm-color-227 {
color: #ffff5f;
}
.terminal .xterm-bg-color-227 {
background-color: #ffff5f;
}
.terminal .xterm-color-228 {
color: #ffff87;
}
.terminal .xterm-bg-color-228 {
background-color: #ffff87;
}
.terminal .xterm-color-229 {
color: #ffffaf;
}
.terminal .xterm-bg-color-229 {
background-color: #ffffaf;
}
.terminal .xterm-color-230 {
color: #ffffd7;
}
.terminal .xterm-bg-color-230 {
background-color: #ffffd7;
}
.terminal .xterm-color-231 {
color: #ffffff;
}
.terminal .xterm-bg-color-231 {
background-color: #ffffff;
}
.terminal .xterm-color-232 {
color: #080808;
}
.terminal .xterm-bg-color-232 {
background-color: #080808;
}
.terminal .xterm-color-233 {
color: #121212;
}
.terminal .xterm-bg-color-233 {
background-color: #121212;
}
.terminal .xterm-color-234 {
color: #1c1c1c;
}
.terminal .xterm-bg-color-234 {
background-color: #1c1c1c;
}
.terminal .xterm-color-235 {
color: #262626;
}
.terminal .xterm-bg-color-235 {
background-color: #262626;
}
.terminal .xterm-color-236 {
color: #303030;
}
.terminal .xterm-bg-color-236 {
background-color: #303030;
}
.terminal .xterm-color-237 {
color: #3a3a3a;
}
.terminal .xterm-bg-color-237 {
background-color: #3a3a3a;
}
.terminal .xterm-color-238 {
color: #444444;
}
.terminal .xterm-bg-color-238 {
background-color: #444444;
}
.terminal .xterm-color-239 {
color: #4e4e4e;
}
.terminal .xterm-bg-color-239 {
background-color: #4e4e4e;
}
.terminal .xterm-color-240 {
color: #585858;
}
.terminal .xterm-bg-color-240 {
background-color: #585858;
}
.terminal .xterm-color-241 {
color: #626262;
}
.terminal .xterm-bg-color-241 {
background-color: #626262;
}
.terminal .xterm-color-242 {
color: #6c6c6c;
}
.terminal .xterm-bg-color-242 {
background-color: #6c6c6c;
}
.terminal .xterm-color-243 {
color: #767676;
}
.terminal .xterm-bg-color-243 {
background-color: #767676;
}
.terminal .xterm-color-244 {
color: #808080;
}
.terminal .xterm-bg-color-244 {
background-color: #808080;
}
.terminal .xterm-color-245 {
color: #8a8a8a;
}
.terminal .xterm-bg-color-245 {
background-color: #8a8a8a;
}
.terminal .xterm-color-246 {
color: #949494;
}
.terminal .xterm-bg-color-246 {
background-color: #949494;
}
.terminal .xterm-color-247 {
color: #9e9e9e;
}
.terminal .xterm-bg-color-247 {
background-color: #9e9e9e;
}
.terminal .xterm-color-248 {
color: #a8a8a8;
}
.terminal .xterm-bg-color-248 {
background-color: #a8a8a8;
}
.terminal .xterm-color-249 {
color: #b2b2b2;
}
.terminal .xterm-bg-color-249 {
background-color: #b2b2b2;
}
.terminal .xterm-color-250 {
color: #bcbcbc;
}
.terminal .xterm-bg-color-250 {
background-color: #bcbcbc;
}
.terminal .xterm-color-251 {
color: #c6c6c6;
}
.terminal .xterm-bg-color-251 {
background-color: #c6c6c6;
}
.terminal .xterm-color-252 {
color: #d0d0d0;
}
.terminal .xterm-bg-color-252 {
background-color: #d0d0d0;
}
.terminal .xterm-color-253 {
color: #dadada;
}
.terminal .xterm-bg-color-253 {
background-color: #dadada;
}
.terminal .xterm-color-254 {
color: #e4e4e4;
}
.terminal .xterm-bg-color-254 {
background-color: #e4e4e4;
}
.terminal .xterm-color-255 {
color: #eeeeee;
}
.terminal .xterm-bg-color-255 {
background-color: #eeeeee;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<div class="footer fixed">
<div class="pull-right">
Version <strong>1.2.1-{% include '_build.html' %}</strong> GPLv2.
Version <strong>1.3.1-{% include '_build.html' %}</strong> GPLv2.
<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">
</div>
<div>
......
......@@ -9,6 +9,7 @@ from django.core.cache import cache
from django.shortcuts import get_object_or_404, redirect
from django.utils import timezone
from django.core.files.storage import default_storage
from django.http.response import HttpResponseRedirectBase
from django.http import HttpResponseNotFound
from django.conf import settings
......@@ -25,7 +26,7 @@ from .serializers import TerminalSerializer, StatusSerializer, \
SessionSerializer, TaskSerializer, ReplaySerializer
from .hands import IsSuperUserOrAppUser, IsAppUser, \
IsSuperUserOrAppUserOrUserReadonly
from .backends import get_command_store, get_multi_command_store, \
from .backends import get_command_storage, get_multi_command_storage, \
SessionCommandSerializer
logger = logging.getLogger(__file__)
......@@ -108,6 +109,8 @@ class StatusViewSet(viewsets.ModelViewSet):
task_serializer_class = TaskSerializer
def create(self, request, *args, **kwargs):
from_gua = self.request.query_params.get("from_guacamole", None)
if not from_gua:
self.handle_sessions()
super().create(request, *args, **kwargs)
tasks = self.request.user.terminal.task_set.filter(is_finished=False)
......@@ -224,8 +227,8 @@ class CommandViewSet(viewsets.ViewSet):
}
"""
command_store = get_command_store()
multi_command_storage = get_multi_command_store()
command_store = get_command_storage()
multi_command_storage = get_multi_command_storage()
serializer_class = SessionCommandSerializer
permission_classes = (IsSuperUserOrAppUser,)
......@@ -288,74 +291,37 @@ class SessionReplayViewSet(viewsets.ViewSet):
url = default_storage.url(path)
return redirect(url)
else:
configs = settings.TERMINAL_REPLAY_STORAGE.items()
configs = settings.TERMINAL_REPLAY_STORAGE
configs = [cfg for cfg in configs if cfg['TYPE'] != 'server']
if not configs:
return HttpResponseNotFound()
for name, config in configs:
client = jms_storage.init(config)
date = self.session.date_start.strftime('%Y-%m-%d')
file_path = os.path.join(date, str(self.session.id) + '.replay.gz')
target_path = default_storage.base_location + '/' + path
folder_path = default_storage.base_location + date
if not default_storage.exists(folder_path):
os.mkdir(folder_path)
if client and client.has_file(file_path) and \
client.download_file(file_path, target_path):
storage = jms_storage.get_multi_object_storage(configs)
ok, err = storage.download(file_path, target_path)
if ok:
return redirect(default_storage.url(path))
else:
logger.error("Failed download replay file: {}".format(err))
return HttpResponseNotFound()
class SessionReplayV2ViewSet(viewsets.ViewSet):
class SessionReplayV2ViewSet(SessionReplayViewSet):
serializer_class = ReplaySerializer
permission_classes = (IsSuperUserOrAppUser,)
session = None
def gen_session_path(self):
date = self.session.date_start.strftime('%Y-%m-%d')
replay = {
"id": self.session.id,
# "width": 100,
# "heith": 100
}
if self.session.protocol == "ssh":
replay['type'] = "json"
replay['path'] = os.path.join(date, str(self.session.id) + '.gz')
return replay
elif self.session.protocol == "rdp":
replay['type'] = "mp4"
replay['path'] = os.path.join(date, str(self.session.id) + '.mp4')
return replay
else:
return replay
def retrieve(self, request, *args, **kwargs):
session_id = kwargs.get('pk')
self.session = get_object_or_404(Session, id=session_id)
replay = self.gen_session_path()
if replay.get("path", "") == "":
return HttpResponseNotFound()
if default_storage.exists(replay["path"]):
replay["src"] = default_storage.url(replay["path"])
return Response(replay)
else:
configs = settings.TERMINAL_REPLAY_STORAGE.items()
if not configs:
return HttpResponseNotFound()
for name, config in configs:
client = jms_storage.init(config)
target_path = default_storage.base_location + '/' + replay["path"]
if client and client.has_file(replay["path"]) and \
client.download_file(replay["path"], target_path):
replay["src"] = default_storage.url(replay["path"])
return Response(replay)
response = super().retrieve(request, *args, **kwargs)
data = {
'type': 'guacamole' if self.session.protocol == 'rdp' else 'json',
'src': '',
}
if isinstance(response, HttpResponseRedirectBase):
data['src'] = response.url
return Response(data)
return HttpResponseNotFound()
......
......@@ -7,19 +7,19 @@ TYPE_ENGINE_MAPPING = {
}
def get_command_store():
params = settings.COMMAND_STORAGE
engine_class = import_module(params['ENGINE'])
storage = engine_class.CommandStore(params)
def get_command_storage():
config = settings.COMMAND_STORAGE
engine_class = import_module(config['ENGINE'])
storage = engine_class.CommandStore(config)
return storage
def get_terminal_command_store():
def get_terminal_command_storages():
storage_list = {}
for name, params in settings.TERMINAL_COMMAND_STORAGE.items():
tp = params['TYPE']
if tp == 'server':
storage = get_command_store()
storage = get_command_storage()
else:
if not TYPE_ENGINE_MAPPING.get(tp):
continue
......@@ -29,9 +29,9 @@ def get_terminal_command_store():
return storage_list
def get_multi_command_store():
def get_multi_command_storage():
from .command.multi import CommandStore
storage_list = get_terminal_command_store().values()
storage_list = get_terminal_command_storages().values()
storage = CommandStore(storage_list)
return storage
......
# -*- coding: utf-8 -*-
#
from jms_es_sdk import ESStore
from jms_storage.es import ESStorage
from .base import CommandBase
from .models import AbstractSessionCommand
class CommandStore(CommandBase, ESStore):
class CommandStore(ESStorage, CommandBase):
def __init__(self, params):
hosts = params.get('HOSTS', ['http://localhost'])
ESStore.__init__(self, hosts=hosts)
def save(self, command):
return ESStore.save(self, command)
def bulk_save(self, commands):
return ESStore.bulk_save(self, commands)
super().__init__(params)
def filter(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
data = ESStore.filter(
self, date_from=date_from, date_to=date_to,
data = super().filter(date_from=date_from, date_to=date_to,
user=user, asset=asset, system_user=system_user,
input=input, session=session
)
input=input, session=session)
return AbstractSessionCommand.from_multi_dict(
[item["_source"] for item in data["hits"] if item]
)
def count(self, date_from=None, date_to=None,
user=None, asset=None, system_user=None,
input=None, session=None):
amount = ESStore.count(
self, date_from=date_from, date_to=date_to,
user=user, asset=asset, system_user=system_user,
input=input, session=session
)
return amount
......@@ -9,7 +9,7 @@ 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_store
from .backends import get_multi_command_storage
class TerminalSerializer(serializers.ModelSerializer):
......@@ -47,7 +47,7 @@ class TerminalSerializer(serializers.ModelSerializer):
class SessionSerializer(serializers.ModelSerializer):
command_amount = serializers.SerializerMethodField()
command_store = get_multi_command_store()
command_store = get_multi_command_storage()
class Meta:
model = Session
......
......@@ -88,7 +88,7 @@
<td>{% trans 'Replay session' %}:</td>
<td>
<span class="pull-right">
<button type="button" onclick="window.open('/luna/replay/{{ object.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Go' %}</button>
<button type="button" onclick="window.open('/luna/replay/{{ object.id }}','luna', 'height=600, width=800, top=400, left=400, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Go' %}</button>
</span>
</td>
</tr>
......
......@@ -99,11 +99,15 @@
<td class="text-center">{{ session.date_start|time_util_with_seconds:session.date_end }}</td>
<td>
{% if session.is_finished %}
<a onclick="window.open('/luna/replay/{{ 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-replay" >{% trans "Replay" %}</a>
<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>
{% 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 == 'rdp' %}
<a class="btn btn-xs btn-danger btn-term" disabled value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a>
{% else %}
<a class="btn btn-xs btn-danger btn-term" value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a>
{% endif %}
{% endif %}
</td>
</tr>
{% endfor %}
......
# ~*~ coding: utf-8 ~*~
from django import template
from ..backends import get_multi_command_store
from ..backends import get_multi_command_storage
register = template.Library()
command_store = get_multi_command_store()
command_store = get_multi_command_storage()
@register.filter
......
......@@ -9,10 +9,10 @@ from django.utils.translation import ugettext as _
from common.mixins import DatetimeSearchMixin, AdminUserRequiredMixin
from ..models import Command
from .. import utils
from ..backends import get_multi_command_store
from ..backends import get_multi_command_storage
__all__ = ['CommandListView']
common_storage = get_multi_command_store()
common_storage = get_multi_command_storage()
class CommandListView(DatetimeSearchMixin, AdminUserRequiredMixin, ListView):
......
......@@ -10,7 +10,7 @@ from django.conf import settings
from users.utils import AdminUserRequiredMixin
from common.mixins import DatetimeSearchMixin
from ..models import Session, Command, Terminal
from ..backends import get_multi_command_store
from ..backends import get_multi_command_storage
from .. import utils
......@@ -19,7 +19,7 @@ __all__ = [
'SessionDetailView',
]
command_store = get_multi_command_store()
command_store = get_multi_command_storage()
class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
......
......@@ -11,7 +11,7 @@ __all__ = ['UserGroup']
class UserGroup(NoDeleteModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'))
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True,
verbose_name=_('Date created'))
......
......@@ -77,7 +77,7 @@ class User(AbstractUser):
is_first_login = models.BooleanField(default=True)
date_expired = models.DateTimeField(
default=date_expired_default, blank=True, null=True,
verbose_name=_('Date expired')
db_index=True, verbose_name=_('Date expired')
)
created_by = models.CharField(
max_length=30, default='', verbose_name=_('Created by')
......
......@@ -70,6 +70,7 @@
<br>
<input type="checkbox" id="acceptTerms">
<label for="acceptTerms" style="margin-top:20px">{% trans "I agree with the terms and conditions." %}</label>
<p id="noTerms" class="red-fonts" style="visibility: hidden; font-size: 10px; margin-top: 10px;">* {% trans 'Please choose the terms and conditions.' %}</p>
{% endif %}
{% bootstrap_form wizard.form %}
......@@ -99,11 +100,7 @@
{% if wizard.steps.prev %}
<li><a class="fl_goto" name="wizard_goto_step" data-goto="{{ wizard.steps.prev }}">{% trans "Previous" %}</a></li>
{% endif %}
{#{% if wizard.steps.next %}#}
{#<li><a class="fl_goto" name="wizard_goto_step" data-goto="{{ wizard.steps.next }}">{% trans "Next" %}</a></li>#}
{#{% endif %}#}
{#<li><a id="fl_submit">{% trans "Submit" %}</a></li>#}
{#将原来的下一页-替换为提交;修复 每页都提交,最后才能成功问题#}
{% if wizard.steps.next %}
<li><a id="fl_submit" >{% trans "Next" %}</a></li>
{% else %}
......@@ -124,16 +121,21 @@
{% block custom_foot_js %}
<script>
$('#id_2-otp_level div').eq(2).css('display', 'none');
$(document).on('click', ".fl_goto", function(){
var $form = $('#fl_form');
$('<input />', {'name': 'wizard_goto_step', 'value': $(this).data('goto'), 'type': 'hidden'}).appendTo($form);
$form.submit();
return false;
}).on('click', '#fl_submit', function(){
var isFinish = $('#fl_submit').html() === "{% trans 'Finish' %}";
var noChecked = !$('#acceptTerms').prop('checked');
if ( isFinish && noChecked){
$('#noTerms').css('visibility', 'visible');
}
else{
$('#fl_form').submit();
return false;
}
}).on('click', '#btn-reset-pubkey', function () {
var the_url = '{% url "users:user-pubkey-generate" %}';
window.open(the_url, "_blank")
......
......@@ -68,7 +68,7 @@ var asset_table;
function initTable() {
if (inited){
return
return asset_table
} else {
inited = true;
}
......
......@@ -64,10 +64,11 @@
var zTree;
var inited = false;
var url;
var asset_table;
function initTable() {
if (inited){
return
return asset_table
} else {
inited = true;
}
......
......@@ -41,6 +41,8 @@ def send_user_created_mail(user):
</br>
Your account has been created successfully
</br>
Username: %(username)s
</br>
<a href="%(rest_password_url)s?token=%(rest_password_token)s">click here to set your password</a>
</br>
This link is valid for 1 hour. After it expires, <a href="%(forget_password_url)s?email=%(email)s">request new one</a>
......@@ -54,6 +56,7 @@ def send_user_created_mail(user):
</br>
""") % {
'name': user.name,
'username': user.username,
'rest_password_url': reverse('users:reset-password', external=True),
'rest_password_token': user.generate_reset_token(),
'forget_password_url': reverse('users:forgot-password', external=True),
......
......@@ -278,6 +278,16 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
def get_form(self, step=None, data=None, files=None):
form = super().get_form(step, data, files)
form.instance = self.request.user
if isinstance(form, forms.UserMFAForm):
choices = form.fields["otp_level"].choices
if self.request.user.otp_force_enabled:
choices = [(k, v) for k, v in choices if k == 2]
else:
choices = [(k, v) for k, v in choices if k in [0, 1]]
form.fields["otp_level"].choices = choices
form.fields["otp_level"].initial = self.request.user.otp_level
return form
......
......@@ -3,10 +3,10 @@ ansible==2.4.2.0
asn1crypto==0.24.0
bcrypt==3.1.4
billiard==3.5.0.3
boto3==1.6.4
botocore==1.9.4
boto3==1.6.5
botocore==1.9.5
celery==4.1.0
certifi==2017.11.5
certifi==2018.1.18
cffi==1.11.2
chardet==3.0.4
configparser==3.5.0
......@@ -31,7 +31,7 @@ ecdsa==0.13
elasticsearch==6.1.1
enum-compat==0.0.2
ephem==3.7.6.0
eventlet==0.21.0
eventlet==0.22.1
ForgeryPy==0.1
greenlet==0.4.12
gunicorn==19.7.1
......@@ -40,7 +40,6 @@ itsdangerous==0.24
itypes==1.1.0
Jinja2==2.10
jmespath==0.9.3
jms-es-sdk
kombu==4.0.2
ldap3==2.4
MarkupSafe==1.0
......@@ -58,11 +57,11 @@ pyotp==2.2.6
PyNaCl==1.2.1
python-dateutil==2.6.1
python-gssapi==0.6.4
pytz==2017.3
pytz==2018.3
PyYAML==3.12
redis==2.10.6
requests==2.18.4
jms-storage==0.0.13
jms-storage==0.0.17
s3transfer==0.1.13
simplejson==3.13.2
six==1.11.0
......
#!/usr/bin/python
#
import os
import sys
from collections import Counter
import django
from django.db.models import Count
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 users.models import UserGroup
def clean_group(interactive=True):
groups = UserGroup.objects.all()
groups_name_list = groups.values_list('name', flat=True)
groups_with_info = groups.annotate(Count('users'))\
.annotate(Count('asset_permissions'))
counter = Counter(groups_name_list)
for name, count in counter.items():
if count == 0:
continue
groups_duplicate = groups_with_info.filter(name=name)
need_clean_count = groups_duplicate.count()
for group in groups_duplicate:
need_clean = True
if group.users__count > 0:
need_clean = False
elif group.asset_permissions__count > 0:
need_clean = False
elif need_clean_count == 1:
need_clean = False
if need_clean:
confirm = True
if interactive:
confirm = False
while True:
confirm = input(
"Delete user group <{}>, create at {}? ([y]/n)".format(
name, group.date_created)
)
if confirm.lower() == "y":
confirm = True
break
elif confirm.lower() == "n":
confirm = False
break
else:
print("No valid input")
continue
if confirm:
group.delete()
print("Delete success: {}".format(name))
need_clean_count -= 1
else:
continue
if __name__ == '__main__':
clean_group()
#!/bin/bash
if [ ! -d "/opt/py3" ]; then
echo -e "\033[31m python3虚拟环境不是默认路径 \033[0m"
ps -ef | grep jumpserver/tmp/beat.pid | grep -v grep
if [ $? -ne 0 ]
then
echo -e "\033[31m jumpserver未运行,请到jumpserver目录使用 ./jms start all -d 启动 \033[0m"
exit 0
else
echo -e "\033[31m 正在计算python3虚拟环境路径 \033[0m"
fi
py3pid=`ps -ef | grep jumpserver/tmp/beat.pid | grep -v grep | awk '{print $2}'`
py3file=`cat /proc/$py3pid/cmdline`
py3even=`echo ${py3file%/bin/python3*}`
echo -e "\033[31m python3虚拟环境路径为$py3even \033[0m"
source $py3even/bin/activate
if grep -q 'source ~/.autoenv/activate.sh' ~/.bashrc; then
echo -e "\033[31m 正在自动载入 python 环境 \033[0m"
else
source /opt/py3/bin/activate
echo -e "\033[31m 不支持自动升级,请参考 http://docs.jumpserver.org/zh/docs/upgrade.html 手动升级 \033[0m"
exit 0
fi
source ~/.bashrc
cd `dirname $0`/ && cd .. && ./jms stop
jumpserver_backup=/tmp/jumpserver_backup$(date -d "today" +"%Y%m%d_%H%M%S")
......@@ -29,21 +19,20 @@ echo -e "\033[31m 是否需要备份Jumpserver数据库 \033[0m"
stty erase ^H
read -p "确认备份请按Y,否则按其他键跳过备份 " a
if [ "$a" == y -o "$a" == Y ];then
echo -e "\033[31m 正在备份数据库 \033[0m"
echo -e "\033[31m 请手动输入数据库信息 \033[0m"
read -p '请输入Jumpserver数据库ip:' DB_HOST
read -p '请输入Jumpserver数据库端口:' DB_PORT
read -p '请输入Jumpserver数据库名称:' DB_NAME
read -p '请输入有权限导出数据库的用户:' DB_USER
read -p '请输入该用户的密码:' DB_PASSWORD
mysqldump -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASSWORD $DB_NAME > /$jumpserver_backup/$DB_NAME$(date -d "today" +"%Y%m%d_%H%M%S").sql || {
echo -e "\033[31m 备份数据库失败,请检查输入是否有误 \033[0m"
exit 1
}
echo -e "\033[31m 备份数据库完成 \033[0m"
echo -e "\033[31m 正在备份数据库 \033[0m"
echo -e "\033[31m 请手动输入数据库信息 \033[0m"
read -p '请输入Jumpserver数据库ip:' DB_HOST
read -p '请输入Jumpserver数据库端口:' DB_PORT
read -p '请输入Jumpserver数据库名称:' DB_NAME
read -p '请输入有权限导出数据库的用户:' DB_USER
read -p '请输入该用户的密码:' DB_PASSWORD
mysqldump -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASSWORD $DB_NAME > /$jumpserver_backup/$DB_NAME$(date -d "today" +"%Y%m%d_%H%M%S").sql || {
echo -e "\033[31m 备份数据库失败,请检查输入是否有误 \033[0m"
exit 1
}
echo -e "\033[31m 备份数据库完成 \033[0m"
else
echo -e "\033[31m 已取消备份数据库操作 \033[0m"
echo -e "\033[31m 已取消备份数据库操作 \033[0m"
fi
git pull && pip install -r requirements/requirements.txt && cd utils && sh make_migrations.sh
......
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