Commit de2416b1 authored by BaiJiangJie's avatar BaiJiangJie

Merge branch 'dev' of github.com:jumpserver/jumpserver into github_dev

parents 486793dd c529061e
......@@ -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):
"""
......
......@@ -107,6 +107,7 @@ class AssetUser(models.Model):
def clear_auth(self):
self._password = ''
self._private_key = ''
self._public_key = ''
self.save()
def auto_gen_auth(self):
......
......@@ -61,10 +61,14 @@ 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_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_family(self):
children = list(self.get_all_children())
......
......@@ -107,14 +107,14 @@
</div>
<div class="col-sm-4" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary only-ssh">
<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>#}
......@@ -239,6 +248,7 @@ $(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;
......@@ -321,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 %}
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-08 17:24+0800\n"
"POT-Creation-Date: 2018-05-17 11:32+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,22 +17,22 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: assets/api/node.py:96
#: assets/api/node.py:106
msgid "New node {}"
msgstr "新节点 {}"
#: assets/api/node.py:225
#: assets/api/node.py:242
msgid "更新节点资产硬件信息: {}"
msgstr ""
#: assets/api/node.py:238
#: assets/api/node.py:255
msgid "测试节点下资产是否可连接: {}"
msgstr ""
#: 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:33
#: assets/templates/assets/system_user_detail.html:175 perms/models.py:33
msgid "Nodes"
msgstr "节点管理"
......@@ -438,7 +438,7 @@ msgstr "默认资产组"
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 "值"
......@@ -535,7 +535,7 @@ msgstr "测试系统用户可连接性: {}"
msgid "定期测试系统用户可连接性: {}"
msgstr ""
#: assets/tasks.py:401
#: assets/tasks.py:402
msgid "推送系统用户到入资产: {}"
msgstr ""
......@@ -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
......@@ -782,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:634
#: 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
......@@ -963,19 +963,19 @@ msgstr "仅显示当前节点资产"
msgid "Displays all child node assets"
msgstr "显示所有子节点资产"
#: assets/templates/assets/asset_list.html:215
#: assets/templates/assets/asset_list.html:217
msgid "Create node failed"
msgstr "创建节点失败"
#: assets/templates/assets/asset_list.html:227
#: assets/templates/assets/asset_list.html:229
msgid "Have child node, cancel"
msgstr "存在子节点,不能删除"
#: assets/templates/assets/asset_list.html:229
#: assets/templates/assets/asset_list.html:231
msgid "Have assets, cancel"
msgstr "存在资产,不能删除"
#: assets/templates/assets/asset_list.html:629
#: 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
......@@ -984,20 +984,20 @@ msgstr "存在资产,不能删除"
msgid "Are you sure?"
msgstr "你确认吗?"
#: assets/templates/assets/asset_list.html:630
#: assets/templates/assets/asset_list.html:632
msgid "This will delete the selected assets !!!"
msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:638
#: assets/templates/assets/asset_list.html:640
msgid "Asset Deleted."
msgstr "已被删除"
#: assets/templates/assets/asset_list.html:639
#: assets/templates/assets/asset_list.html:644
#: assets/templates/assets/asset_list.html:641
#: assets/templates/assets/asset_list.html:646
msgid "Asset Delete"
msgstr "删除"
#: assets/templates/assets/asset_list.html:643
#: assets/templates/assets/asset_list.html:645
msgid "Asset Deleting failed."
msgstr "删除失败"
......@@ -1032,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"
......@@ -1080,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"
......@@ -2113,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 "终断任务已发送,请等待"
......
......@@ -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
......@@ -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
......
......@@ -13,7 +13,6 @@ logger = get_logger(__file__)
class AssetPermissionUtil:
@staticmethod
def get_user_permissions(user):
return AssetPermission.objects.all().valid().filter(users=user)
......@@ -122,6 +121,24 @@ 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)
......@@ -164,15 +181,24 @@ class AssetPermissionUtil:
:param user:
:return: {node: {asset: set(su1, su2)}}
"""
from assets.models import Node
unnode = Node(value='Unnode')
nodes = defaultdict(dict)
for _node in cls.get_user_nodes(user):
children = _node.get_family()
for node in children:
nodes[node] = defaultdict(set)
nodes[unnode] = defaultdict(set)
_assets = cls.get_user_assets(user)
for asset, _system_users in _assets.items():
_nodes = asset.get_nodes()
in_node = False
for node in _nodes:
if asset in nodes[node]:
if node in nodes:
in_node = True
nodes[node][asset].update(_system_users)
else:
nodes[node][asset] = _system_users
if not in_node:
nodes[unnode][asset].update(_system_users)
return nodes
@classmethod
......
......@@ -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__)
......@@ -227,8 +228,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,)
......@@ -291,19 +292,20 @@ 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
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()
......@@ -313,33 +315,13 @@ class SessionReplayV2ViewSet(SessionReplayViewSet):
session = None
def retrieve(self, request, *args, **kwargs):
session_id = kwargs.get('pk')
self.session = get_object_or_404(Session, id=session_id)
path = self.gen_session_path()
response = super().retrieve(request, *args, **kwargs)
data = {
'type': 'guacamole' if self.session.protocol == 'rdp' else 'json',
'src': '',
}
if default_storage.exists(path):
url = default_storage.url(path)
data['src'] = url
return Response(data)
else:
configs = settings.TERMINAL_REPLAY_STORAGE.items()
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
if client and client.has_file(file_path) and \
client.download_file(file_path, target_path):
url = default_storage.url(path)
data['src'] = url
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
......
# ~*~ 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'))
......
......@@ -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
......@@ -62,7 +61,7 @@ pytz==2017.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()
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