diff --git a/.gitignore b/.gitignore index 2c3064f75..983fedd49 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ nosetests.xml .mr.developer.cfg .project .pydevproject +*.log logs/* keys/* jumpserver.conf diff --git a/connect.py b/connect.py index 60f0daebb..8558ed745 100644 --- a/connect.py +++ b/connect.py @@ -19,8 +19,8 @@ import struct, fcntl, signal, socket, select os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() -from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object -from jumpserver.api import logger, mkdir, Log, TtyLog +from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object, mkdir +from jumpserver.api import logger, Log, TtyLog from jumpserver.settings import LOG_DIR @@ -68,9 +68,6 @@ def check_vim_status(command, ssh): return False - - - class Tty(object): """ A virtual tty class @@ -252,6 +249,7 @@ class Tty(object): log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.asset_name, time_start)) try: + mkdir(os.path.dirname(today_connect_log_dir), mode=0777) mkdir(today_connect_log_dir, mode=0777) except OSError: logger.debug('创建目录 %s 失败,请修改%s目录权限' % (today_connect_log_dir, tty_log_dir)) @@ -289,7 +287,7 @@ class Tty(object): # 2. get 映射用户 # 3. get 映射用户的账号,密码或者key # self.connect_info = {'user': '', 'asset': '', 'ip': '', 'port': 0, 'role_name': '', 'role_pass': '', 'role_key': ''} - self.connect_info = {'user': 'a', 'asset': 'b', 'ip': '127.0.0.1', 'port': 22, 'role_name': 'root', 'role_pass': '', 'role_key': '/root/.ssh/id_rsa.bak'} + self.connect_info = {'user': 'a', 'asset': 'b', 'ip': '127.0.0.1', 'port': 22, 'role_name': 'root', 'role_pass': 'redhat', 'role_key': ''} return self.connect_info def get_connection(self): @@ -452,7 +450,7 @@ class SshTty(Tty): #print 'ok'+tmp+'ok' # SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip() # SSH_TTY = '' - channel.send('clear\n') + # channel.send('clear\n') # Make ssh interactive tunnel self.posix_shell() @@ -468,20 +466,83 @@ class SshTty(Tty): pass -def print_prompt(): - """ - Print prompt - 打印提示导航 - """ - msg = """\033[1;32m### Welcome Use JumpServer To Login. ### \033[0m - 1) Type \033[32mIP or Part IP, Host Alias or Comments \033[0m To Login. - 2) Type \033[32mP/p\033[0m To Print The Servers You Available. - 3) Type \033[32mG/g\033[0m To Print The Server Groups You Available. - 4) Type \033[32mG/g(1-N)\033[0m To Print The Server Group Hosts You Available. - 5) Type \033[32mE/e\033[0m To Execute Command On Several Servers. - 6) Type \033[32mQ/q\033[0m To Quit. - """ - print textwrap.dedent(msg) +def print_user_asset_group_info(user): + asset_groups = AssetGroup.objects.all() + for asset_group in asset_groups: + if asset_group.comment: + print '[%-2s] %-10s %s' % (asset_group.id, asset_group.name, asset_group.comment) + else: + print '[%-2s] %-10s' % (asset_group.id, asset_group.name) + print + + +class Nav(object): + def __init__(self, user): + self.user = user + self.search_result = {} + + @staticmethod + def print_nav(): + """ + Print prompt + 打印提示导航 + """ + msg = """\n\033[1;32m### Welcome To Use JumpServer, A Open Source System . ### \033[0m + 1) Type \033[32mID\033[0m To Login. + 2) Type \033[32m/\033[0m + \033[32mIP, Host Name, Host Alias or Comments \033[0mTo Search. + 3) Type \033[32mP/p\033[0m To Print The Servers You Available. + 4) Type \033[32mG/g\033[0m To Print The Server Groups You Available. + 5) Type \033[32mG/g\033[0m\033[0m + \033[32mGroup ID\033[0m To Print The Server Group You Available. + 6) Type \033[32mE/e\033[0m To Execute Command On Several Servers. + 7) Type \033[32mQ/q\033[0m To Quit. + """ + + msg = """\n\033[1;32m### 欢迎使用Jumpserver开源跳板机 ### \033[0m + 1) 输入 \033[32mID\033[0m 直接登录. + 2) 输入 \033[32m/\033[0m + \033[32mIP, 主机名, 主机别名 or 备注 \033[0m搜索. + 3) 输入 \033[32mP/p\033[0m 显示您有权限的主机. + 4) 输入 \033[32mG/g\033[0m 显示您有权限的主机组. + 5) 输入 \033[32mG/g\033[0m\033[0m + \033[32m组ID\033[0m 显示该组下主机. + 6) 输入 \033[32mE/e\033[0m 批量执行命令. + 7) 输入 \033[32mQ/q\033[0m 退出. + """ + print textwrap.dedent(msg) + + def search(self, str_r=''): + gid_pattern = re.compile(r'^g\d+$') + user_asset_all = list(Asset.objects.all()) + user_asset_search = [] + if str_r: + if gid_pattern.match(str_r): + user_asset_search = list(Asset.objects.all()) + else: + for asset in user_asset_all: + if str_r in asset.ip or str_r in str(asset.comment): + user_asset_search.append(asset) + else: + user_asset_search = user_asset_all + + self.search_result = dict(zip(range(len(user_asset_search)), user_asset_search)) + + print '\033[32m[%-3s] %-15s %-15s %-5s %-5s %s \033[0m' % ('ID', 'AssetName', 'IP', 'Port', 'Role', 'Comment') + for index, asset in self.search_result.items(): + if asset.comment: + print '[%-3s] %-15s %-15s %-5s %-5s %s' % (index, 'asset_name'+str(index), asset.ip, asset.port, 'role', asset.comment) + else: + print '[%-3s] %-15s %-15s %-5s %-5s' % (index, 'asset_name'+str(index), asset.ip, asset.port, 'role') + print + + @staticmethod + def print_asset_group(): + user_asset_group_all = AssetGroup.objects.all() + + print '\033[32m[%-3s] %-15s %s \033[0m' % ('ID', 'GroupName', 'Comment') + for asset_group in user_asset_group_all: + if asset_group.comment: + print '[%-3s] %-15s %s' % (asset_group.id, asset_group.name, asset_group.comment) + else: + print '[%-3s] %-15s' % (asset_group.id, asset_group.name) + print def main(): @@ -492,29 +553,26 @@ def main(): if not login_user: # 判断用户是否存在 color_print(u'没有该用户,或许你是以root运行的 No that user.', exits=True) - print_prompt() gid_pattern = re.compile(r'^g\d+$') + nav = Nav(login_user) + nav.print_nav() try: while True: try: - option = raw_input("\033[1;32mOpt or IP>:\033[0m ") + option = raw_input("\033[1;32mOpt or ID>:\033[0m ").strip() except EOFError: - print_prompt() + nav.print_nav() continue except KeyboardInterrupt: sys.exit(0) - if option in ['P', 'p']: - login_user.get_asset_info(printable=True) + if option in ['P', 'p', '\n', '']: + nav.search() continue + if option.startswith('/') or gid_pattern.match(option): + nav.search(option.lstrip('/')) elif option in ['G', 'g']: - login_user.get_asset_group_info(printable=True) - continue - elif gid_pattern.match(option): - gid = option[1:].strip() - asset_group = get_object(AssetGroup, id=gid) - if asset_group and asset_group.is_permed(user=login_user): - asset_group.get_asset_info(printable=True) + nav.print_asset_group() continue elif option in ['E', 'e']: # exec_cmd_servers(login_name) @@ -523,7 +581,11 @@ def main(): sys.exit() else: try: - verify_connect(login_user, option) + asset = nav.search_result[int(option)] + ssh_tty = SshTty('a', 'b') + ssh_tty.connect() + except (KeyError, ValueError): + color_print('请输入正确ID', 'red') except ServerError, e: color_print(e, 'red') except IndexError: diff --git a/docs/requirements.txt b/docs/requirements.txt index ea56ab0c6..28d7b9137 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,5 @@ sphinx-me==0.3 django==1.6 -python-ldap==2.4.19 pycrypto==2.6.1 paramiko==1.15.2 ecdsa==0.13 @@ -9,4 +8,9 @@ django-uuidfield==0.5.0 psutil==2.2.1 xlsxwriter==0.7.7 xlrd==0.9.4 -django-bootstrap-form \ No newline at end of file +django-bootstrap-form +tornado +ansible +pyinotify +passlib +argparse \ No newline at end of file diff --git a/jasset/asset_api.py b/jasset/asset_api.py index 998d669c5..8103db23b 100644 --- a/jasset/asset_api.py +++ b/jasset/asset_api.py @@ -311,8 +311,12 @@ def write_excel(asset_all): group_all = '/'.join(group_list) status = asset.get_status_display() idc_name = asset.idc.name if asset.idc else u'' + system_type = asset.system_type if asset.idc else u'' + system_version = asset.system_version if asset.idc else u'' + system_os = unicode(system_type) + unicode(system_version) + alter_dic = [asset.hostname, asset.ip, idc_name, asset.mac, asset.remote_ip, asset.cpu, asset.memory, - asset.disk, (asset.system_type + asset.system_version), asset.cabinet, group_all, status, + asset.disk, system_os, asset.cabinet, group_all, status, asset.comment] data.append(alter_dic) format = workbook.add_format() @@ -381,13 +385,13 @@ def excel_to_db(excel_file): row = table.row_values(row_num) if row: ip, port, hostname, use_default_auth, username, password, group = row - print ip use_default_auth = 1 if use_default_auth == u'默认' else 0 if get_object(Asset, ip=ip): continue if ip and port: asset = Asset(ip=ip, port=port, + hostname=hostname, use_default_auth=use_default_auth, username=username, password=password diff --git a/jasset/models.py b/jasset/models.py index e011302a2..f1e5b46fc 100644 --- a/jasset/models.py +++ b/jasset/models.py @@ -37,8 +37,8 @@ class AssetGroup(models.Model): class IDC(models.Model): name = models.CharField(max_length=32, verbose_name=u'机房名称') bandwidth = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'机房带宽') - linkman = models.CharField(max_length=16, null=True, verbose_name=u'联系人') - phone = models.CharField(max_length=32, verbose_name=u'联系电话') + linkman = models.CharField(max_length=16, blank=True, null=True, verbose_name=u'联系人') + phone = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'联系电话') address = models.CharField(max_length=128, blank=True, null=True, verbose_name=u"机房地址") network = models.TextField(blank=True, null=True, verbose_name=u"IP地址段") date_added = models.DateField(auto_now=True, null=True) diff --git a/jasset/views.py b/jasset/views.py index 8427f9f67..71448f22a 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -2,9 +2,9 @@ import ast from django.db.models import Q -from django.shortcuts import get_object_or_404 from jasset.asset_api import * from jumpserver.api import * +from jumpserver.models import Setting from jasset.forms import AssetForm, IdcForm from jasset.models import Asset, IDC, AssetGroup, ASSET_TYPE, ASSET_STATUS from ansible_api import Tasks @@ -13,7 +13,7 @@ from ansible_api import Tasks @require_role('admin') def group_add(request): """ - Add asset group + Group add view 添加资产组 """ header_title, path1, path2 = u'添加资产组', u'资产管理', u'添加资产组' @@ -47,7 +47,7 @@ def group_add(request): @require_role('admin') def group_edit(request): """ - Edit asset group + Group edit view 编辑资产组 """ header_title, path1, path2 = u'编辑主机组', u'资产管理', u'编辑主机组' @@ -89,7 +89,10 @@ def group_edit(request): @require_role('admin') def group_detail(request): - """ 主机组详情 """ + """ + Group detail view + 主机组详情 + """ header_title, path1, path2 = u'主机组详情', u'资产管理', u'主机组详情' group_id = request.GET.get('id', '') group = get_object(AssetGroup, id=group_id) @@ -121,7 +124,7 @@ def group_list(request): @require_role('admin') def group_del(request): """ - del asset group + Group delete view 删除主机组 """ group_ids = request.GET.get('id', '') @@ -160,7 +163,7 @@ def asset_add(request): asset_save = af_post.save(commit=False) if not use_default_auth: password = request.POST.get('password', '') - password_encode = CRYPTOR.encrypt(password) + password_encode = password asset_save.password = password_encode asset_save.is_active = True if is_active else False asset_save.save() @@ -293,7 +296,7 @@ def asset_list(request): s = write_excel(asset_find) if s[0]: file_name = s[1] - smg = 'excel文件已生成,请点击下载!' + smg = u'excel文件已生成,请点击下载!' return my_render('jasset/asset_excel_download.html', locals(), request) assets_list, p, assets, page_range, current_page, show_first, show_end = pages(asset_find, request) return my_render('jasset/asset_list.html', locals(), request) @@ -330,13 +333,20 @@ def asset_update(request): return HttpResponseRedirect('/jasset/asset_detail/?id=%s' % asset_id) name = request.session.get('username', 'admin') if asset.use_default_auth: - username = 'root' - password = '123456' + default = Setting.objects.all() + if default: + default = default[0] + username = default.default_user + password = default.default_password + port = default.default_port + else: + return HttpResponse(u'没有设置默认用户名和密码!') else: username = asset.username password = asset.password + port = asset.port - resource = [{"hostname": asset.ip, "port": asset.port, + resource = [{"hostname": asset.ip, "port": port, "username": username, "password": password}] ansible_instance = Tasks(resource) @@ -446,16 +456,17 @@ def idc_del(request): """ IDC delete view """ - uuid = request.GET.get('uuid', '') - idc = get_object_or_404(IDC, uuid=uuid) - idc.delete() + uuid = request.GET.get('id', '') + idc = get_object(IDC, id=uuid) + if idc: + idc.delete() return HttpResponseRedirect('/jasset/idc_list/') @require_role('admin') def asset_upload(request): """ - Upload file view + Upload asset excel file view """ if request.method == 'POST': excel_file = request.FILES.get('file_name', '') diff --git a/jperm/ansible_api.py b/jperm/ansible_api.py index 2e89008fc..4e66da246 100644 --- a/jperm/ansible_api.py +++ b/jperm/ansible_api.py @@ -15,11 +15,11 @@ from utils import get_rand_pass import os.path + API_DIR = os.path.dirname(os.path.abspath(__file__)) ANSIBLE_DIR = os.path.join(API_DIR, 'playbooks') - class AnsibleError(StandardError): """ the base AnsibleError which contains error(required), @@ -61,7 +61,7 @@ class MyInventory(object): [{"hostname": "10.10.10.10", "port": "22", "username": "test", "password": "mypass"}, ...] """ self.resource = resource - self.inventory = Inventory() + self.inventory = Inventory(host_list=[]) self.gen_inventory() def add_group(self, hosts, groupname, groupvars=None): @@ -101,7 +101,7 @@ class MyInventory(object): add hosts to inventory. """ if isinstance(self.resource, list): - self.add_group(self.resource, 'my_group') + self.add_group(self.resource, 'default_group') elif isinstance(self.resource, dict): for groupname, hosts_and_vars in self.resource.iteritems(): self.add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars")) @@ -115,21 +115,23 @@ class Command(MyInventory): super(Command, self).__init__(*args, **kwargs) self.results = '' - def run(self, command, module_name="command", timeout=5, forks=10, group='my_group'): + def run(self, command, module_name="command", timeout=10, forks=10, group='default_group', pattern='*'): """ run command from andible ad-hoc. command : 必须是一个需要执行的命令字符串, 比如 'uname -a' """ + if module_name not in ["raw", "command", "shell"]: - raise CommandValueError("module_name", + raise CommandValueError("module_name", "module_name must be of the 'raw, command, shell'") hoc = Runner(module_name=module_name, module_args=command, timeout=timeout, inventory=self.inventory, subset=group, - forks=forks + pattern=pattern, + forks=forks, ) self.results = hoc.run() @@ -203,7 +205,7 @@ class Tasks(Command): def __init__(self, *args, **kwargs): super(Tasks, self).__init__(*args, **kwargs) - def __run(self, module_args, module_name="command", timeout=5, forks=10, group='my_group'): + def __run(self, module_args, module_name="command", timeout=5, forks=10, group='default_group', pattern='*'): """ run command from andible ad-hoc. command : 必须是一个需要执行的命令字符串, 比如 @@ -214,7 +216,8 @@ class Tasks(Command): timeout=timeout, inventory=self.inventory, subset=group, - forks=forks + pattern=pattern, + forks=forks, ) self.results = hoc.run() @@ -425,7 +428,6 @@ class MyPlaybook(MyInventory): def __init__(self, *args, **kwargs): super(MyPlaybook, self).__init__(*args, **kwargs) - def run(self, playbook_relational_path, extra_vars=None): """ run ansible playbook, @@ -464,7 +466,6 @@ class App(MyPlaybook): if __name__ == "__main__": - pass # resource = { # "group1": { @@ -472,8 +473,10 @@ if __name__ == "__main__": # "vars" : {"var1": "value1", "var2": "value2"}, # }, # } -# command = Command(resource) -# print command.run("who", group="group1") + + resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902"}] + command = Command(resource) + print command.run("who") # resource = [{"hostname": "192.168.10.148", "port": "22", "username": "root", "password": "xxx"}] # task = Tasks(resource) diff --git a/jperm/models.py b/jperm/models.py index 2bcea14e3..dc8643b67 100644 --- a/jperm/models.py +++ b/jperm/models.py @@ -32,14 +32,13 @@ class PermRole(models.Model): class PermRule(models.Model): date_added = models.DateTimeField(auto_now=True) - name = models.CharField(max_length=100) + name = models.CharField(max_length=100, unique=True) comment = models.CharField(max_length=100) asset = models.ManyToManyField(Asset, related_name='perm_rule') asset_group = models.ManyToManyField(AssetGroup, related_name='perm_rule') user = models.ManyToManyField(User, related_name='perm_rule') user_group = models.ManyToManyField(UserGroup, related_name='perm_rule') role = models.ManyToManyField(PermRole, related_name='perm_rule') - ssh_type = models.BooleanField() def __unicode__(self): return self.name \ No newline at end of file diff --git a/jperm/perm_api.py b/jperm/perm_api.py index c04aeda52..1b363f547 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -89,7 +89,7 @@ def perm_user_api(perm_info): the_new_users = ','.join(new_username) the_del_users = ','.join(del_username) - playbook = get_playbook(os.path.join(BASE_DIR, 'playbook', 'user_perm.yaml'), + playbook = get_playbook(os.path.join(BASE_DIR, 'keys/../playbook', 'user_perm.yaml'), {'the_new_group': 'new', 'the_del_group': 'del', 'the_new_users': the_new_users, 'the_del_users': the_del_users, 'KEY_DIR': os.path.join(SSH_KEY_DIR, 'sysuser')}) diff --git a/jperm/utils.py b/jperm/utils.py index 12756b773..c8acff371 100644 --- a/jperm/utils.py +++ b/jperm/utils.py @@ -4,7 +4,7 @@ import random import os.path from paramiko.rsakey import RSAKey -from os import chmod, mkdir +from jumpserver.api import mkdir from uuid import uuid4 from jumpserver.settings import KEY_DIR @@ -45,9 +45,8 @@ def gen_keys(): :return: 返回目录名(uuid) """ key_basename = "key-" + uuid4().hex - key_path_dir = os.path.join(KEY_DIR, key_basename) - mkdir(key_path_dir, 0700) - + key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename) + mkdir(key_path_dir, 0755) key = RSAKey.generate(2048) private_key = os.path.join(key_path_dir, 'id_rsa') public_key = os.path.join(key_path_dir, 'id_rsa.pub') @@ -62,8 +61,6 @@ def gen_keys(): return key_path_dir - - if __name__ == "__main__": print gen_keys() diff --git a/jperm/views.py b/jperm/views.py index 2685d6bd6..f961a8e07 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- - from django.db.models import Q from jperm.perm_api import * from jperm.models import PermLog as Log @@ -11,12 +10,13 @@ from juser.user_api import gen_ssh_key from juser.models import User, UserGroup from jasset.models import Asset, AssetGroup from jperm.models import PermRole, PermRule +from jumpserver.models import Setting from jperm.utils import updates_dict, gen_keys, get_rand_pass from jperm.ansible_api import Tasks from jperm.perm_api import get_role_info -from jumpserver.api import my_render, get_object +from jumpserver.api import my_render, get_object, CRYPTOR @require_role('admin') @@ -62,7 +62,7 @@ def perm_rule_detail(request): assets = asset_obj return my_render('jperm/perm_rule_detail.html', locals(), request) - + def perm_rule_add(request): """ @@ -89,7 +89,8 @@ def perm_rule_add(request): asset_groups_select = request.POST.getlist('assetgroup', []) roles_select = request.POST.getlist('role', []) rule_name = request.POST.get('rulename') - rule_comment = request.POST.get('comment') + rule_comment = request.POST.get('rule_comment') + rule_ssh_key = request.POST.get("use_publicKey") # 获取需要授权的主机列表 assets_obj = [Asset.objects.get(ip=asset) for asset in assets_select] @@ -115,7 +116,19 @@ def perm_rule_add(request): rule.asset_group = asset_groups_obj rule.role = roles_obj rule.save() - return HttpResponse(u"添加授权规则:%s" % rule.name) + + msg = u"添加授权规则:%s" % rule.name + # 渲染数据 + header_title, path1, path2 = "授权规则", "规则管理", "查看规则" + rules_list = PermRule.objects.all() + + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + rules_list = rules_list.filter(Q(name=keyword)) + rules_list, p, rules, page_range, current_page, show_first, show_end = pages(rules_list, request) + + return my_render('jperm/perm_rule_list.html', locals(), request) @require_role('admin') @@ -131,17 +144,69 @@ def perm_rule_edit(request): rule = PermRule.objects.get(id=rule_id) if request.method == 'GET' and rule_id: - # 渲染数据, 获取所有的rule对象 - users = rule.user.all() - user_groups = rule.user_group.all() - assets = rule.asset.all() - asset_groups = rule.asset_group.all() - roles = rule.role.all() + # 渲染数据, 获取所选的rule对象 + rule_comment = rule.comment + users_select = rule.user.all() + user_groups_select = rule.user_group.all() + assets_select = rule.asset.all() + asset_groups_select = rule.asset_group.all() + roles_select = rule.role.all() + + users = User.objects.all() + user_groups = UserGroup.objects.all() + assets = Asset.objects.all() + asset_groups = AssetGroup.objects.all() + roles = PermRole.objects.all() return my_render('jperm/perm_rule_edit.html', locals(), request) elif request.method == 'POST' and rule_id: - return HttpResponse("uncompleted") + # 获取用户选择的 用户,用户组,资产,资产组,用户角色 + rule_name = request.POST.get('rule_name') + rule_comment = request.POST.get("rule_comment") + users_select = request.POST.getlist('user', []) + user_groups_select = request.POST.getlist('usergroup', []) + assets_select = request.POST.getlist('asset', []) + asset_groups_select = request.POST.getlist('assetgroup', []) + roles_select = request.POST.getlist('role', []) + + # 获取需要授权的主机列表 + assets_obj = [Asset.objects.get(ip=asset) for asset in assets_select] + asset_groups_obj = [AssetGroup.objects.get(name=group) for group in asset_groups_select] + group_assets_obj = [asset for asset in [group.asset_set.all() for group in asset_groups_obj]] + calc_assets = set(group_assets_obj) | set(assets_obj) + + # 获取需要授权的用户列表 + users_obj = [User.objects.get(name=user) for user in users_select] + user_groups_obj = [UserGroup.objects.get(name=group) for group in user_groups_select] + group_users_obj = [user for user in [group.user_set.all() for group in user_groups_obj]] + calc_users = set(group_users_obj) | set(users_obj) + + # 获取授予的角色列表 + roles_obj = [PermRole.objects.get(name=role) for role in roles_select] + + # 仅授权成功的,写回数据库(授权规则,用户,用户组,资产,资产组,用户角色) + rule.user = users_obj + rule.usergroup = user_groups_obj + rule.asset = assets_obj + rule.asset_group = asset_groups_obj + rule.role = roles_obj + rule.name = rule_name + rule.comment = rule.comment + rule.save() + + msg = u"更新授权规则:%s" % rule.name + # 渲染数据 + header_title, path1, path2 = "授权规则", "规则管理", "查看规则" + rules_list = PermRule.objects.all() + + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + rules_list = rules_list.filter(Q(name=keyword)) + rules_list, p, rules, page_range, current_page, show_first, show_end = pages(rules_list, request) + + return my_render('jperm/perm_rule_list.html', locals(), request) @require_role('admin') @@ -201,12 +266,24 @@ def perm_role_add(request): name = request.POST.get("role_name") comment = request.POST.get("role_comment") password = request.POST.get("role_password") + encrypt_pass = CRYPTOR.encrypt(password) # 生成随机密码,生成秘钥对 key_path = gen_keys() - role = PermRole(name=name, comment=comment, password=password, key_path=key_path) + role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) role.save() - return HttpResponse(u"添加角色: %s" % name) + + msg = u"添加角色: %s" % name + # 渲染 刷新数据 + header_title, path1, path2 = "系统角色", "角色管理", "查看角色" + roles_list = PermRole.objects.all() + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + roles_list = roles_list.filter(Q(name=keyword)) + + roles_list, p, roles, page_range, current_page, show_first, show_end = pages(roles_list, request) + return my_render('jperm/perm_role_list.html', locals(), request) else: return HttpResponse(u"不支持该操作") @@ -252,8 +329,12 @@ def perm_role_detail(request): role_info = get_role_info(role_id) # 渲染数据 - for key, value in role_info.iteritems(): - key = value + rules = role_info.get("rules") + assets = role_info.get("assets") + asset_groups = role_info.get("asset_groups") + users = role_info.get("users") + user_groups = role_info.get("user_groups") + return my_render('jperm/perm_role_detail.html', locals(), request) @@ -265,15 +346,40 @@ def perm_role_edit(request): # 渲染数据 header_title, path1, path2 = "系统角色", "角色管理", "角色编辑" + # 渲染数据 + role_id = request.GET.get("id") + role = PermRole.objects.get(id=role_id) + role_pass = CRYPTOR.decrypt(role.password) if request.method == "GET": - role_id = request.GET.get("id") - # 渲染数据 - role = PermRole.objects.get(id=role_id) - return my_render('jperm/perm_role_edit.html', locals(), request) if request.method == "POST": - return HttpResponse(u"未实现") + # 获取 POST 数据 + role_name = request.POST.get("role_name") + role_password = request.POST.get("role_password") + encrypt_role_pass = CRYPTOR.encrypt(role_password) + role_comment = request.POST.get("role_comment") + + # 写入数据库 + role.name = role_name + role.password = encrypt_role_pass + role.comment = role_comment + + role.save() + msg = u"更新系统角色: %s" % role.name + + # 渲染 刷新数据 + header_title, path1, path2 = "系统角色", "角色管理", "查看角色" + roles_list = PermRole.objects.all() + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + roles_list = roles_list.filter(Q(name=keyword)) + + roles_list, p, roles, page_range, current_page, show_first, show_end = pages(roles_list, request) + return my_render('jperm/perm_role_list.html', locals(), request) + + @require_role('admin') @@ -307,10 +413,20 @@ def perm_role_push(request): calc_assets = set(assets_obj) | set(group_assets_obj) # 生成Inventory - push_resource = [{"hostname": asset.ip, - "port": asset.port, - "username": asset.username, - "password": asset.password} for asset in calc_assets] + push_resource = [] + for asset in calc_assets: + if asset.use_default_auth: + username = Setting.default_user + password = Setting.default_password + port = Setting.default_port + else: + username = asset.username + password = asset.password + port = asset.port + push_resource.append({"hostname": asset.ip, + "port": port, + "username": username, + "password": password}) # 获取角色的推送方式,以及推送需要的信息 roles_obj = [PermRole.objects.get(name=role_name) for role_name in role_names] @@ -326,10 +442,13 @@ def perm_role_push(request): task = Tasks(push_resource) ret = {} ret_failed = [] - if password_push: - ret["password_push"] = task.add_multi_user(**role_pass) - if ret["password_push"].get("status") != "success": - ret_failed.append(1) + + # 因为要先建立用户,所以password 是必选项, + # 而push key是在 password也完成的情况下的 可选项 + ret["password_push"] = task.add_multi_user(**role_pass) + if ret["password_push"].get("status") != "success": + ret_failed.append(1) + if key_push: ret["key_push"] = task.push_multi_key(**role_key) if ret["key_push"].get("status") != "success": diff --git a/jumpserver/api.py b/jumpserver/api.py index 5b964586c..6dc1a1602 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -14,10 +14,8 @@ from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.http import HttpResponse, Http404 from django.template import RequestContext from juser.models import User, UserGroup +from jlog.models import Log from jasset.models import Asset, AssetGroup -# from jlog.models import Log -from jlog.models import Log, TtyLog -from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.core.mail import send_mail @@ -197,9 +195,9 @@ def require_role(role='user'): def _deco(func): def __deco(request, *args, **kwargs): + request.session['pre_url'] = request.path if not request.user.is_authenticated(): return HttpResponseRedirect('/login/') - if role == 'admin': # if request.session.get('role_id', 0) < 1: if request.user.role == 'CU': @@ -395,8 +393,9 @@ def mkdir(dir_name, username='root', mode=0755): """ if not os.path.isdir(dir_name): os.makedirs(dir_name) - bash("chown %s:%s '%s'" % (username, username, dir_name)) os.chmod(dir_name, mode) + if username: + bash('chown %s:%s %s' % (username, username, dir_name)) def http_success(request, msg): @@ -414,4 +413,3 @@ def my_render(template, data, request): CRYPTOR = PyCrypt(KEY) logger = set_log(LOG_LEVEL) -KEY_DIR = os.path.join(BASE_DIR, 'keys') diff --git a/jumpserver/settings.py b/jumpserver/settings.py index cc3f5a8c7..be8464d38 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -17,8 +17,8 @@ config = ConfigParser.ConfigParser() BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) config.read(os.path.join(BASE_DIR, 'jumpserver.conf')) +KEY_DIR = os.path.join(BASE_DIR, 'keys') -KEY_DIR = os.path.join(BASE_DIR, 'role_keys') DB_HOST = config.get('db', 'host') DB_PORT = config.getint('db', 'port') @@ -37,7 +37,7 @@ EMAIL_TIMEOUT = 5 # ======== Log ========== LOG_DIR = os.path.join(BASE_DIR, 'logs') -SSH_KEY_DIR = os.path.join(BASE_DIR, 'role_keys') +SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys/role_keys') KEY = config.get('base', 'key') URL = config.get('base', 'url') LOG_LEVEL = config.get('base', 'log') diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 8dcdf377c..5af20cdd6 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -226,3 +226,14 @@ def ip_str_to_list(ip_str): ip str to list """ return ip_str.split(',') + + +@register.filter(name='key_exist') +def key_exist(username): + """ + ssh key is exist or not + """ + if os.path.isfile(os.path.join(KEY_DIR, 'user', username)): + return True + else: + return False diff --git a/jumpserver/views.py b/jumpserver/views.py index 3f8ba2fae..57ae3f5dd 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -15,7 +15,6 @@ from jumpserver.api import * from jumpserver.models import Setting from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required -from settings import BASE_DIR from jlog.models import Log @@ -236,7 +235,7 @@ def Login(request): request.session['role_id'] = 1 else: request.session['role_id'] = 0 - return HttpResponseRedirect(request.GET.get('next', '/'), ) + return HttpResponseRedirect(request.session.get('pre_url', '/')) # response.set_cookie('username', username, expires=604800) # response.set_cookie('seed', PyCrypt.md5_crypt(password), expires=604800) # return response @@ -269,7 +268,7 @@ def setting(request): if '' in [username, port] and ('' in password or '' in private_key): return HttpResponse('所填内容不能为空, 且密码和私钥填一个') else: - private_key_path = os.path.join(BASE_DIR, 'role_keys', 'default', 'default_private_key.pem') + private_key_path = os.path.join(BASE_DIR, 'keys/role_keys', 'default', 'default_private_key.pem') if private_key: with open(private_key_path, 'w') as f: f.write(private_key) diff --git a/juser/user_api.py b/juser/user_api.py index 911c554ee..87e7b5cc5 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -123,21 +123,21 @@ def db_del_user(username): def gen_ssh_key(username, password='', key_dir=os.path.join(KEY_DIR, 'user'), - authorized_keys=True, home="/home", length=2048): """ generate a user ssh key in a property dir 生成一个用户ssh密钥对 """ + logger.debug('生成ssh key, 并设置authorized_keys') private_key_file = os.path.join(key_dir, username) - mkdir(private_key_file, username) + mkdir(key_dir) if os.path.isfile(private_key_file): os.unlink(private_key_file) ret = bash('echo -e "y\n"|ssh-keygen -t rsa -f %s -b %s -P "%s"' % (private_key_file, length, password)) if authorized_keys: auth_key_dir = os.path.join(home, username, '.ssh') - mkdir(auth_key_dir, username, mode=0700) + mkdir(auth_key_dir, mode=0700) authorized_key_file = os.path.join(auth_key_dir, 'authorized_keys') with open(private_key_file+'.pub') as pub_f: with open(authorized_key_file, 'w') as auth_f: diff --git a/keys/README.md b/keys/README.md new file mode 100644 index 000000000..9e060e41d --- /dev/null +++ b/keys/README.md @@ -0,0 +1,3 @@ +看山是山,看水是水 +看山不是山,看水不是水 +看山是山,看水是水 diff --git a/logs/README.md b/logs/README.md new file mode 100644 index 000000000..309d1a147 --- /dev/null +++ b/logs/README.md @@ -0,0 +1 @@ +永远年轻,永远热泪盈眶 diff --git a/playbook/user_perm.yaml b/playbook/user_perm.yaml deleted file mode 100644 index 4bcfd72e6..000000000 --- a/playbook/user_perm.yaml +++ /dev/null @@ -1,17 +0,0 @@ -- hosts: the_del_group - tasks: - - name: del user - user: name={{ item }} state=absent remove=yes - with_items: [ the_del_users ] - -- hosts: the_new_group - tasks: - - name: add user - user: name={{ item }} state=present - with_items: [ the_new_users ] - - name: .ssh direcotory - file: name=/home/{{ item }}/.ssh mode=700 owner={{ item }} group={{ item }} state=directory - with_items: [ the_new_users ] - - name: set authorizied_file - copy: src=KEY_DIR/{{ item }}.pub dest=/home/{{ item }}/.ssh/authorizied_keys owner={{ item }} group={{ item }} mode=600 - with_items: [ the_new_users ] diff --git a/templates/index.html b/templates/index.html index af6966ef6..0d8baf9f9 100644 --- a/templates/index.html +++ b/templates/index.html @@ -14,7 +14,6 @@

{{ users.count}}

-{#
{{ percent_user }}
#} All user
@@ -27,7 +26,6 @@

{{ hosts.count }}

-{#
{{ percent_host }}
#} All host
@@ -37,7 +35,7 @@
Online -
实时在线用户
+
在线用户

{{ online_user | length }}

@@ -55,7 +53,6 @@

{{ online_host | length }}

-{#
{{ percent_online_host }}
#} Connected host
@@ -169,7 +166,7 @@
-

一周Top10资产

+

一周Top10资产

登录次数及最近一次登录记录.
@@ -309,14 +306,7 @@
- - - - - - - - + {% endblock %} diff --git a/templates/jasset/asset_detail.html b/templates/jasset/asset_detail.html index 1f8d0d130..ef202bae9 100644 --- a/templates/jasset/asset_detail.html +++ b/templates/jasset/asset_detail.html @@ -31,11 +31,11 @@ - + - + @@ -53,11 +53,11 @@ - + - + @@ -66,7 +66,7 @@
IP{{ asset.ip }}{{ asset.ip|default_if_none:"" }}
主机名{{ asset.hostname }}{{ asset.hostname|default_if_none:"" }}
其他IP
远控IP{{ asset.remote_ip }}{{ asset.remote_ip|default_if_none:"" }}
端口{{ asset.port }}{{ asset.port|default_if_none:"" }}
{% for asset_group in asset.group.all %} - + {% endfor %}
{{ asset_group.name }}{{ asset_group.name|default_if_none:"" }}
@@ -79,19 +79,19 @@ 机房 - {{ asset.idc.name }} + {{ asset.idc.name|default_if_none:"" }} 硬件厂商型号 - {{ asset.brand }} + {{ asset.brand|default_if_none:"" }} CPU - {{ asset.cpu }} + {{ asset.cpu|default_if_none:"" }} 内存 - {{ asset.memory }}M + {{ asset.memory|default_if_none:"" }}{% if asset.memory %}M{% endif %} 硬盘 @@ -100,7 +100,7 @@ {% if asset.disk %} {% for disk, value in asset.disk|str_to_dic %} - {{ disk }}     {{ value }} + {{ disk|default_if_none:"" }}     {{ value|default_if_none:"" }} {% endfor %} {% endif %} @@ -109,35 +109,35 @@ 资产编号 - {{ asset.number }} + {{ asset.number|default_if_none:"" }} SN - {{ asset.sn }} + {{ asset.sn|default_if_none:"" }} 主机类型 - {{ asset.get_asset_type_display }} + {{ asset.get_asset_type_display|default_if_none:"" }} 系统版本 - {{ asset.system_type }} {{ asset.system_version }} + {{ asset.system_type|default_if_none:"" }} {{ asset.system_version|default_if_none:"" }} 运行环境 - {{ asset.get_env_display }} + {{ asset.get_env_display|default_if_none:"" }} 机器状态 - {{ asset.get_status_display }} + {{ asset.get_status_display|default_if_none:"" }} 机柜号 - {{ asset.cabinet }} + {{ asset.cabinet|default_if_none:"" }} 机柜位置 - {{ asset.position }} + {{ asset.position|default_if_none:"" }} 激活 @@ -149,7 +149,7 @@ 备注 - {{ asset.comment }} + {{ asset.comment|default_if_none:"" }} diff --git a/templates/jasset/asset_edit_batch.html b/templates/jasset/asset_edit_batch.html index b41afcb7b..5422a75b1 100644 --- a/templates/jasset/asset_edit_batch.html +++ b/templates/jasset/asset_edit_batch.html @@ -54,17 +54,17 @@
@@ -140,8 +140,8 @@ $('#uuid').val(ids) }); - $('#id_use_default_auth').click(function(){ - if ($(this).is(':checked')){ + $('.auth').click(function(){ + if ($(this).attr('id') == 'pass'){ $('#admin_account').css('display', 'block') } else { diff --git a/templates/jasset/asset_excel_download.html b/templates/jasset/asset_excel_download.html index d1b6738ee..113544c15 100644 --- a/templates/jasset/asset_excel_download.html +++ b/templates/jasset/asset_excel_download.html @@ -1,7 +1,6 @@
-

-

Nice! excel文件已生成请点击 下载 + Nice! excel文件已生成请点击 下载
\ No newline at end of file diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 133e41eea..9e3ea2d2c 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -119,12 +119,12 @@ - {{ asset.ip }} - {{ asset.hostname }} - {{ asset.idc.name }} + {{ asset.ip|default_if_none:"" }} + {{ asset.hostname|default_if_none:"" }} + {{ asset.idc.name|default_if_none:"" }} {{ asset.group.all|group_str2 }} {# {{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}#} - {{ asset.system_type }}{{ asset.system_version }} + {{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }} {{ asset.use_default_auth|bool2str }} 详情 @@ -169,17 +169,17 @@ }); $(document).ready(function(){ - $('#editable').editableTableWidget({editor: $('