diff --git a/.gitignore b/.gitignore index dc51315af..488f8a776 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .env env env* +venv dist build *.egg diff --git a/README.md b/README.md index ba6766a1f..e44a23f9c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Jumpserver是全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协 Jumpserver使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。 -Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发访问限制。 +Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发限制。 改变世界,从一点点开始。 @@ -52,7 +52,7 @@ Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点 ### License & Copyright -Copyright (c) 2014-2017 Beijing Duizhan Tech, Inc., All rights reserved. +Copyright (c) 2014-2018 Beijing Duizhan Tech, Inc., All rights reserved. Licensed under The GNU General Public License version 2 (GPLv2) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index 78cd7a27a..9c002b271 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -18,10 +18,12 @@ from rest_framework.views import APIView from rest_framework.response import Response from rest_framework_bulk import BulkModelViewSet from django.utils.translation import ugettext_lazy as _ +from django.shortcuts import get_object_or_404 from common.utils import get_logger, get_object_or_none from ..hands import IsSuperUser from ..models import Node +from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util from .. import serializers @@ -29,7 +31,8 @@ logger = get_logger(__file__) __all__ = [ 'NodeViewSet', 'NodeChildrenApi', 'NodeAddAssetsApi', 'NodeRemoveAssetsApi', - 'NodeAddChildrenApi', + 'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi', + 'TestNodeConnectiveApi' ] @@ -117,3 +120,31 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView): instance = self.get_object() if instance != Node.root(): instance.assets.remove(*tuple(assets)) + + +class RefreshNodeHardwareInfoApi(APIView): + permission_classes = (IsSuperUser,) + model = Node + + def get(self, request, *args, **kwargs): + node_id = kwargs.get('pk') + node = get_object_or_404(self.model, id=node_id) + assets = node.assets.all() + # task_name = _("Refresh node assets hardware info: {}".format(node.name)) + task_name = _("更新节点资产硬件信息: {}".format(node.name)) + update_assets_hardware_info_util.delay(assets, task_name=task_name) + return Response({"msg": "Task created"}) + + +class TestNodeConnectiveApi(APIView): + permission_classes = (IsSuperUser,) + model = Node + + def get(self, request, *args, **kwargs): + node_id = kwargs.get('pk') + node = get_object_or_404(self.model, id=node_id) + assets = node.assets.all() + task_name = _("测试节点下资产是否可连接: {}".format(node.name)) + test_asset_connectability_util.delay(assets, task_name=task_name) + return Response({"msg": "Task created"}) + diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index c34690076..dd92afad4 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -40,23 +40,22 @@ class SystemUserViewSet(BulkModelViewSet): permission_classes = (IsSuperUserOrAppUser,) -class SystemUserAuthInfoApi(generics.RetrieveAPIView): +class SystemUserAuthInfoApi(generics.RetrieveUpdateAPIView): """ Get system user auth info """ queryset = SystemUser.objects.all() permission_classes = (IsSuperUserOrAppUser,) + serializer_class = serializers.SystemUserAuthSerializer - def retrieve(self, request, *args, **kwargs): - system_user = self.get_object() - data = { - 'id': system_user.id, - 'name': system_user.name, - 'username': system_user.username, - 'password': system_user.password, - 'private_key': system_user.private_key, - } - return Response(data) + def update(self, request, *args, **kwargs): + password = request.data.pop("password", None) + private_key = request.data.pop("private_key", None) + instance = self.get_object() + + if password or private_key: + instance.set_auth(password=password, private_key=private_key) + return super().update(request, *args, **kwargs) class SystemUserPushApi(generics.RetrieveAPIView): diff --git a/apps/assets/forms/asset.py b/apps/assets/forms/asset.py index ce3e3fb56..65716f533 100644 --- a/apps/assets/forms/asset.py +++ b/apps/assets/forms/asset.py @@ -35,10 +35,10 @@ class AssetCreateForm(forms.ModelForm): 'ip': '* required', 'port': '* required', 'admin_user': _( - 'Admin user is a privilege user exist on this asset,' - 'Example: root or other NOPASSWD sudo privilege user' - 'If asset not support ansible, set any one' - ) + 'root or other NOPASSWD sudo privilege user existed in asset,' + 'If asset is windows or other set any one, more see admin user left menu' + ), + 'platform': _("* required Must set exact system platform, Windows, Linux ...") } @@ -67,10 +67,10 @@ class AssetUpdateForm(forms.ModelForm): 'port': '* required', 'cluster': '* required', 'admin_user': _( - 'Admin user is a privilege user exist on this asset,' - 'Example: root or other NOPASSWD sudo privilege user' - 'If asset not support ansible, set any one' - ) + 'root or other NOPASSWD sudo privilege user existed in asset,' + 'If asset is windows or other set any one, more see admin user left menu' + ), + 'platform': _("* required Must set exact system platform, Windows, Linux ...") } @@ -102,7 +102,7 @@ class AssetBulkUpdateForm(forms.ModelForm): class Meta: model = Asset fields = [ - 'assets', 'port', 'admin_user', 'labels', 'nodes', + 'assets', 'port', 'admin_user', 'labels', 'nodes', 'platform' ] widgets = { 'labels': forms.SelectMultiple( diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 794c841f2..38aad66ce 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -36,6 +36,21 @@ class SystemUserSerializer(serializers.ModelSerializer): return len(obj.assets) +class SystemUserAuthSerializer(serializers.ModelSerializer): + """ + 系统用户认证信息 + """ + password = serializers.CharField(max_length=1024) + private_key = serializers.CharField(max_length=4096) + + class Meta: + model = SystemUser + fields = [ + "id", "name", "username", "protocol", + "password", "private_key", + ] + + class AssetSystemUserSerializer(serializers.ModelSerializer): """ 查看授权的资产系统用户的数据结构,这个和AssetSerializer不同,字段少 diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index 2577c7a6c..fe5508720 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -21,7 +21,7 @@ def update_asset_hardware_info_on_created(asset): def test_asset_conn_on_created(asset): logger.debug("Test asset `{}` connectability".format(asset)) - test_asset_connectability_util.delay(asset) + test_asset_connectability_util.delay([asset]) def set_asset_root_node(asset): diff --git a/apps/assets/tasks.py b/apps/assets/tasks.py index 3f792a080..0cf5cbfd8 100644 --- a/apps/assets/tasks.py +++ b/apps/assets/tasks.py @@ -1,6 +1,7 @@ # ~*~ coding: utf-8 ~*~ import json import re +import os from celery import shared_task from django.core.cache import cache @@ -20,6 +21,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") @shared_task @@ -89,7 +91,8 @@ def update_assets_hardware_info_util(assets, task_name=None): """ from ops.utils import update_or_create_ansible_task if task_name is None: - task_name = _("Update some assets hardware info") + # task_name = _("Update some assets hardware info") + task_name = _("更新资产硬件信息") tasks = const.UPDATE_ASSETS_HARDWARE_TASKS hostname_list = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()] task, created = update_or_create_ansible_task( @@ -105,7 +108,8 @@ def update_assets_hardware_info_util(assets, task_name=None): @shared_task def update_asset_hardware_info_manual(asset): - task_name = _("Update asset hardware info") + # task_name = _("Update asset hardware info") + task_name = _("更新资产硬件信息") return update_assets_hardware_info_util([asset], task_name=task_name) @@ -118,8 +122,13 @@ def update_assets_hardware_info_period(): Update asset hardware period task :return: """ + if PERIOD_TASK != "on": + logger.debug("Period task disabled, update assets hardware info pass") + return + from ops.utils import update_or_create_ansible_task - task_name = _("Update assets hardware info period") + # task_name = _("Update assets hardware info period") + task_name = _("定期更新资产硬件信息") hostname_list = [ asset.hostname for asset in Asset.objects.all() if asset.is_active and asset.is_unixlike() @@ -190,25 +199,32 @@ def test_admin_user_connectability_period(): """ A period task that update the ansible task period """ + if PERIOD_TASK != "on": + logger.debug("Period task disabled, test admin user connectability pass") + return + admin_users = AdminUser.objects.all() for admin_user in admin_users: - task_name = _("Test admin user connectability period: {}".format(admin_user.name)) + # task_name = _("Test admin user connectability period: {}".format(admin_user.name)) + task_name = _("定期测试管理账号可连接性: {}".format(admin_user.name)) test_admin_user_connectability_util(admin_user, task_name) @shared_task def test_admin_user_connectability_manual(admin_user): - task_name = _("Test admin user connectability: {}").format(admin_user.name) + # task_name = _("Test admin user connectability: {}").format(admin_user.name) + task_name = _("测试管理行号可连接性: {}").format(admin_user.name) return test_admin_user_connectability_util.delay(admin_user, task_name) @shared_task -def test_asset_connectability_util(asset, task_name=None): +def test_asset_connectability_util(assets, task_name=None): from ops.utils import update_or_create_ansible_task if task_name is None: - task_name = _("Test asset connectability") - hosts = [asset.hostname] + # task_name = _("Test assets connectability") + task_name = _("测试资产可连接性") + hosts = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()] if not hosts: logger.info("No hosts, passed") return {} @@ -219,18 +235,17 @@ def test_asset_connectability_util(asset, task_name=None): ) result = task.run() summary = result[1] - if summary.get('dark'): - cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(asset.hostname), 0, - CACHE_MAX_TIME) - else: - cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(asset.hostname), 1, - CACHE_MAX_TIME) + for k in summary.get('dark'): + cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(k), 0, CACHE_MAX_TIME) + + for k in summary.get('contacted'): + cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(k), 1, CACHE_MAX_TIME) return summary @shared_task def test_asset_connectability_manual(asset): - summary = test_asset_connectability_util(asset) + summary = test_asset_connectability_util([asset]) if summary.get('dark'): return False, summary['dark'] @@ -287,9 +302,14 @@ def test_system_user_connectability_manual(system_user): @after_app_ready_start @after_app_shutdown_clean def test_system_user_connectability_period(): + if PERIOD_TASK != "on": + logger.debug("Period task disabled, test system user connectability pass") + return + system_users = SystemUser.objects.all() for system_user in system_users: - task_name = _("test system user connectability period: {}".format(system_user)) + # task_name = _("Test system user connectability period: {}".format(system_user)) + task_name = _("定期测试系统用户可连接性: {}".format(system_user)) test_system_user_connectability_util(system_user, task_name) @@ -366,7 +386,9 @@ def push_system_user_util(system_users, assets, task_name): def get_node_push_system_user_task_name(system_user, node): - return _("Push system user to node: {} => {}").format( + + # return _("Push system user to node: {} => {}").format( + return _("推送系统用户到节点资产: {} => {}").format( system_user.name, node.value ) @@ -404,7 +426,8 @@ def push_node_system_users_to_asset(node, assets): system_users.extend(list(n.systemuser_set.all())) if system_users: - task_name = _("Push system users to node: {}").format(node.value) + # task_name = _("Push system users to node: {}").format(node.value) + task_name = _("推送节点系统用户到新加入资产中: {}").format(node.value) push_system_user_util.delay(system_users, assets, task_name) diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html index 7cccc6d03..bcb40fb29 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -5,7 +5,8 @@ {% block help_message %}
- 管理用户是 服务器上已存在的特权用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。 + 管理用户是服务器的root,或拥有 NOPASSWD: ALL sudo权限的用户,Jumpserver使用该用户来 `推送系统用户`、`获取资产硬件信息`等。 + Windows或其它硬件可以随意设置一个
{% endblock %} diff --git a/apps/assets/templates/assets/asset_detail.html b/apps/assets/templates/assets/asset_detail.html index 80916340f..9767320a3 100644 --- a/apps/assets/templates/assets/asset_detail.html +++ b/apps/assets/templates/assets/asset_detail.html @@ -155,6 +155,7 @@ + {% if asset.is_unixlike %} {% trans 'Refresh hardware' %}: @@ -171,6 +172,7 @@ + {% endif %} diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index 628d0f905..da1b0be58 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -119,6 +119,8 @@