From e979c2753ba541f14bbcc225fe82f256fa5b245e Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 8 Jun 2015 23:46:40 +0800 Subject: [PATCH 001/385] modify 1 --- connect.py | 124 ++++++++++++++++++++++++++++++------------------- juser/tests.py | 3 -- 2 files changed, 77 insertions(+), 50 deletions(-) delete mode 100644 juser/tests.py diff --git a/connect.py b/connect.py index 196699778..8bd93daac 100644 --- a/connect.py +++ b/connect.py @@ -18,33 +18,36 @@ import textwrap import getpass import fnmatch import readline +import django import datetime from multiprocessing import Pool os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' +if django.get_version() != '1.6': + django.setup() from juser.models import User from jlog.models import Log from jumpserver.api import CONF, BASE_DIR, ServerError, user_perm_group_api, user_perm_group_hosts_api, get_user_host from jumpserver.api import AssetAlias, get_connect_item - try: import termios import tty except ImportError: - print '\033[1;31mOnly UnixLike supported.\033[0m' + print '\033[1;31mOnly unix like supported.\033[0m' time.sleep(3) sys.exit() CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) -LOG_DIR = os.path.join(BASE_DIR, 'logs') -SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys') -SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') -LOGIN_NAME = getpass.getuser() +log_dir = os.path.join(BASE_DIR, 'logs') +login_name = getpass.getuser() def color_print(msg, color='blue'): - """Print colorful string.""" + """ + Print colorful string. + 颜色打印 + """ color_msg = {'blue': '\033[1;36m%s\033[0m', 'green': '\033[1;32m%s\033[0m', 'red': '\033[1;31m%s\033[0m'} @@ -53,48 +56,63 @@ def color_print(msg, color='blue'): def color_print_exit(msg, color='red'): - """Print colorful string and exit.""" + """ + Print colorful string and exit. + 颜色打印并推出 + """ color_print(msg, color=color) time.sleep(2) sys.exit() def get_win_size(): - """This function use to get the size of the windows!""" + """ + This function use to get the size of the windows! + 获得terminal窗口大小 + """ if 'TIOCGWINSZ' in dir(termios): TIOCGWINSZ = termios.TIOCGWINSZ else: - TIOCGWINSZ = 1074295912L # Assume + TIOCGWINSZ = 1074295912L s = struct.pack('HHHH', 0, 0, 0, 0) x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) return struct.unpack('HHHH', x)[0:2] def set_win_size(sig, data): - """This function use to set the window size of the terminal!""" + """ + This function use to set the window size of the terminal! + 设置terminal窗口大小 + """ try: win_size = get_win_size() channel.resize_pty(height=win_size[0], width=win_size[1]) - except: + except Exception: pass def log_record(username, host): - """Logging user command and output.""" - connect_log_dir = os.path.join(LOG_DIR, 'connect') + """ + Logging user command and output. + 记录用户的日志 + """ + connect_log_dir = os.path.join(log_dir, 'connect') timestamp_start = int(time.time()) today = time.strftime('%Y%m%d', time.localtime(timestamp_start)) time_now = time.strftime('%H%M%S', time.localtime(timestamp_start)) today_connect_log_dir = os.path.join(connect_log_dir, today) log_filename = '%s_%s_%s.log' % (username, host, time_now) log_file_path = os.path.join(today_connect_log_dir, log_filename) - dept_name = User.objects.get(username=username).dept.name + dept = User.objects.filter(username=username) + if dept: + dept = dept[0] + dept_name = dept.name + else: + dept_name = 'None' + pid = os.getpid() - ip_list = [] - remote_ip = os.popen("who |grep `ps aux |gawk '{if ($2==%s) print $1}'` |gawk '{print $5}'|tr -d '()'" % pid).readlines() - for ip in remote_ip: - ip_list.append(ip.strip('\n')) - ip_list = ','.join(list(set(ip_list))) + pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() + remote_ip = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') if not os.path.isdir(today_connect_log_dir): try: @@ -108,9 +126,9 @@ def log_record(username, host): except IOError: raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - log = Log(user=username, host=host, remote_ip=ip_list, dept_name=dept_name, + log = Log(user=username, host=host, remote_ip=remote_ip, dept_name=dept_name, log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) - log_file.write('Starttime is %s\n' % datetime.datetime.now()) + log_file.write('Start time is %s\n' % datetime.datetime.now()) log.save() return log_file, log @@ -118,6 +136,7 @@ def log_record(username, host): def posix_shell(chan, username, host): """ Use paramiko channel connect server interactive. + 使用paramiko模块的channel,连接后端,进入交互式 """ log_file, log = log_record(username, host) old_tty = termios.tcgetattr(sys.stdin) @@ -129,7 +148,7 @@ def posix_shell(chan, username, host): while True: try: r, w, e = select.select([chan, sys.stdin], [], []) - except: + except Exception: pass if chan in r: @@ -152,17 +171,20 @@ def posix_shell(chan, username, host): finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - log_file.write('Endtime is %s' % datetime.datetime.now()) + log_file.write('End time is %s' % datetime.datetime.now()) log_file.close() log.is_finished = True log.log_finished = False log.end_time = datetime.datetime.now() log.save() - print_prompt() + # print_prompt() -def get_user_hostgroup(username): - """Get the hostgroups of under the user control.""" +def get_user_host_group(username): + """ + Get the host groups of under the user control. + 获取用户有权限的主机组 + """ groups_attr = {} group_all = user_perm_group_api(username) for group in group_all: @@ -170,17 +192,25 @@ def get_user_hostgroup(username): return groups_attr -def get_user_hostgroup_host(username, gid): - """Get the hostgroup hosts of under the user control.""" +def get_host_group_host(username, gid): + """ + Get the host group hosts of under the user control. + 获取用户有权限主机组下的主机 + """ + groups_attr = get_user_host_group(username) + groups_ids = [attr[0] for name, attr in groups_attr.items()] hosts_attr = {} - user = User.objects.get(username=username) - hosts = user_perm_group_hosts_api(gid) - for host in hosts: - alias = AssetAlias.objects.filter(user=user, host=host) - if alias and alias[0].alias != '': - hosts_attr[host.ip] = [host.id, host.ip, alias[0].alias] - else: - hosts_attr[host.ip] = [host.id, host.ip, host.comment] + if gid in groups_ids: + user = User.objects.filter(username=username) + if user: + user = user[0] + hosts = user_perm_group_hosts_api(gid) + for host in hosts: + alias = AssetAlias.objects.filter(user=user, host=host) + if alias and alias[0].alias != '': + hosts_attr[host.ip] = [host.id, host.ip, alias[0].alias] + else: + hosts_attr[host.ip] = [host.id, host.ip, host.comment] return hosts_attr @@ -209,7 +239,7 @@ def verify_connect(username, part_ip): color_print('No Permission or No host.', 'red') else: username, password, host, port = get_connect_item(username, ip_matched[0]) - connect(username, password, host, port, LOGIN_NAME) + connect(username, password, host, port, login_name) def print_prompt(): @@ -238,7 +268,7 @@ def print_user_host(username): def print_user_hostgroup(username): - group_attr = get_user_hostgroup(username) + group_attr = get_user_host_group(username) groups = group_attr.keys() for g in groups: print "[%3s] %s -- %s" % (group_attr[g][0], g, group_attr[g][1]) @@ -248,7 +278,7 @@ def print_user_hostgroup_host(username, gid): pattern = re.compile(r'\d+') match = pattern.match(gid) if match: - hosts_attr = get_user_hostgroup_host(username, gid) + hosts_attr = get_host_group_host(username, gid) hosts = hosts_attr.keys() hosts.sort() for ip in hosts: @@ -327,7 +357,7 @@ def multi_remote_exec_cmd(hosts, username, cmd): def exec_cmd_servers(username): color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green') - print_user_host(LOGIN_NAME) + print_user_host(login_name) while True: hosts = [] inputs = raw_input('\033[1;32mip(s)>: \033[0m') @@ -355,7 +385,7 @@ def exec_cmd_servers(username): cmd = raw_input('\033[1;32mCmd(s): \033[0m') if cmd in ['q', 'Q']: break - exec_log_dir = os.path.join(LOG_DIR, 'exec_cmds') + exec_log_dir = os.path.join(log_dir, 'exec_cmds') if not os.path.isdir(exec_log_dir): os.mkdir(exec_log_dir) os.chmod(exec_log_dir, 0777) @@ -379,22 +409,22 @@ if __name__ == '__main__': except KeyboardInterrupt: sys.exit(0) if option in ['P', 'p']: - print_user_host(LOGIN_NAME) + print_user_host(login_name) continue elif option in ['G', 'g']: - print_user_hostgroup(LOGIN_NAME) + print_user_hostgroup(login_name) continue elif gid_pattern.match(option): gid = option[1:].strip() - print_user_hostgroup_host(LOGIN_NAME, gid) + print_user_hostgroup_host(login_name, gid) continue elif option in ['E', 'e']: - exec_cmd_servers(LOGIN_NAME) + exec_cmd_servers(login_name) elif option in ['Q', 'q', 'exit']: sys.exit() else: try: - verify_connect(LOGIN_NAME, option) + verify_connect(login_name, option) except ServerError, e: color_print(e, 'red') except IndexError: diff --git a/juser/tests.py b/juser/tests.py deleted file mode 100644 index 7ce503c2d..000000000 --- a/juser/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. From fc22677f3fbe91e2688290c09010fa7bfdd3fbf3 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 9 Jun 2015 23:06:32 +0800 Subject: [PATCH 002/385] modify some --- connect.py | 131 +++++++++++++----------- jasset/models.py | 2 +- jlog/views.py | 5 +- jumpserver.conf | 1 + jumpserver/api.py | 249 ++++++++++++++++++++++++++++++++++++---------- 5 files changed, 278 insertions(+), 110 deletions(-) diff --git a/connect.py b/connect.py index 8bd93daac..d25d207d0 100644 --- a/connect.py +++ b/connect.py @@ -27,8 +27,8 @@ if django.get_version() != '1.6': django.setup() from juser.models import User from jlog.models import Log -from jumpserver.api import CONF, BASE_DIR, ServerError, user_perm_group_api, user_perm_group_hosts_api, get_user_host -from jumpserver.api import AssetAlias, get_connect_item +from jumpserver.api import CONF, BASE_DIR, ServerError, Juser +from jumpserver.api import AssetAlias, get_connect_item, logger try: import termios @@ -41,6 +41,7 @@ except ImportError: CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) log_dir = os.path.join(BASE_DIR, 'logs') login_name = getpass.getuser() +user = Juser(username=login_name) def color_print(msg, color='blue'): @@ -177,52 +178,80 @@ def posix_shell(chan, username, host): log.log_finished = False log.end_time = datetime.datetime.now() log.save() - # print_prompt() -def get_user_host_group(username): - """ - Get the host groups of under the user control. - 获取用户有权限的主机组 - """ - groups_attr = {} - group_all = user_perm_group_api(username) - for group in group_all: - groups_attr[group.name] = [group.id, group.comment] - return groups_attr +# def get_user_host_group(username): +# """ +# Get the host groups of under the user control. +# 获取用户有权限的主机组 +# """ +# groups_attr = {} +# group_all = get_host_groups(username) +# for group in group_all: +# groups_attr[group.name] = [group.id, group.comment] +# return groups_attr -def get_host_group_host(username, gid): - """ - Get the host group hosts of under the user control. - 获取用户有权限主机组下的主机 - """ - groups_attr = get_user_host_group(username) - groups_ids = [attr[0] for name, attr in groups_attr.items()] - hosts_attr = {} - if gid in groups_ids: - user = User.objects.filter(username=username) - if user: - user = user[0] - hosts = user_perm_group_hosts_api(gid) - for host in hosts: - alias = AssetAlias.objects.filter(user=user, host=host) - if alias and alias[0].alias != '': - hosts_attr[host.ip] = [host.id, host.ip, alias[0].alias] - else: - hosts_attr[host.ip] = [host.id, host.ip, host.comment] - return hosts_attr +# def get_user_host_group_member(username, gid): +# """ +# Get the host group hosts of under the user control. +# 获取用户有权限主机组下的主机 +# """ +# groups_attr = get_user_host_group(username) +# groups_ids = [attr[0] for name, attr in groups_attr.items()] +# hosts_attr = {} +# if int(gid) in groups_ids: +# user = User.objects.filter(username=username) +# if user: +# user = user[0] +# hosts = get_host_groups(gid) +# for host in hosts: +# alias = AssetAlias.objects.filter(user=user, host=host) +# if alias and alias[0].alias != '': +# hosts_attr[host.ip] = [host.id, host.ip, alias[0].alias] +# else: +# hosts_attr[host.ip] = [host.id, host.ip, host.comment] +# return hosts_attr + + +# def user_asset_info(user, printable=False): +# """ +# Get or Print asset info +# 获取或打印用户资产信息 +# """ +# assets_info = {} +# try: +# assets = get_asset(user) +# except ServerError, e: +# color_print(e, 'red') +# return +# +# for asset in assets: +# asset_alias = AssetAlias.objects.filter(user=user, asset=asset) +# if asset_alias and asset_alias[0].alias != '': +# assets_info[asset.ip] = [asset.id, asset.ip, asset_alias[0].alias] +# else: +# assets_info[asset.ip] = [asset.id, asset.ip, asset.comment] +# +# if printable: +# ips = assets_info.keys() +# ips.sort() +# for ip in ips: +# print '%-15s -- %s' % (ip, assets_info[ip][2]) +# print '' +# else: +# return assets_info def verify_connect(username, part_ip): ip_matched = [] try: - hosts_attr = get_user_host(username) - hosts = hosts_attr.values() + assets = get_asset(username=username) except ServerError, e: color_print(e, 'red') return False + assets_info = for ip_info in hosts: if part_ip in ip_info[1:] and part_ip: ip_matched = [ip_info[1]] @@ -254,29 +283,16 @@ def print_prompt(): print textwrap.dedent(msg) -def print_user_host(username): - try: - hosts_attr = get_user_host(username) - except ServerError, e: - color_print(e, 'red') - return - hosts = hosts_attr.keys() - hosts.sort() - for ip in hosts: - print '%-15s -- %s' % (ip, hosts_attr[ip][2]) - print '' +# def print_user_host_group(username): +# host_groups = get_host_groups(username) +# for host_group in host_groups: +# print "[%3s] %s -- %s" % (host_group.id, host_group.ip, host_group.comment) -def print_user_hostgroup(username): - group_attr = get_user_host_group(username) - groups = group_attr.keys() - for g in groups: - print "[%3s] %s -- %s" % (group_attr[g][0], g, group_attr[g][1]) - - -def print_user_hostgroup_host(username, gid): +def asset_group_member(username, gid): pattern = re.compile(r'\d+') match = pattern.match(gid) + if match: hosts_attr = get_host_group_host(username, gid) hosts = hosts_attr.keys() @@ -397,6 +413,9 @@ def exec_cmd_servers(username): if __name__ == '__main__': + if not user.validate(): + color_print_exit(u'没有该用户 No that user.') + print_prompt() gid_pattern = re.compile(r'^g\d+$') try: @@ -409,10 +428,10 @@ if __name__ == '__main__': except KeyboardInterrupt: sys.exit(0) if option in ['P', 'p']: - print_user_host(login_name) + user.get_asset_info(printable=True) continue elif option in ['G', 'g']: - print_user_hostgroup(login_name) + user.get_asset_group_info(printable=True) continue elif gid_pattern.match(option): gid = option[1:].strip() diff --git a/jasset/models.py b/jasset/models.py index 416c4ddfa..cc3e0399b 100644 --- a/jasset/models.py +++ b/jasset/models.py @@ -47,7 +47,7 @@ class Asset(models.Model): class AssetAlias(models.Model): user = models.ForeignKey(User) - host = models.ForeignKey(Asset) + asset = models.ForeignKey(Asset) alias = models.CharField(max_length=100, blank=True, null=True) def __unicode__(self): diff --git a/jlog/views.py b/jlog/views.py index 29fc3e38d..0eb74f815 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -76,7 +76,10 @@ def log_kill(request): deptname = get_session_user_info(request)[4] if is_group_admin(request) and dept_name != deptname: return httperror(request, u'Kill失败, 您无权操作!') - os.kill(int(pid), 9) + try: + os.kill(int(pid), 9) + except OSError: + pass Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.datetime.now()) return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request)) else: diff --git a/jumpserver.conf b/jumpserver.conf index c4d294679..306da1e77 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -4,6 +4,7 @@ ip = 192.168.20.209 port = 80 key = 88aaaf7ffe3c6c04 +log = debug [db] diff --git a/jumpserver/api.py b/jumpserver/api.py index fa98cc350..3f23ff69a 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -23,20 +23,32 @@ from jasset.models import AssetAlias from django.core.exceptions import ObjectDoesNotExist from django.core.mail import send_mail import json +import logging BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) CONF = ConfigParser() CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) LOG_DIR = os.path.join(BASE_DIR, 'logs') +JLOG_FILE = os.path.join(LOG_DIR, 'jumpserver.log') SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys') -SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') +# SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') KEY = CONF.get('base', 'key') LOGIN_NAME = getpass.getuser() LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') SEND_IP = CONF.get('base', 'ip') SEND_PORT = CONF.get('base', 'port') MAIL_FROM = CONF.get('mail', 'email_host_user') +log_level = CONF.get('base', 'log') +log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR, + 'critical': logging.CRITICAL} +logger = logging.getLogger('jumpserver') +logger.setLevel(logging.DEBUG) +fh = logging.FileHandler(JLOG_FILE) +fh.setLevel(log_level_total.get(log_level, logging.DEBUG)) +formatter = logging.Formatter('%(asctime)s - %(filename)s - %(levelname)s - %(message)s') +fh.setFormatter(formatter) +logger.addHandler(fh) class LDAPMgmt(): @@ -87,14 +99,6 @@ class LDAPMgmt(): except ldap.LDAPError, e: print e - def decrypt(self, text): - cryptor = AES.new(self.key, self.mode, b'0000000000000000') - try: - plain_text = cryptor.decrypt(a2b_hex(text)) - except TypeError: - raise ServerError('Decrypt password error, TYpe error.') - return plain_text.rstrip('\0') - if LDAP_ENABLE: LDAP_HOST_URL = CONF.get('ldap', 'host_url') @@ -292,40 +296,199 @@ def user_group_perm_asset_group_api(user_group): return asset_group_list -def user_perm_group_api(username): - if username: - user = User.objects.get(username=username) +class Juser(object): + def __init__(self, username=None, uid=None): + if username: + user = User.objects.filter(username=username) + elif uid: + user = User.objects.filter(id=uid) + else: + user = '' + + if user: + user = user[0] + self.user = user + self.id = user.id + self.username = user.username + self.name = user.name + self.group = user.group.all() + + def validate(self): + """ + Validate is or not a true user + 鉴定用户 + """ + if self.user: + return True + else: + return False + + def get_asset_group(self): + """ + Get user host_groups. + 获取用户有权限的主机组 + """ + host_group_list = [] perm_list = [] - user_group_all = user.group.all() + user_group_all = self.user.group.all() for user_group in user_group_all: perm_list.extend(user_group.perm_set.all()) - asset_group_list = [] for perm in perm_list: - asset_group_list.append(perm.asset_group) - return asset_group_list + host_group_list.append(perm.asset_group) + + return host_group_list + + def get_asset_group_info(self, printable=False): + """ + Get or print asset group info + 获取或打印用户授权资产组 + """ + asset_groups_info = {} + asset_groups = self.get_asset_group() + + for asset_group in asset_groups: + asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] + + if printable: + for group_id in asset_groups_info: + if asset_groups_info[group_id][1]: + print "[%3s] %s -- %s" % (group_id, + asset_groups_info[group_id][0], + asset_groups_info[group_id][1]) + else: + print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) + else: + return asset_groups_info + + def get_asset(self): + """ + Get the hosts of under the user control. + 获取主机列表 + """ + hosts = [] + host_groups = self.get_asset_group() + + for host_group in host_groups: + hosts.extend(get_asset_group_member(host_group.id)) + + return hosts + + def get_asset_info(self, printable=False): + """ + Get or print the user asset info + 获取或打印用户资产信息 + """ + assets_info = {} + assets = self.get_asset() + + for asset in assets: + asset_alias = AssetAlias.objects.filter(user=self.user, asset=asset) + if asset_alias and asset_alias[0].alias != '': + assets_info[asset.ip] = [asset.id, asset.ip, asset_alias[0].alias] + else: + assets_info[asset.ip] = [asset.id, asset.ip, asset.comment] + + if printable: + ips = assets_info.keys() + ips.sort() + for ip in ips: + if assets_info[ip][2]: + print '%-15s -- %s' % (ip, assets_info[ip][2]) + else: + print '%-15s' % ip + print '' + else: + return assets_info -def user_perm_group_hosts_api(gid): - hostgroup = BisGroup.objects.filter(id=gid) - if hostgroup: - return hostgroup[0].asset_set.all() - else: - return [] +class Jasset(object): + def __init__(self, ip=None, id=None): + if ip: + asset = Asset.objects.filter(ip=ip) + elif id: + asset = Asset.objects.filter(id=id) + else: + asset = '' + + if asset: + asset = asset[0] + self.asset = asset + self.ip = asset.ip + self.id = asset.id + self.port = asset.port + self.comment = asset.comment + + def validate(self): + if self.asset: + return True + else: + return False -def user_perm_asset_api(username): - user = User.objects.filter(username=username) - if user: - user = user[0] - asset_list = [] - asset_group_list = user_perm_group_api(user) - for asset_group in asset_group_list: - asset_list.extend(asset_group.asset_set.all()) - asset_list = list(set(asset_list)) - return asset_list - else: - return [] +class JassetGroup(object): + pass + + + +# def get_asset_group(user=None): +# """ +# Get user host_groups. +# 获取用户有权限的主机组 +# """ +# host_group_list = [] +# if user: +# user = user[0] +# perm_list = [] +# user_group_all = user.group.all() +# for user_group in user_group_all: +# perm_list.extend(user_group.perm_set.all()) +# +# host_group_list = [] +# for perm in perm_list: +# host_group_list.append(perm.asset_group) +# return host_group_list + + +def get_asset_group_member(gid): + """ + Get host_group's member host + 获取主机组下的主机 + """ + hosts = [] + if gid: + host_group = BisGroup.objects.filter(id=gid) + if host_group: + host_group = host_group[0] + hosts = host_group.asset_set.all() + return hosts + + +# def get_asset(user=None): +# """ +# Get the hosts of under the user control. +# 获取主机列表 +# """ +# hosts = [] +# if user: +# host_groups = get_asset_group(user) +# for host_group in host_groups: +# hosts.extend(get_asset_group_member(host_group.id)) +# return hosts + + +# def user_perm_asset_api(username): +# user = User.objects.filter(username=username) +# if user: +# user = user[0] +# asset_list = [] +# asset_group_list = user_perm_group_api(user) +# for asset_group in asset_group_list: +# asset_list.extend(asset_group.asset_set.all()) +# asset_list = list(set(asset_list)) +# return asset_list +# else: +# return [] def asset_perm_api(asset): @@ -346,24 +509,6 @@ def asset_perm_api(asset): return user_permed_list -def get_user_host(username): - """Get the hosts of under the user control.""" - hosts_attr = {} - asset_all = user_perm_asset_api(username) - user = User.objects.filter(username=username) - if user: - user = user[0] - for asset in asset_all: - alias = AssetAlias.objects.filter(user=user, host=asset) - if alias and alias[0].alias != '': - hosts_attr[asset.ip] = [asset.id, asset.ip, alias[0].alias] - else: - hosts_attr[asset.ip] = [asset.id, asset.ip, asset.comment] - return hosts_attr - else: - raise ServerError('User %s does not exit!' % username) - - def get_connect_item(username, ip): asset = get_object(Asset, ip=ip) port = int(asset.port) From 5d16836b053cef623fa6d9ae5582f8d83a578f3c Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 10 Jun 2015 23:16:24 +0800 Subject: [PATCH 003/385] modify connect.py --- connect.py | 86 ++++++++++++++++++++++++----------------------- jumpserver/api.py | 36 +++++++++++++++++++- 2 files changed, 79 insertions(+), 43 deletions(-) diff --git a/connect.py b/connect.py index d25d207d0..e6444f098 100644 --- a/connect.py +++ b/connect.py @@ -27,7 +27,7 @@ if django.get_version() != '1.6': django.setup() from juser.models import User from jlog.models import Log -from jumpserver.api import CONF, BASE_DIR, ServerError, Juser +from jumpserver.api import CONF, BASE_DIR, ServerError, Juser, JassetGroup from jumpserver.api import AssetAlias, get_connect_item, logger try: @@ -244,31 +244,32 @@ def posix_shell(chan, username, host): def verify_connect(username, part_ip): - ip_matched = [] - try: - assets = get_asset(username=username) - except ServerError, e: - color_print(e, 'red') - return False - - assets_info = - for ip_info in hosts: - if part_ip in ip_info[1:] and part_ip: - ip_matched = [ip_info[1]] - break - for info in ip_info[1:]: - if part_ip in info: - ip_matched.append(ip_info[1]) - - ip_matched = list(set(ip_matched)) - if len(ip_matched) > 1: - for ip in ip_matched: - print '%-15s -- %s' % (ip, hosts_attr[ip][2]) - elif len(ip_matched) < 1: - color_print('No Permission or No host.', 'red') - else: - username, password, host, port = get_connect_item(username, ip_matched[0]) - connect(username, password, host, port, login_name) + pass + # ip_matched = [] + # try: + # assets = get_asset(username=username) + # except ServerError, e: + # color_print(e, 'red') + # return False + # + # assets_info = + # for ip_info in hosts: + # if part_ip in ip_info[1:] and part_ip: + # ip_matched = [ip_info[1]] + # break + # for info in ip_info[1:]: + # if part_ip in info: + # ip_matched.append(ip_info[1]) + # + # ip_matched = list(set(ip_matched)) + # if len(ip_matched) > 1: + # for ip in ip_matched: + # print '%-15s -- %s' % (ip, hosts_attr[ip][2]) + # elif len(ip_matched) < 1: + # color_print('No Permission or No host.', 'red') + # else: + # username, password, host, port = get_connect_item(username, ip_matched[0]) + # connect(username, password, host, port, login_name) def print_prompt(): @@ -289,18 +290,18 @@ def print_prompt(): # print "[%3s] %s -- %s" % (host_group.id, host_group.ip, host_group.comment) -def asset_group_member(username, gid): - pattern = re.compile(r'\d+') - match = pattern.match(gid) - - if match: - hosts_attr = get_host_group_host(username, gid) - hosts = hosts_attr.keys() - hosts.sort() - for ip in hosts: - print '%-15s -- %s' % (ip, hosts_attr[ip][2]) - else: - color_print('No such group id, Please check it.', 'red') +# def asset_group_member(username, gid): +# pattern = re.compile(r'\d+') +# match = pattern.match(gid) +# +# if match: +# hosts_attr = get_host_group_host(username, gid) +# hosts = hosts_attr.keys() +# hosts.sort() +# for ip in hosts: +# print '%-15s -- %s' % (ip, hosts_attr[ip][2]) +# else: +# color_print('No such group id, Please check it.', 'red') def connect(username, password, host, port, login_name): @@ -373,13 +374,13 @@ def multi_remote_exec_cmd(hosts, username, cmd): def exec_cmd_servers(username): color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green') - print_user_host(login_name) + user.get_asset_info(printable=True) while True: hosts = [] inputs = raw_input('\033[1;32mip(s)>: \033[0m') if inputs in ['q', 'Q']: break - get_hosts = get_user_host(username).keys() + get_hosts = user.get_asset_info().keys() if ',' in inputs: ips_input = inputs.split(',') @@ -423,7 +424,7 @@ if __name__ == '__main__': try: option = raw_input("\033[1;32mOpt or IP>:\033[0m ") except EOFError: - print + print_prompt() continue except KeyboardInterrupt: sys.exit(0) @@ -435,7 +436,8 @@ if __name__ == '__main__': continue elif gid_pattern.match(option): gid = option[1:].strip() - print_user_hostgroup_host(login_name, gid) + asset_group = JassetGroup(id=gid) + asset_group.get_asset_info(printable=True) continue elif option in ['E', 'e']: exec_cmd_servers(login_name) diff --git a/jumpserver/api.py b/jumpserver/api.py index 3f23ff69a..004d03415 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -297,6 +297,10 @@ def user_group_perm_asset_group_api(user_group): class Juser(object): + """ + Jumpserver user class + 用户类 + """ def __init__(self, username=None, uid=None): if username: user = User.objects.filter(username=username) @@ -427,7 +431,37 @@ class Jasset(object): class JassetGroup(object): - pass + def __init__(self, id=None, name=None): + if id: + asset_group = BisGroup.objects.filter(id=int(id)) + elif name: + asset_group = BisGroup.objects.filter(name=name) + else: + asset_group = '' + + if asset_group: + asset_group = asset_group[0] + self.asset_group = asset_group + self.name = asset_group.name + self.id = asset_group.id + + def validate(self): + if self.asset_group: + return True + else: + return False + + def get_asset(self): + return self.asset_group.asset_set.all() + + def get_asset_info(self, printable=False): + assets = self.get_asset() + for asset in assets: + print '%-15s -- %s' % (asset.ip, asset.comment) + + def get_asset_num(self): + return len(self.get_asset()) + From fdcaa358e5c5ba54f356d7f1c90fea7fc5098aa7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 13 Jun 2015 00:36:10 +0800 Subject: [PATCH 004/385] modify some api.py bug --- connect.py | 60 ++++++++++++----------- jumpserver/api.py | 118 +++++++++++++++++++++++++++++++++------------- 2 files changed, 118 insertions(+), 60 deletions(-) diff --git a/connect.py b/connect.py index e6444f098..f8f1352f4 100644 --- a/connect.py +++ b/connect.py @@ -244,32 +244,37 @@ def posix_shell(chan, username, host): def verify_connect(username, part_ip): - pass - # ip_matched = [] - # try: - # assets = get_asset(username=username) - # except ServerError, e: - # color_print(e, 'red') - # return False - # - # assets_info = - # for ip_info in hosts: - # if part_ip in ip_info[1:] and part_ip: - # ip_matched = [ip_info[1]] - # break - # for info in ip_info[1:]: - # if part_ip in info: - # ip_matched.append(ip_info[1]) - # - # ip_matched = list(set(ip_matched)) - # if len(ip_matched) > 1: - # for ip in ip_matched: - # print '%-15s -- %s' % (ip, hosts_attr[ip][2]) - # elif len(ip_matched) < 1: - # color_print('No Permission or No host.', 'red') - # else: - # username, password, host, port = get_connect_item(username, ip_matched[0]) - # connect(username, password, host, port, login_name) + ip_matched = [] + try: + assets_info = user.get_asset_info() + except ServerError, e: + color_print(e, 'red') + return False + + for ip, asset_info in assets_info.items(): + if part_ip in asset_info[1:] and part_ip: + ip_matched = [asset_info[1]] + break + + for info in asset_info[1:]: + if part_ip in info: + ip_matched.append(ip) + + logger.debug('%s matched input %s: %s' % (user.username, part_ip, ip_matched)) + ip_matched = list(set(ip_matched)) + + if len(ip_matched) > 1: + for ip in ip_matched: + if assets_info[ip][2]: + print '%-15s -- %s' % (ip, assets_info[ip][2]) + else: + print '%-15s' % ip + print '' + elif len(ip_matched) < 1: + color_print('No Permission or No host.', 'red') + else: + username, password, host, port = get_connect_item(username, ip_matched[0]) + connect(username, password, host, port, login_name) def print_prompt(): @@ -437,7 +442,8 @@ if __name__ == '__main__': elif gid_pattern.match(option): gid = option[1:].strip() asset_group = JassetGroup(id=gid) - asset_group.get_asset_info(printable=True) + if asset_group.validate(): + asset_group.get_asset_info(printable=True) continue elif option in ['E', 'e']: exec_cmd_servers(login_name) diff --git a/jumpserver/api.py b/jumpserver/api.py index 004d03415..faebd2843 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -301,6 +301,7 @@ class Juser(object): Jumpserver user class 用户类 """ + def __init__(self, username=None, uid=None): if username: user = User.objects.filter(username=username) @@ -313,16 +314,31 @@ class Juser(object): user = user[0] self.user = user self.id = user.id - self.username = user.username - self.name = user.name + # self.id = user.id + # self.username = user.username + # self.name = user.name self.group = user.group.all() + else: + self.id = None + + def __repr__(self): + if self.id: + return '<%s Juser instance>' % getattr(self.user, 'username') + else: + return 'None' + + def __getattr__(self, item): + if self.id: + return getattr(self.user, item) + else: + return None def validate(self): """ Validate is or not a true user 鉴定用户 """ - if self.user: + if self.id: return True else: return False @@ -362,21 +378,22 @@ class Juser(object): asset_groups_info[group_id][1]) else: print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) + print '' else: return asset_groups_info def get_asset(self): """ - Get the hosts of under the user control. + Get the assets of under the user control. 获取主机列表 """ - hosts = [] - host_groups = self.get_asset_group() + assets = [] + asset_groups = self.get_asset_group() - for host_group in host_groups: - hosts.extend(get_asset_group_member(host_group.id)) + for asset_group in asset_groups: + assets.extend(asset_group.asset_set.all()) - return hosts + return assets def get_asset_info(self, printable=False): """ @@ -389,9 +406,9 @@ class Juser(object): for asset in assets: asset_alias = AssetAlias.objects.filter(user=self.user, asset=asset) if asset_alias and asset_alias[0].alias != '': - assets_info[asset.ip] = [asset.id, asset.ip, asset_alias[0].alias] + assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] else: - assets_info[asset.ip] = [asset.id, asset.ip, asset.comment] + assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] if printable: ips = assets_info.keys() @@ -407,6 +424,7 @@ class Juser(object): class Jasset(object): + def __init__(self, ip=None, id=None): if ip: asset = Asset.objects.filter(ip=ip) @@ -418,20 +436,40 @@ class Jasset(object): if asset: asset = asset[0] self.asset = asset - self.ip = asset.ip self.id = asset.id - self.port = asset.port - self.comment = asset.comment + # self.ip = asset.ip + # self.id = asset.id + # self.port = asset.port + # self.comment = asset.comment + else: + self.id = None + + def __repr__(self): + if self.id: + return '<%s Jasset instance>' % self.asset.ip + else: + return 'None' + + def __getattr__(self, item): + if self.id: + return getattr(self.asset, item) + else: + return None def validate(self): - if self.asset: + """ + Validate is or not a true asset + 判断是否存在 + """ + if self.id: return True else: return False class JassetGroup(object): - def __init__(self, id=None, name=None): + + def __init__(self, name=None, id=None): if id: asset_group = BisGroup.objects.filter(id=int(id)) elif name: @@ -442,11 +480,23 @@ class JassetGroup(object): if asset_group: asset_group = asset_group[0] self.asset_group = asset_group - self.name = asset_group.name + # self.name = asset_group.name self.id = asset_group.id + else: + self.id = None + + def __repr__(self): + if self.id: + return '<%s JassetGroup instance>' % self.name + else: + return 'None' def validate(self): - if self.asset_group: + """ + Validate it is a true asset group or not + 鉴定是否为真是存在的组 + """ + if self.id: return True else: return False @@ -457,14 +507,16 @@ class JassetGroup(object): def get_asset_info(self, printable=False): assets = self.get_asset() for asset in assets: - print '%-15s -- %s' % (asset.ip, asset.comment) + if asset.comment: + print '%-15s -- %s' % (asset.ip, asset.comment) + else: + print '%-15s' % asset.ip + print '' def get_asset_num(self): return len(self.get_asset()) - - # def get_asset_group(user=None): # """ # Get user host_groups. @@ -484,18 +536,18 @@ class JassetGroup(object): # return host_group_list -def get_asset_group_member(gid): - """ - Get host_group's member host - 获取主机组下的主机 - """ - hosts = [] - if gid: - host_group = BisGroup.objects.filter(id=gid) - if host_group: - host_group = host_group[0] - hosts = host_group.asset_set.all() - return hosts +# def get_asset_group_member(gid): +# """ +# Get host_group's member host +# 获取主机组下的主机 +# """ +# hosts = [] +# if gid: +# host_group = BisGroup.objects.filter(id=gid) +# if host_group: +# host_group = host_group[0] +# hosts = host_group.asset_set.all() +# return hosts # def get_asset(user=None): From 8d167baf46b1e663f69088e26fcd27e80388822c Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 15 Jun 2015 19:20:05 +0800 Subject: [PATCH 005/385] modify some --- connect.py | 297 ++++++++++++++++++---------------------------- jumpserver/api.py | 61 +++++++--- 2 files changed, 158 insertions(+), 200 deletions(-) diff --git a/connect.py b/connect.py index f8f1352f4..46b1da82b 100644 --- a/connect.py +++ b/connect.py @@ -40,11 +40,10 @@ except ImportError: CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) log_dir = os.path.join(BASE_DIR, 'logs') -login_name = getpass.getuser() -user = Juser(username=login_name) +login_user = Juser(username=getpass.getuser()) -def color_print(msg, color='blue'): +def color_print(msg, color='red', exits=False): """ Print colorful string. 颜色打印 @@ -54,199 +53,133 @@ def color_print(msg, color='blue'): 'red': '\033[1;31m%s\033[0m'} print color_msg.get(color, 'blue') % msg + if exits: + time.sleep(2) + sys.exit() -def color_print_exit(msg, color='red'): - """ - Print colorful string and exit. - 颜色打印并推出 - """ - color_print(msg, color=color) - time.sleep(2) - sys.exit() +class Jtty(object): + def __init__(self, chan, user, asset): + self.chan = chan + self.username = user.username + self.ip = asset.ip + @staticmethod + def get_win_size(): + """ + This function use to get the size of the windows! + 获得terminal窗口大小 + """ + if 'TIOCGWINSZ' in dir(termios): + TIOCGWINSZ = termios.TIOCGWINSZ + else: + TIOCGWINSZ = 1074295912L + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] -def get_win_size(): - """ - This function use to get the size of the windows! - 获得terminal窗口大小 - """ - if 'TIOCGWINSZ' in dir(termios): - TIOCGWINSZ = termios.TIOCGWINSZ - else: - TIOCGWINSZ = 1074295912L - s = struct.pack('HHHH', 0, 0, 0, 0) - x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) - return struct.unpack('HHHH', x)[0:2] - - -def set_win_size(sig, data): - """ - This function use to set the window size of the terminal! - 设置terminal窗口大小 - """ - try: - win_size = get_win_size() - channel.resize_pty(height=win_size[0], width=win_size[1]) - except Exception: - pass - - -def log_record(username, host): - """ - Logging user command and output. - 记录用户的日志 - """ - connect_log_dir = os.path.join(log_dir, 'connect') - timestamp_start = int(time.time()) - today = time.strftime('%Y%m%d', time.localtime(timestamp_start)) - time_now = time.strftime('%H%M%S', time.localtime(timestamp_start)) - today_connect_log_dir = os.path.join(connect_log_dir, today) - log_filename = '%s_%s_%s.log' % (username, host, time_now) - log_file_path = os.path.join(today_connect_log_dir, log_filename) - dept = User.objects.filter(username=username) - if dept: - dept = dept[0] - dept_name = dept.name - else: - dept_name = 'None' - - pid = os.getpid() - pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() - remote_ip = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') - - if not os.path.isdir(today_connect_log_dir): + def set_win_size(self, sig, data): + """ + This function use to set the window size of the terminal! + 设置terminal窗口大小 + """ try: - os.makedirs(today_connect_log_dir) - os.chmod(today_connect_log_dir, 0777) - except OSError: - raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, connect_log_dir)) + win_size = self.get_win_size() + self.channel.resize_pty(height=win_size[0], width=win_size[1]) + except Exception: + pass - try: - log_file = open(log_file_path, 'a') - except IOError: - raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) + def log_record(self): + """ + Logging user command and output. + 记录用户的日志 + """ + tty_log_dir = os.path.join(log_dir, 'tty') + timestamp_start = int(time.time()) + date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) + time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) + today_connect_log_dir = os.path.join(tty_log_dir, date_start) + log_filename = '%s_%s_%s.log' % (self.username, self.host, time_start) + log_file_path = os.path.join(today_connect_log_dir, log_filename) + dept = User.objects.filter(username=username) + if dept: + dept = dept[0] + dept_name = dept.name + else: + dept_name = 'None' - log = Log(user=username, host=host, remote_ip=remote_ip, dept_name=dept_name, - log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) - log_file.write('Start time is %s\n' % datetime.datetime.now()) - log.save() - return log_file, log + pid = os.getpid() + pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() + remote_ip = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') - -def posix_shell(chan, username, host): - """ - Use paramiko channel connect server interactive. - 使用paramiko模块的channel,连接后端,进入交互式 - """ - log_file, log = log_record(username, host) - old_tty = termios.tcgetattr(sys.stdin) - try: - tty.setraw(sys.stdin.fileno()) - tty.setcbreak(sys.stdin.fileno()) - chan.settimeout(0.0) - - while True: + if not os.path.isdir(today_connect_log_dir): try: - r, w, e = select.select([chan, sys.stdin], [], []) - except Exception: - pass + os.makedirs(today_connect_log_dir) + os.chmod(today_connect_log_dir, 0777) + except OSError: + raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, connect_log_dir)) - if chan in r: + try: + log_file = open(log_file_path, 'a') + except IOError: + raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) + + log = Log(user=username, host=host, remote_ip=remote_ip, dept_name=dept_name, + log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) + log_file.write('Start time is %s\n' % datetime.datetime.now()) + log.save() + return log_file, log + + def posix_shell(chan, username, host): + """ + Use paramiko channel connect server interactive. + 使用paramiko模块的channel,连接后端,进入交互式 + """ + log_file, log = log_record(username, host) + old_tty = termios.tcgetattr(sys.stdin) + try: + tty.setraw(sys.stdin.fileno()) + tty.setcbreak(sys.stdin.fileno()) + chan.settimeout(0.0) + + while True: try: - x = chan.recv(1024) - if len(x) == 0: - break - sys.stdout.write(x) - sys.stdout.flush() - log_file.write(x) - log_file.flush() - except socket.timeout: + r, w, e = select.select([chan, sys.stdin], [], []) + except Exception: pass - if sys.stdin in r: - x = os.read(sys.stdin.fileno(), 1) - if len(x) == 0: - break - chan.send(x) + if chan in r: + try: + x = chan.recv(1024) + if len(x) == 0: + break + sys.stdout.write(x) + sys.stdout.flush() + log_file.write(x) + log_file.flush() + except socket.timeout: + pass - finally: - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - log_file.write('End time is %s' % datetime.datetime.now()) - log_file.close() - log.is_finished = True - log.log_finished = False - log.end_time = datetime.datetime.now() - log.save() + if sys.stdin in r: + x = os.read(sys.stdin.fileno(), 1) + if len(x) == 0: + break + chan.send(x) - -# def get_user_host_group(username): -# """ -# Get the host groups of under the user control. -# 获取用户有权限的主机组 -# """ -# groups_attr = {} -# group_all = get_host_groups(username) -# for group in group_all: -# groups_attr[group.name] = [group.id, group.comment] -# return groups_attr - - -# def get_user_host_group_member(username, gid): -# """ -# Get the host group hosts of under the user control. -# 获取用户有权限主机组下的主机 -# """ -# groups_attr = get_user_host_group(username) -# groups_ids = [attr[0] for name, attr in groups_attr.items()] -# hosts_attr = {} -# if int(gid) in groups_ids: -# user = User.objects.filter(username=username) -# if user: -# user = user[0] -# hosts = get_host_groups(gid) -# for host in hosts: -# alias = AssetAlias.objects.filter(user=user, host=host) -# if alias and alias[0].alias != '': -# hosts_attr[host.ip] = [host.id, host.ip, alias[0].alias] -# else: -# hosts_attr[host.ip] = [host.id, host.ip, host.comment] -# return hosts_attr - - -# def user_asset_info(user, printable=False): -# """ -# Get or Print asset info -# 获取或打印用户资产信息 -# """ -# assets_info = {} -# try: -# assets = get_asset(user) -# except ServerError, e: -# color_print(e, 'red') -# return -# -# for asset in assets: -# asset_alias = AssetAlias.objects.filter(user=user, asset=asset) -# if asset_alias and asset_alias[0].alias != '': -# assets_info[asset.ip] = [asset.id, asset.ip, asset_alias[0].alias] -# else: -# assets_info[asset.ip] = [asset.id, asset.ip, asset.comment] -# -# if printable: -# ips = assets_info.keys() -# ips.sort() -# for ip in ips: -# print '%-15s -- %s' % (ip, assets_info[ip][2]) -# print '' -# else: -# return assets_info + finally: + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) + log_file.write('End time is %s' % datetime.datetime.now()) + log_file.close() + log.is_finished = True + log.log_finished = False + log.end_time = datetime.datetime.now() + log.save() def verify_connect(username, part_ip): ip_matched = [] try: - assets_info = user.get_asset_info() + assets_info = login_user.get_asset_info() except ServerError, e: color_print(e, 'red') return False @@ -260,7 +193,7 @@ def verify_connect(username, part_ip): if part_ip in info: ip_matched.append(ip) - logger.debug('%s matched input %s: %s' % (user.username, part_ip, ip_matched)) + logger.debug('%s matched input %s: %s' % (login_user.username, part_ip, ip_matched)) ip_matched = list(set(ip_matched)) if len(ip_matched) > 1: @@ -385,7 +318,7 @@ def exec_cmd_servers(username): inputs = raw_input('\033[1;32mip(s)>: \033[0m') if inputs in ['q', 'Q']: break - get_hosts = user.get_asset_info().keys() + get_hosts = login_user.get_asset_info().keys() if ',' in inputs: ips_input = inputs.split(',') @@ -419,8 +352,8 @@ def exec_cmd_servers(username): if __name__ == '__main__': - if not user.validate(): - color_print_exit(u'没有该用户 No that user.') + if not login_user.validate(): + color_print(u'没有该用户 No that user.', exits=True) print_prompt() gid_pattern = re.compile(r'^g\d+$') @@ -434,10 +367,10 @@ if __name__ == '__main__': except KeyboardInterrupt: sys.exit(0) if option in ['P', 'p']: - user.get_asset_info(printable=True) + login_user.get_asset_info(printable=True) continue elif option in ['G', 'g']: - user.get_asset_group_info(printable=True) + login_user.get_asset_group_info(printable=True) continue elif gid_pattern.match(option): gid = option[1:].strip() diff --git a/jumpserver/api.py b/jumpserver/api.py index faebd2843..68831bcb0 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -12,6 +12,7 @@ import ldap from ldap import modlist import hashlib import datetime +import random import subprocess from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.http import HttpResponse, Http404 @@ -150,34 +151,57 @@ def pages(posts, r): class PyCrypt(object): - """This class used to encrypt and decrypt password.""" + """ + This class used to encrypt and decrypt password. + 对称加密库 + """ def __init__(self, key): self.key = key self.mode = AES.MODE_CBC - def encrypt(self, text): - cryptor = AES.new(self.key, self.mode, b'0000000000000000') - length = 16 + def _random_pass(self): + """ + random password + 随机生成密码 + """ + salt_key = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@$%^&*()_' + symbol = '!@$%^&*()_' + salt_list = [] + for i in range(60): + salt_list.append(random.choice(salt_key)) + for i in range(4): + salt_list.append(random.choice(symbol)) + salt = ''.join(salt_list) + self.salt = salt + + def encrypt(self): + """ + encrypt gen password + 加密生成密码 + """ + cryptor = AES.new(self.key, self.mode, b'8122ca7d906ad5e1') + length = 64 try: - count = len(text) + count = len(self.salt) except TypeError: - raise ServerError('Encrypt password error, TYpe error.') + # raise ServerError('Encrypt password error, TYpe error.') + pass add = (length - (count % length)) - text += ('\0' * add) - ciphertext = cryptor.encrypt(text) - return b2a_hex(ciphertext) + self.salt += ('\0' * add) + cipher_text = cryptor.encrypt(self.salt) + return b2a_hex(cipher_text) def decrypt(self, text): - cryptor = AES.new(self.key, self.mode, b'0000000000000000') + cryptor = AES.new(self.key, self.mode, b'8122ca7d906ad5e1') try: plain_text = cryptor.decrypt(a2b_hex(text)) except TypeError: - raise ServerError('Decrypt password error, TYpe error.') + # raise ServerError('Decrypt password error, TYpe error.') + pass return plain_text.rstrip('\0') -CRYPTOR = PyCrypt(KEY) class ServerError(Exception): @@ -595,17 +619,15 @@ def asset_perm_api(asset): return user_permed_list -def get_connect_item(username, ip): +def get_connect_item(user, ip): asset = get_object(Asset, ip=ip) port = int(asset.port) if not asset.is_active: raise ServerError('Host %s is not active.' % ip) - user = get_object(User, username=username) - if not user.is_active: - raise ServerError('User %s is not active.' % username) + raise ServerError('User %s is not active.' % user.username) login_type_dict = { 'L': user.ldap_pwd, @@ -613,7 +635,7 @@ def get_connect_item(username, ip): if asset.login_type in login_type_dict: password = CRYPTOR.decrypt(login_type_dict[asset.login_type]) - return username, password, ip, port + return user.username, password, ip, port elif asset.login_type == 'M': username = asset.username @@ -749,4 +771,7 @@ def node_auth(request): else: result = {'auth': {'username': username, 'result': 'failed'}} - return HttpResponse(json.dumps(result, sort_keys=True, indent=2), content_type='application/json') \ No newline at end of file + return HttpResponse(json.dumps(result, sort_keys=True, indent=2), content_type='application/json') + + +CRYPTOR = PyCrypt(KEY) \ No newline at end of file From bb32c0480cb488b9bb69e5b4a22b94c9136597a5 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 15 Jun 2015 23:00:19 +0800 Subject: [PATCH 006/385] dev --- connect.py | 290 +++++++++++++++++++++++--------------------- jumpserver/api.py | 199 +++++++++--------------------- jumpserver/views.py | 20 ++- 3 files changed, 230 insertions(+), 279 deletions(-) diff --git a/connect.py b/connect.py index 46b1da82b..b2e941472 100644 --- a/connect.py +++ b/connect.py @@ -25,10 +25,9 @@ from multiprocessing import Pool os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() -from juser.models import User from jlog.models import Log -from jumpserver.api import CONF, BASE_DIR, ServerError, Juser, JassetGroup -from jumpserver.api import AssetAlias, get_connect_item, logger +from jumpserver.api import CONF, BASE_DIR, ServerError, Juser, Jasset, JassetGroup +from jumpserver.api import CRYPTOR, logger, is_dir try: import termios @@ -63,6 +62,8 @@ class Jtty(object): self.chan = chan self.username = user.username self.ip = asset.ip + self.user = user + self.asset = asset @staticmethod def get_win_size(): @@ -99,58 +100,51 @@ class Jtty(object): date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) today_connect_log_dir = os.path.join(tty_log_dir, date_start) - log_filename = '%s_%s_%s.log' % (self.username, self.host, time_start) + log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start) log_file_path = os.path.join(today_connect_log_dir, log_filename) - dept = User.objects.filter(username=username) - if dept: - dept = dept[0] - dept_name = dept.name - else: - dept_name = 'None' + dept_name = self.user.dept.name pid = os.getpid() pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() remote_ip = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') - if not os.path.isdir(today_connect_log_dir): - try: - os.makedirs(today_connect_log_dir) - os.chmod(today_connect_log_dir, 0777) - except OSError: - raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, connect_log_dir)) + try: + is_dir(today_connect_log_dir) + except OSError: + raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) try: log_file = open(log_file_path, 'a') except IOError: raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - log = Log(user=username, host=host, remote_ip=remote_ip, dept_name=dept_name, + log = Log(user=self.username, host=self.ip, remote_ip=remote_ip, dept_name=dept_name, log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) log_file.write('Start time is %s\n' % datetime.datetime.now()) log.save() return log_file, log - def posix_shell(chan, username, host): + def posix_shell(self): """ Use paramiko channel connect server interactive. 使用paramiko模块的channel,连接后端,进入交互式 """ - log_file, log = log_record(username, host) + log_file, log = self.log_record() old_tty = termios.tcgetattr(sys.stdin) try: tty.setraw(sys.stdin.fileno()) tty.setcbreak(sys.stdin.fileno()) - chan.settimeout(0.0) + self.chan.settimeout(0.0) while True: try: - r, w, e = select.select([chan, sys.stdin], [], []) + r, w, e = select.select([self.chan, sys.stdin], [], []) except Exception: pass - if chan in r: + if self.chan in r: try: - x = chan.recv(1024) + x = self.chan.recv(1024) if len(x) == 0: break sys.stdout.write(x) @@ -164,7 +158,7 @@ class Jtty(object): x = os.read(sys.stdin.fileno(), 1) if len(x) == 0: break - chan.send(x) + self.chan.send(x) finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) @@ -175,8 +169,72 @@ class Jtty(object): log.end_time = datetime.datetime.now() log.save() + def get_connect_item(self): + port = int(self.asset.port) -def verify_connect(username, part_ip): + if not self.asset.is_active: + raise ServerError('Host %s is not active.' % self.ip) + + if not self.user.is_active: + raise ServerError('User %s is not active.' % self.username) + + login_type_dict = { + 'L': self.user.ldap_pwd, + } + + if self.asset.login_type in login_type_dict: + password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type]) + return self.username, password, self.ip, port + + elif self.asset.login_type == 'M': + username = self.asset.username + password = CRYPTOR.decrypt(self.asset.password) + return username, password, self.ip, port + + else: + raise ServerError('Login type is not in ["L", "M"]') + + def connect(self): + """ + Connect server. + """ + username, password, ip, port = self.get_connect_item() + ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip + + # Make a ssh connection + ssh = paramiko.SSHClient() + ssh.load_system_host_keys() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(ip, port=port, username=username, password=password, compress=True) + except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: + raise ServerError('Authentication Error.') + except socket.error: + raise ServerError('Connect SSH Socket Port Error, Please Correct it.') + + # Make a channel and set windows size + global channel + win_size = self.get_win_size() + channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) + try: + signal.signal(signal.SIGWINCH, self.set_win_size) + except: + pass + + # Set PS1 and msg it + channel.send(ps1) + channel.send(login_msg) + + # Make ssh interactive tunnel + self.posix_shell() + + # Shutdown channel socket + channel.close() + ssh.close() + + +def verify_connect(user, option): ip_matched = [] try: assets_info = login_user.get_asset_info() @@ -185,15 +243,15 @@ def verify_connect(username, part_ip): return False for ip, asset_info in assets_info.items(): - if part_ip in asset_info[1:] and part_ip: + if option in asset_info[1:] and option: ip_matched = [asset_info[1]] break for info in asset_info[1:]: - if part_ip in info: + if option in info: ip_matched.append(ip) - logger.debug('%s matched input %s: %s' % (login_user.username, part_ip, ip_matched)) + logger.debug('%s matched input %s: %s' % (login_user.username, option, ip_matched)) ip_matched = list(set(ip_matched)) if len(ip_matched) > 1: @@ -206,8 +264,8 @@ def verify_connect(username, part_ip): elif len(ip_matched) < 1: color_print('No Permission or No host.', 'red') else: - username, password, host, port = get_connect_item(username, ip_matched[0]) - connect(username, password, host, port, login_name) + asset = Jasset(ip=ip_matched[0]) + jtty = Jtty(chan, user, ) def print_prompt(): @@ -242,113 +300,74 @@ def print_prompt(): # color_print('No such group id, Please check it.', 'red') -def connect(username, password, host, port, login_name): - """ - Connect server. - """ - ps1 = "PS1='[\u@%s \W]\$ '\n" % host - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % host - - # Make a ssh connection - ssh = paramiko.SSHClient() - ssh.load_system_host_keys() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - try: - ssh.connect(host, port=port, username=username, password=password, compress=True) - except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: - raise ServerError('Authentication Error.') - except socket.error: - raise ServerError('Connect SSH Socket Port Error, Please Correct it.') - - # Make a channel and set windows size - global channel - win_size = get_win_size() - channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) - try: - signal.signal(signal.SIGWINCH, set_win_size) - except: - pass - - # Set PS1 and msg it - channel.send(ps1) - channel.send(login_msg) - - # Make ssh interactive tunnel - posix_shell(channel, login_name, host) - - # Shutdown channel socket - channel.close() - ssh.close() +# def remote_exec_cmd(ip, port, username, password, cmd): +# try: +# time.sleep(5) +# ssh = paramiko.SSHClient() +# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +# ssh.connect(ip, port, username, password, timeout=5) +# stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd) +# out = stdout.readlines() +# err = stderr.readlines() +# color_print('%s:' % ip, 'blue') +# for i in out: +# color_print(" " * 4 + i.strip(), 'green') +# for j in err: +# color_print(" " * 4 + j.strip(), 'red') +# ssh.close() +# except Exception as e: +# color_print(ip + ':', 'blue') +# color_print(str(e), 'red') -def remote_exec_cmd(ip, port, username, password, cmd): - try: - time.sleep(5) - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - ssh.connect(ip, port, username, password, timeout=5) - stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd) - out = stdout.readlines() - err = stderr.readlines() - color_print('%s:' % ip, 'blue') - for i in out: - color_print(" " * 4 + i.strip(), 'green') - for j in err: - color_print(" " * 4 + j.strip(), 'red') - ssh.close() - except Exception as e: - color_print(ip + ':', 'blue') - color_print(str(e), 'red') +# def multi_remote_exec_cmd(hosts, username, cmd): +# pool = Pool(processes=5) +# for host in hosts: +# username, password, ip, port = get_connect_item(username, host) +# pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd)) +# pool.close() +# pool.join() -def multi_remote_exec_cmd(hosts, username, cmd): - pool = Pool(processes=5) - for host in hosts: - username, password, ip, port = get_connect_item(username, host) - pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd)) - pool.close() - pool.join() - - -def exec_cmd_servers(username): - color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green') - user.get_asset_info(printable=True) - while True: - hosts = [] - inputs = raw_input('\033[1;32mip(s)>: \033[0m') - if inputs in ['q', 'Q']: - break - get_hosts = login_user.get_asset_info().keys() - - if ',' in inputs: - ips_input = inputs.split(',') - for host in ips_input: - if host in get_hosts: - hosts.append(host) - else: - for host in get_hosts: - if fnmatch.fnmatch(host, inputs): - hosts.append(host.strip()) - - if len(hosts) == 0: - color_print("Check again, Not matched any ip!", 'red') - continue - else: - print "You matched ip: %s" % hosts - color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green') - while True: - cmd = raw_input('\033[1;32mCmd(s): \033[0m') - if cmd in ['q', 'Q']: - break - exec_log_dir = os.path.join(log_dir, 'exec_cmds') - if not os.path.isdir(exec_log_dir): - os.mkdir(exec_log_dir) - os.chmod(exec_log_dir, 0777) - filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d')) - f = open(filename, 'a') - f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" % - (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd)) - multi_remote_exec_cmd(hosts, username, cmd) +# def exec_cmd_servers(username): +# color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green') +# user.get_asset_info(printable=True) +# while True: +# hosts = [] +# inputs = raw_input('\033[1;32mip(s)>: \033[0m') +# if inputs in ['q', 'Q']: +# break +# get_hosts = login_user.get_asset_info().keys() +# +# if ',' in inputs: +# ips_input = inputs.split(',') +# for host in ips_input: +# if host in get_hosts: +# hosts.append(host) +# else: +# for host in get_hosts: +# if fnmatch.fnmatch(host, inputs): +# hosts.append(host.strip()) +# +# if len(hosts) == 0: +# color_print("Check again, Not matched any ip!", 'red') +# continue +# else: +# print "You matched ip: %s" % hosts +# color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green') +# while True: +# cmd = raw_input('\033[1;32mCmd(s): \033[0m') +# if cmd in ['q', 'Q']: +# break +# exec_log_dir = os.path.join(log_dir, 'exec_cmds') +# if not os.path.isdir(exec_log_dir): +# os.mkdir(exec_log_dir) +# os.chmod(exec_log_dir, 0777) +# filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d')) +# f = open(filename, 'a') +# f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" % +# (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd)) +# multi_remote_exec_cmd(hosts, username, cmd) if __name__ == '__main__': @@ -379,12 +398,13 @@ if __name__ == '__main__': asset_group.get_asset_info(printable=True) continue elif option in ['E', 'e']: - exec_cmd_servers(login_name) + # exec_cmd_servers(login_name) + pass elif option in ['Q', 'q', 'exit']: sys.exit() else: try: - verify_connect(login_name, option) + verify_connect(login_user, option) except ServerError, e: color_print(e, 'red') except IndexError: diff --git a/jumpserver/api.py b/jumpserver/api.py index 68831bcb0..5066bca8c 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -101,20 +101,6 @@ class LDAPMgmt(): print e -if LDAP_ENABLE: - LDAP_HOST_URL = CONF.get('ldap', 'host_url') - LDAP_BASE_DN = CONF.get('ldap', 'base_dn') - LDAP_ROOT_DN = CONF.get('ldap', 'root_dn') - LDAP_ROOT_PW = CONF.get('ldap', 'root_pw') - ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) -else: - ldap_conn = None - - -def md5_crypt(string): - return hashlib.new("md5", string).hexdigest() - - def page_list_return(total, current=1): min_page = current - 2 if current - 4 > 0 else 1 max_page = min_page + 4 if min_page + 4 < total else total @@ -160,7 +146,8 @@ class PyCrypt(object): self.key = key self.mode = AES.MODE_CBC - def _random_pass(self): + @staticmethod + def random_pass(): """ random password 随机生成密码 @@ -173,23 +160,30 @@ class PyCrypt(object): for i in range(4): salt_list.append(random.choice(symbol)) salt = ''.join(salt_list) - self.salt = salt + return salt - def encrypt(self): + @staticmethod + def md5_crypt(string): + return hashlib.new("md5", string).hexdigest() + + def encrypt(self, passwd=None): """ encrypt gen password 加密生成密码 """ + if not passwd: + passwd = self.random_pass() + cryptor = AES.new(self.key, self.mode, b'8122ca7d906ad5e1') length = 64 try: - count = len(self.salt) + count = len(passwd) except TypeError: - # raise ServerError('Encrypt password error, TYpe error.') - pass + raise ServerError('Encrypt password error, TYpe error.') + add = (length - (count % length)) - self.salt += ('\0' * add) - cipher_text = cryptor.encrypt(self.salt) + passwd += ('\0' * add) + cipher_text = cryptor.encrypt(passwd) return b2a_hex(cipher_text) def decrypt(self, text): @@ -202,8 +196,6 @@ class PyCrypt(object): return plain_text.rstrip('\0') - - class ServerError(Exception): pass @@ -490,6 +482,22 @@ class Jasset(object): else: return False + def get__user(self): + perm_list = [] + asset_group_all = self.asset.bis_group.all() + for asset_group in asset_group_all: + perm_list.extend(asset_group.perm_set.all()) + + user_group_list = [] + for perm in perm_list: + user_group_list.append(perm.user_group) + + user_permed_list = [] + for user_group in user_group_list: + user_permed_list.extend(user_group.user_set.all()) + user_permed_list = list(set(user_permed_list)) + return user_permed_list + class JassetGroup(object): @@ -541,109 +549,22 @@ class JassetGroup(object): return len(self.get_asset()) -# def get_asset_group(user=None): -# """ -# Get user host_groups. -# 获取用户有权限的主机组 -# """ -# host_group_list = [] -# if user: -# user = user[0] +# def asset_perm_api(asset): +# if asset: # perm_list = [] -# user_group_all = user.group.all() -# for user_group in user_group_all: -# perm_list.extend(user_group.perm_set.all()) +# asset_group_all = asset.bis_group.all() +# for asset_group in asset_group_all: +# perm_list.extend(asset_group.perm_set.all()) # -# host_group_list = [] +# user_group_list = [] # for perm in perm_list: -# host_group_list.append(perm.asset_group) -# return host_group_list - - -# def get_asset_group_member(gid): -# """ -# Get host_group's member host -# 获取主机组下的主机 -# """ -# hosts = [] -# if gid: -# host_group = BisGroup.objects.filter(id=gid) -# if host_group: -# host_group = host_group[0] -# hosts = host_group.asset_set.all() -# return hosts - - -# def get_asset(user=None): -# """ -# Get the hosts of under the user control. -# 获取主机列表 -# """ -# hosts = [] -# if user: -# host_groups = get_asset_group(user) -# for host_group in host_groups: -# hosts.extend(get_asset_group_member(host_group.id)) -# return hosts - - -# def user_perm_asset_api(username): -# user = User.objects.filter(username=username) -# if user: -# user = user[0] -# asset_list = [] -# asset_group_list = user_perm_group_api(user) -# for asset_group in asset_group_list: -# asset_list.extend(asset_group.asset_set.all()) -# asset_list = list(set(asset_list)) -# return asset_list -# else: -# return [] - - -def asset_perm_api(asset): - if asset: - perm_list = [] - asset_group_all = asset.bis_group.all() - for asset_group in asset_group_all: - perm_list.extend(asset_group.perm_set.all()) - - user_group_list = [] - for perm in perm_list: - user_group_list.append(perm.user_group) - - user_permed_list = [] - for user_group in user_group_list: - user_permed_list.extend(user_group.user_set.all()) - user_permed_list = list(set(user_permed_list)) - return user_permed_list - - -def get_connect_item(user, ip): - asset = get_object(Asset, ip=ip) - port = int(asset.port) - - if not asset.is_active: - raise ServerError('Host %s is not active.' % ip) - - if not user.is_active: - raise ServerError('User %s is not active.' % user.username) - - login_type_dict = { - 'L': user.ldap_pwd, - } - - if asset.login_type in login_type_dict: - password = CRYPTOR.decrypt(login_type_dict[asset.login_type]) - return user.username, password, ip, port - - elif asset.login_type == 'M': - username = asset.username - password = CRYPTOR.decrypt(asset.password) - return username, password, ip, port - - else: - raise ServerError('Login type is not in ["L", "M"]') +# user_group_list.append(perm.user_group) +# +# user_permed_list = [] +# for user_group in user_group_list: +# user_permed_list.extend(user_group.user_set.all()) +# user_permed_list = list(set(user_permed_list)) +# return user_permed_list def validate(request, user_group=None, user=None, asset_group=None, asset=None, edept=None): @@ -747,31 +668,23 @@ def is_dir(dir_name, username='root', mode=0755): os.chmod(dir_name, mode) -def success(request, msg): +def http_success(request, msg): return render_to_response('success.html', locals()) -def httperror(request, emg): +def http_error(request, emg): message = emg return render_to_response('error.html', locals()) -def node_auth(request): - username = request.POST.get('username', ' ') - seed = request.POST.get('seed', ' ') - filename = request.POST.get('filename', ' ') - user = User.objects.filter(username=username, password=seed) - auth = 1 - if not user: - auth = 0 - if not filename.startswith('/opt/jumpserver/logs/connect/'): - auth = 0 - if auth: - result = {'auth': {'username': username, 'result': 'success'}} - else: - result = {'auth': {'username': username, 'result': 'failed'}} +CRYPTOR = PyCrypt(KEY) - return HttpResponse(json.dumps(result, sort_keys=True, indent=2), content_type='application/json') +if LDAP_ENABLE: + LDAP_HOST_URL = CONF.get('ldap', 'host_url') + LDAP_BASE_DN = CONF.get('ldap', 'base_dn') + LDAP_ROOT_DN = CONF.get('ldap', 'root_dn') + LDAP_ROOT_PW = CONF.get('ldap', 'root_pw') + ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) +else: + ldap_conn = None - -CRYPTOR = PyCrypt(KEY) \ No newline at end of file diff --git a/jumpserver/views.py b/jumpserver/views.py index 31f5b9cfa..fd151d824 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -259,7 +259,7 @@ def install(request): User(id=5000, username="admin", password=md5_crypt('admin'), name='admin', email='admin@jumpserver.org', role='SU', is_active=True, dept=dept).save() - return success(request, u'Jumpserver初始化成功') + return http_success(request, u'Jumpserver初始化成功') def download(request): @@ -327,3 +327,21 @@ def upload(request): return HttpResponse('传送成功') return render_to_response('upload.html', locals(), context_instance=RequestContext(request)) + + +def node_auth(request): + username = request.POST.get('username', ' ') + seed = request.POST.get('seed', ' ') + filename = request.POST.get('filename', ' ') + user = User.objects.filter(username=username, password=seed) + auth = 1 + if not user: + auth = 0 + if not filename.startswith('/opt/jumpserver/logs/connect/'): + auth = 0 + if auth: + result = {'auth': {'username': username, 'result': 'success'}} + else: + result = {'auth': {'username': username, 'result': 'failed'}} + + return HttpResponse(json.dumps(result, sort_keys=True, indent=2), content_type='application/json') From f3a0c390b157065592df83f320c91b3533dd686d Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 16 Jun 2015 09:21:11 +0800 Subject: [PATCH 007/385] connect.py base ok --- connect.py | 36 +++++++++--------------------------- jumpserver/api.py | 40 +++++++++++++++++++++++++++++++++------- juser/models.py | 2 +- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/connect.py b/connect.py index b2e941472..b2da5a934 100644 --- a/connect.py +++ b/connect.py @@ -58,8 +58,8 @@ def color_print(msg, color='red', exits=False): class Jtty(object): - def __init__(self, chan, user, asset): - self.chan = chan + def __init__(self, user, asset): + self.chan = None self.username = user.username self.ip = asset.ip self.user = user @@ -86,7 +86,7 @@ class Jtty(object): """ try: win_size = self.get_win_size() - self.channel.resize_pty(height=win_size[0], width=win_size[1]) + self.chan.resize_pty(height=win_size[0], width=win_size[1]) except Exception: pass @@ -199,6 +199,7 @@ class Jtty(object): Connect server. """ username, password, ip, port = self.get_connect_item() + logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port)) ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip @@ -216,7 +217,7 @@ class Jtty(object): # Make a channel and set windows size global channel win_size = self.get_win_size() - channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) + self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) try: signal.signal(signal.SIGWINCH, self.set_win_size) except: @@ -264,8 +265,9 @@ def verify_connect(user, option): elif len(ip_matched) < 1: color_print('No Permission or No host.', 'red') else: - asset = Jasset(ip=ip_matched[0]) - jtty = Jtty(chan, user, ) + asset = Jasset(ip=ip_matched[0]).asset + jtty = Jtty(user, asset) + jtty.connect() def print_prompt(): @@ -280,26 +282,6 @@ def print_prompt(): print textwrap.dedent(msg) -# def print_user_host_group(username): -# host_groups = get_host_groups(username) -# for host_group in host_groups: -# print "[%3s] %s -- %s" % (host_group.id, host_group.ip, host_group.comment) - - -# def asset_group_member(username, gid): -# pattern = re.compile(r'\d+') -# match = pattern.match(gid) -# -# if match: -# hosts_attr = get_host_group_host(username, gid) -# hosts = hosts_attr.keys() -# hosts.sort() -# for ip in hosts: -# print '%-15s -- %s' % (ip, hosts_attr[ip][2]) -# else: -# color_print('No such group id, Please check it.', 'red') - - # def remote_exec_cmd(ip, port, username, password, cmd): # try: # time.sleep(5) @@ -394,7 +376,7 @@ if __name__ == '__main__': elif gid_pattern.match(option): gid = option[1:].strip() asset_group = JassetGroup(id=gid) - if asset_group.validate(): + if asset_group.validate() and asset_group.is_permed(user=login_user.user): asset_group.get_asset_info(printable=True) continue elif option in ['E', 'e']: diff --git a/jumpserver/api.py b/jumpserver/api.py index 5066bca8c..a431579ee 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -440,7 +440,10 @@ class Juser(object): class Jasset(object): - + """ + Jumpserver asset class + Jumpserver资产类 + """ def __init__(self, ip=None, id=None): if ip: asset = Asset.objects.filter(ip=ip) @@ -453,10 +456,6 @@ class Jasset(object): asset = asset[0] self.asset = asset self.id = asset.id - # self.ip = asset.ip - # self.id = asset.id - # self.port = asset.port - # self.comment = asset.comment else: self.id = None @@ -482,7 +481,7 @@ class Jasset(object): else: return False - def get__user(self): + def get_user(self): perm_list = [] asset_group_all = self.asset.bis_group.all() for asset_group in asset_group_all: @@ -500,7 +499,10 @@ class Jasset(object): class JassetGroup(object): - + """ + Jumpserver AssetGroup class + Jumpserver 资产组类 + """ def __init__(self, name=None, id=None): if id: asset_group = BisGroup.objects.filter(id=int(id)) @@ -548,6 +550,30 @@ class JassetGroup(object): def get_asset_num(self): return len(self.get_asset()) + def get_user_group(self): + perm_list = self.asset_group.perm_set.all() + user_group_list = [] + for perm in perm_list: + user_group_list.append(perm.user_group) + return user_group_list + + def get_user(self): + user_list = [] + user_group_list = self.get_user_group() + for user_group in user_group_list: + user_list.extend(user_group.user_set.all()) + return user_list + + def is_permed(self, user=None, user_group=None): + if user: + if user in self.get_user(): + return True + + if user_group: + if user_group in self.get_user_group(): + return True + return False + # def asset_perm_api(asset): # if asset: diff --git a/juser/models.py b/juser/models.py index d7efd7a28..b309effe4 100644 --- a/juser/models.py +++ b/juser/models.py @@ -31,7 +31,7 @@ class User(models.Model): role = models.CharField(max_length=2, choices=USER_ROLE_CHOICES, default='CU') dept = models.ForeignKey(DEPT) group = models.ManyToManyField(UserGroup) - ldap_pwd = models.CharField(max_length=100) + ldap_pwd = models.CharField(max_length=128) ssh_key_pwd = models.CharField(max_length=100) is_active = models.BooleanField(default=True) last_login = models.DateTimeField(null=True) From 9e52e6a320ca48c31d563a882c9d7778ce1320f8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 3 Jul 2015 20:45:45 +0800 Subject: [PATCH 008/385] modify some --- jumpserver/api.py | 27 ++++++------- juser/views.py | 97 ++++++++++++++++++++++++----------------------- 2 files changed, 64 insertions(+), 60 deletions(-) diff --git a/jumpserver/api.py b/jumpserver/api.py index a431579ee..ca6fae855 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -40,6 +40,7 @@ LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') SEND_IP = CONF.get('base', 'ip') SEND_PORT = CONF.get('base', 'port') MAIL_FROM = CONF.get('mail', 'email_host_user') + log_level = CONF.get('base', 'log') log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR, 'critical': logging.CRITICAL} @@ -295,21 +296,21 @@ def api_user(request): return HttpResponse(json_data) -def view_splitter(request, su=None, adm=None): - if is_super_user(request): - return su(request) - elif is_group_admin(request): - return adm(request) - else: - return HttpResponseRedirect('/login/') +# def view_splitter(request, su=None, adm=None): +# if is_super_user(request): +# return su(request) +# elif is_group_admin(request): +# return adm(request) +# else: +# return HttpResponseRedirect('/login/') -def user_group_perm_asset_group_api(user_group): - asset_group_list = [] - perm_list = user_group.perm_set.all() - for perm in perm_list: - asset_group_list.append(perm.asset_group) - return asset_group_list +# def user_group_perm_asset_group_api(user_group): +# asset_group_list = [] +# perm_list = user_group.perm_set.all() +# for perm in perm_list: +# asset_group_list.append(perm.asset_group) +# return asset_group_list class Juser(object): diff --git a/juser/views.py b/juser/views.py index 054de5e49..122958927 100644 --- a/juser/views.py +++ b/juser/views.py @@ -6,15 +6,18 @@ import random from Crypto.PublicKey import RSA import crypt -from django.shortcuts import render_to_response from django.db.models import Q from django.template import RequestContext +from django.db.models import ObjectDoesNotExist from jumpserver.api import * def gen_rand_pwd(num): - """生成随机密码""" + """ + generate random password + 生成随机密码 + """ seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" salt_list = [] for i in range(num): @@ -23,24 +26,24 @@ def gen_rand_pwd(num): return salt -class AddError(Exception): - pass - - def gen_sha512(salt, password): + """ + generate sha512 format password + 生成sha512加密密码 + """ return crypt.crypt(password, '$6$%s$' % salt) def group_add_user(group, user_id=None, username=None): - try: - if user_id: - user = User.objects.get(id=user_id) - else: - user = User.objects.get(username=username) - except ObjectDoesNotExist: - raise AddError('用户获取失败') + """ + 用户组中添加用户 + UserGroup Add a user + """ + if user_id: + user = get_object(User, id=user_id) else: - group.user_set.add(user) + user = get_object(User, username=username) + group.user_set.add(user) def db_add_group(**kwargs): @@ -48,7 +51,7 @@ def db_add_group(**kwargs): group = UserGroup.objects.filter(name=name) users = kwargs.pop('users') if group: - raise AddError(u'用户组 %s 已经存在' % name) + raise ServerError(u'用户组 %s 已经存在' % name) group = UserGroup(**kwargs) group.save() for user_id in users: @@ -129,7 +132,7 @@ def ldap_add_user(username, ldap_pwd): if user: user = user[0] else: - raise AddError(u'用户 %s 不存在' % username) + raise ServerError(u'用户 %s 不存在' % username) user_attr = {'uid': [str(username)], 'cn': [str(username)], @@ -173,10 +176,10 @@ def dept_add(request): try: if not name: - raise AddError('部门名称不能为空') + raise ServerError('部门名称不能为空') if DEPT.objects.filter(name=name): - raise AddError(u'部门名称 %s 已存在' % name) - except AddError, e: + raise ServerError(u'部门名称 %s 已存在' % name) + except ServerError, e: error = e else: DEPT(name=name, comment=comment).save() @@ -341,21 +344,21 @@ def group_add(request): try: if '' in [group_name, dept_id]: error = u'组名 或 部门 不能为空' - raise AddError(error) + raise ServerError(error) if UserGroup.objects.filter(name=group_name): error = u'组名已存在' - raise AddError(error) + raise ServerError(error) dept = DEPT.objects.filter(id=dept_id) if dept: dept = dept[0] else: error = u'部门不存在' - raise AddError(error) + raise ServerError(error) db_add_group(name=group_name, users=users_selected, dept=dept, comment=comment) - except AddError: + except ServerError: pass except TypeError: error = u'保存小组失败' @@ -380,13 +383,13 @@ def group_add_adm(request): try: if not validate(request, user=users_selected): - raise AddError('没有某用户权限') + raise ServerError('没有某用户权限') if '' in [group_name]: error = u'组名不能为空' - raise AddError(error) + raise ServerError(error) db_add_group(name=group_name, users=users_selected, dept=dept, comment=comment) - except AddError: + except ServerError: pass except TypeError: error = u'保存小组失败' @@ -509,12 +512,12 @@ def group_edit(request): users = [] try: if '' in [group_id, group_name]: - raise AddError('组名不能为空') + raise ServerError('组名不能为空') dept = DEPT.objects.filter(id=dept_id) if dept: dept = dept[0] else: - raise AddError('部门不存在') + raise ServerError('部门不存在') for user_id in users_selected: users.extend(User.objects.filter(id=user_id)) @@ -525,7 +528,7 @@ def group_edit(request): user_group.user_set.clear() user_group.user_set = users - except AddError, e: + except ServerError, e: error = e return HttpResponseRedirect('/juser/group_list/') @@ -558,10 +561,10 @@ def group_edit_adm(request): users = [] try: if not validate(request, user=users_selected): - raise AddError(u'右侧非部门用户') + raise ServerError(u'右侧非部门用户') if not validate(request, user_group=[group_id]): - raise AddError(u'没有权限修改本组') + raise ServerError(u'没有权限修改本组') for user_id in users_selected: users.extend(User.objects.filter(id=user_id)) @@ -573,7 +576,7 @@ def group_edit_adm(request): user_group.user_set.clear() user_group.user_set = users - except AddError, e: + except ServerError, e: error = e return HttpResponseRedirect('/juser/group_list/') @@ -603,28 +606,28 @@ def user_add(request): try: if '' in [username, password, ssh_key_pwd, name, groups, role_post, is_active]: error = u'带*内容不能为空' - raise AddError + raise ServerError user = User.objects.filter(username=username) if user: error = u'用户 %s 已存在' % username - raise AddError + raise ServerError dept = DEPT.objects.filter(id=dept_id) if dept: dept = dept[0] else: error = u'部门不存在' - raise AddError(error) + raise ServerError(error) - except AddError: + except ServerError: pass else: try: user = db_add_user(username=username, - password=md5_crypt(password), + password=CRYPTOR.md5_crypt(password), name=name, email=email, dept=dept, groups=groups, role=role_post, - ssh_key_pwd=md5_crypt(ssh_key_pwd), + ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd), ldap_pwd=CRYPTOR.encrypt(ldap_pwd), is_active=is_active, date_joined=datetime.datetime.now()) @@ -681,21 +684,21 @@ def user_add_adm(request): try: if '' in [username, password, ssh_key_pwd, name, groups, is_active]: error = u'带*内容不能为空' - raise AddError + raise ServerError user = User.objects.filter(username=username) if user: error = u'用户 %s 已存在' % username - raise AddError + raise ServerError - except AddError: + except ServerError: pass else: try: user = db_add_user(username=username, - password=md5_crypt(password), + password=CRYPTOR.md5_crypt(password), name=name, email=email, dept=dept, groups=groups, role='CU', - ssh_key_pwd=md5_crypt(ssh_key_pwd), + ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd), ldap_pwd=CRYPTOR.encrypt(ldap_pwd), is_active=is_active, date_joined=datetime.datetime.now()) @@ -892,7 +895,7 @@ def user_edit(request): return HttpResponseRedirect('/juser/user_list/') if password != user.password: - password = md5_crypt(password) + password = CRYPTOR.md5_crypt(password) if ssh_key_pwd != user.ssh_key_pwd: gen_ssh_key(user.username, ssh_key_pwd) @@ -951,7 +954,7 @@ def user_edit_adm(request): return HttpResponseRedirect('/juser/user_list/') if password != user.password: - password = md5_crypt(password) + password = CRYPTOR.md5_crypt(password) if ssh_key_pwd != user.ssh_key_pwd: ssh_key_pwd = CRYPTOR.encrypt(ssh_key_pwd) @@ -1001,11 +1004,11 @@ def chg_info(request): if not error: if password != user.password: - password = md5_crypt(password) + password = CRYPTOR.md5_crypt(password) if ssh_key_pwd != user.ssh_key_pwd: gen_ssh_key(user.username, ssh_key_pwd) - ssh_key_pwd = md5_crypt(ssh_key_pwd) + ssh_key_pwd = CRYPTOR.md5_crypt(ssh_key_pwd) user_set.update(name=name, password=password, ssh_key_pwd=ssh_key_pwd, email=email) msg = '修改成功' From 9f0620f97e6c722761382e0cc440cff755cae963 Mon Sep 17 00:00:00 2001 From: Guang Date: Thu, 20 Aug 2015 23:42:27 +0800 Subject: [PATCH 009/385] =?UTF-8?q?=E8=B0=83=E6=95=B4User,UserGroup?= =?UTF-8?q?=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 9 +- docs/AddUserAsset.py | 14 +- jasset/models.py | 55 +++++ jumpserver/api.py | 521 ++++++++++++++++++++++--------------------- juser/models.py | 84 ++++++- juser/views.py | 4 + 6 files changed, 416 insertions(+), 271 deletions(-) diff --git a/connect.py b/connect.py index b2da5a934..ddfd50cfc 100644 --- a/connect.py +++ b/connect.py @@ -26,20 +26,19 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() from jlog.models import Log -from jumpserver.api import CONF, BASE_DIR, ServerError, Juser, Jasset, JassetGroup +from jumpserver.api import CONF, BASE_DIR, ServerError, User, UserGroup, Asset, BisGroup from jumpserver.api import CRYPTOR, logger, is_dir try: import termios import tty except ImportError: - print '\033[1;31mOnly unix like supported.\033[0m' + print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m' time.sleep(3) sys.exit() -CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) log_dir = os.path.join(BASE_DIR, 'logs') -login_user = Juser(username=getpass.getuser()) +login_user = User(username=getpass.getuser()) def color_print(msg, color='red', exits=False): @@ -265,7 +264,7 @@ def verify_connect(user, option): elif len(ip_matched) < 1: color_print('No Permission or No host.', 'red') else: - asset = Jasset(ip=ip_matched[0]).asset + asset = Asset(ip=ip_matched[0]).asset jtty = Jtty(user, asset) jtty.connect() diff --git a/docs/AddUserAsset.py b/docs/AddUserAsset.py index f8a5ed63c..283f838f9 100644 --- a/docs/AddUserAsset.py +++ b/docs/AddUserAsset.py @@ -127,14 +127,14 @@ def test_add_log(): if __name__ == '__main__': - #install() - #test_add_dept() - #test_add_group() - #test_add_user() - #test_add_idc() - #test_add_asset_group() + install() + test_add_dept() + test_add_group() + test_add_user() + test_add_idc() + test_add_asset_group() test_add_asset() - #test_add_log() + test_add_log() diff --git a/jasset/models.py b/jasset/models.py index cc3e0399b..58bab367e 100644 --- a/jasset/models.py +++ b/jasset/models.py @@ -23,6 +23,45 @@ class BisGroup(models.Model): def __unicode__(self): return self.name + def get_asset(self): + return self.asset_set.all() + + def get_asset_info(self, printable=False): + assets = self.get_asset() + for asset in assets: + if asset.comment: + print '%-15s -- %s' % (asset.ip, asset.comment) + else: + print '%-15s' % asset.ip + print '' + + def get_asset_num(self): + return len(self.get_asset()) + + def get_user_group(self): + perm_list = self.perm_set.all() + user_group_list = [] + for perm in perm_list: + user_group_list.append(perm.user_group) + return user_group_list + + def get_user(self): + user_list = [] + user_group_list = self.get_user_group() + for user_group in user_group_list: + user_list.extend(user_group.user_set.all()) + return user_list + + def is_permed(self, user=None, user_group=None): + if user: + if user in self.get_user(): + return True + + if user_group: + if user_group in self.get_user_group(): + return True + return False + class Asset(models.Model): LOGIN_TYPE_CHOICES = ( @@ -44,6 +83,22 @@ class Asset(models.Model): def __unicode__(self): return self.ip + def get_user(self): + perm_list = [] + asset_group_all = self.bis_group.all() + for asset_group in asset_group_all: + perm_list.extend(asset_group.perm_set.all()) + + user_group_list = [] + for perm in perm_list: + user_group_list.append(perm.user_group) + + user_permed_list = [] + for user_group in user_group_list: + user_permed_list.extend(user_group.user_set.all()) + user_permed_list = list(set(user_permed_list)) + return user_permed_list + class AssetAlias(models.Model): user = models.ForeignKey(User) diff --git a/jumpserver/api.py b/jumpserver/api.py index ca6fae855..811759f6d 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -219,6 +219,7 @@ def require_login(func): def require_super_user(func): + """要求是超级管理员""" def _deco(request, *args, **kwargs): if not request.session.get('user_id'): return HttpResponseRedirect('/login/') @@ -230,6 +231,7 @@ def require_super_user(func): def require_admin(func): + """要求是管理员""" def _deco(request, *args, **kwargs): if not request.session.get('user_id'): return HttpResponseRedirect('/login/') @@ -241,6 +243,7 @@ def require_admin(func): def is_super_user(request): + """要求请求是超级管理员""" if request.session.get('role_id') == 2: return True else: @@ -248,6 +251,7 @@ def is_super_user(request): def is_group_admin(request): + """要求请求是组管理员""" if request.session.get('role_id') == 1: return True else: @@ -255,6 +259,7 @@ def is_group_admin(request): def is_common_user(request): + """要求用户是普通用户""" if request.session.get('role_id') == 0: return True else: @@ -313,267 +318,267 @@ def api_user(request): # return asset_group_list -class Juser(object): - """ - Jumpserver user class - 用户类 - """ +# class Juser(object): +# """ +# Jumpserver user class +# 用户类 +# """ +# +# def __init__(self, username=None, uid=None): +# if username: +# user = User.objects.filter(username=username) +# elif uid: +# user = User.objects.filter(id=uid) +# else: +# user = '' +# +# if user: +# user = user[0] +# self.user = user +# self.id = user.id +# # self.id = user.id +# # self.username = user.username +# # self.name = user.name +# self.group = user.group.all() +# else: +# self.id = None +# +# def __repr__(self): +# if self.id: +# return '<%s Juser instance>' % getattr(self.user, 'username') +# else: +# return 'None' +# +# def __getattr__(self, item): +# if self.id: +# return getattr(self.user, item) +# else: +# return None +# +# def validate(self): +# """ +# Validate is or not a true user +# 鉴定用户 +# """ +# if self.id: +# return True +# else: +# return False +# +# def get_asset_group(self): +# """ +# Get user host_groups. +# 获取用户有权限的主机组 +# """ +# host_group_list = [] +# perm_list = [] +# user_group_all = self.user.group.all() +# for user_group in user_group_all: +# perm_list.extend(user_group.perm_set.all()) +# +# for perm in perm_list: +# host_group_list.append(perm.asset_group) +# +# return host_group_list +# +# def get_asset_group_info(self, printable=False): +# """ +# Get or print asset group info +# 获取或打印用户授权资产组 +# """ +# asset_groups_info = {} +# asset_groups = self.get_asset_group() +# +# for asset_group in asset_groups: +# asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] +# +# if printable: +# for group_id in asset_groups_info: +# if asset_groups_info[group_id][1]: +# print "[%3s] %s -- %s" % (group_id, +# asset_groups_info[group_id][0], +# asset_groups_info[group_id][1]) +# else: +# print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) +# print '' +# else: +# return asset_groups_info +# +# def get_asset(self): +# """ +# Get the assets of under the user control. +# 获取主机列表 +# """ +# assets = [] +# asset_groups = self.get_asset_group() +# +# for asset_group in asset_groups: +# assets.extend(asset_group.asset_set.all()) +# +# return assets +# +# def get_asset_info(self, printable=False): +# """ +# Get or print the user asset info +# 获取或打印用户资产信息 +# """ +# assets_info = {} +# assets = self.get_asset() +# +# for asset in assets: +# asset_alias = AssetAlias.objects.filter(user=self.user, asset=asset) +# if asset_alias and asset_alias[0].alias != '': +# assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] +# else: +# assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] +# +# if printable: +# ips = assets_info.keys() +# ips.sort() +# for ip in ips: +# if assets_info[ip][2]: +# print '%-15s -- %s' % (ip, assets_info[ip][2]) +# else: +# print '%-15s' % ip +# print '' +# else: +# return assets_info +# - def __init__(self, username=None, uid=None): - if username: - user = User.objects.filter(username=username) - elif uid: - user = User.objects.filter(id=uid) - else: - user = '' - - if user: - user = user[0] - self.user = user - self.id = user.id - # self.id = user.id - # self.username = user.username - # self.name = user.name - self.group = user.group.all() - else: - self.id = None - - def __repr__(self): - if self.id: - return '<%s Juser instance>' % getattr(self.user, 'username') - else: - return 'None' - - def __getattr__(self, item): - if self.id: - return getattr(self.user, item) - else: - return None - - def validate(self): - """ - Validate is or not a true user - 鉴定用户 - """ - if self.id: - return True - else: - return False - - def get_asset_group(self): - """ - Get user host_groups. - 获取用户有权限的主机组 - """ - host_group_list = [] - perm_list = [] - user_group_all = self.user.group.all() - for user_group in user_group_all: - perm_list.extend(user_group.perm_set.all()) - - for perm in perm_list: - host_group_list.append(perm.asset_group) - - return host_group_list - - def get_asset_group_info(self, printable=False): - """ - Get or print asset group info - 获取或打印用户授权资产组 - """ - asset_groups_info = {} - asset_groups = self.get_asset_group() - - for asset_group in asset_groups: - asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] - - if printable: - for group_id in asset_groups_info: - if asset_groups_info[group_id][1]: - print "[%3s] %s -- %s" % (group_id, - asset_groups_info[group_id][0], - asset_groups_info[group_id][1]) - else: - print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) - print '' - else: - return asset_groups_info - - def get_asset(self): - """ - Get the assets of under the user control. - 获取主机列表 - """ - assets = [] - asset_groups = self.get_asset_group() - - for asset_group in asset_groups: - assets.extend(asset_group.asset_set.all()) - - return assets - - def get_asset_info(self, printable=False): - """ - Get or print the user asset info - 获取或打印用户资产信息 - """ - assets_info = {} - assets = self.get_asset() - - for asset in assets: - asset_alias = AssetAlias.objects.filter(user=self.user, asset=asset) - if asset_alias and asset_alias[0].alias != '': - assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] - else: - assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] - - if printable: - ips = assets_info.keys() - ips.sort() - for ip in ips: - if assets_info[ip][2]: - print '%-15s -- %s' % (ip, assets_info[ip][2]) - else: - print '%-15s' % ip - print '' - else: - return assets_info +# class Jasset(object): +# """ +# Jumpserver asset class +# Jumpserver资产类 +# """ +# def __init__(self, ip=None, id=None): +# if ip: +# asset = Asset.objects.filter(ip=ip) +# elif id: +# asset = Asset.objects.filter(id=id) +# else: +# asset = '' +# +# if asset: +# asset = asset[0] +# self.asset = asset +# self.id = asset.id +# else: +# self.id = None +# +# def __repr__(self): +# if self.id: +# return '<%s Jasset instance>' % self.asset.ip +# else: +# return 'None' +# +# def __getattr__(self, item): +# if self.id: +# return getattr(self.asset, item) +# else: +# return None +# +# def validate(self): +# """ +# Validate is or not a true asset +# 判断是否存在 +# """ +# if self.id: +# return True +# else: +# return False +# +# def get_user(self): +# perm_list = [] +# asset_group_all = self.bis_group.all() +# for asset_group in asset_group_all: +# perm_list.extend(asset_group.perm_set.all()) +# +# user_group_list = [] +# for perm in perm_list: +# user_group_list.append(perm.user_group) +# +# user_permed_list = [] +# for user_group in user_group_list: +# user_permed_list.extend(user_group.user_set.all()) +# user_permed_list = list(set(user_permed_list)) +# return user_permed_list -class Jasset(object): - """ - Jumpserver asset class - Jumpserver资产类 - """ - def __init__(self, ip=None, id=None): - if ip: - asset = Asset.objects.filter(ip=ip) - elif id: - asset = Asset.objects.filter(id=id) - else: - asset = '' - - if asset: - asset = asset[0] - self.asset = asset - self.id = asset.id - else: - self.id = None - - def __repr__(self): - if self.id: - return '<%s Jasset instance>' % self.asset.ip - else: - return 'None' - - def __getattr__(self, item): - if self.id: - return getattr(self.asset, item) - else: - return None - - def validate(self): - """ - Validate is or not a true asset - 判断是否存在 - """ - if self.id: - return True - else: - return False - - def get_user(self): - perm_list = [] - asset_group_all = self.asset.bis_group.all() - for asset_group in asset_group_all: - perm_list.extend(asset_group.perm_set.all()) - - user_group_list = [] - for perm in perm_list: - user_group_list.append(perm.user_group) - - user_permed_list = [] - for user_group in user_group_list: - user_permed_list.extend(user_group.user_set.all()) - user_permed_list = list(set(user_permed_list)) - return user_permed_list - - -class JassetGroup(object): - """ - Jumpserver AssetGroup class - Jumpserver 资产组类 - """ - def __init__(self, name=None, id=None): - if id: - asset_group = BisGroup.objects.filter(id=int(id)) - elif name: - asset_group = BisGroup.objects.filter(name=name) - else: - asset_group = '' - - if asset_group: - asset_group = asset_group[0] - self.asset_group = asset_group - # self.name = asset_group.name - self.id = asset_group.id - else: - self.id = None - - def __repr__(self): - if self.id: - return '<%s JassetGroup instance>' % self.name - else: - return 'None' - - def validate(self): - """ - Validate it is a true asset group or not - 鉴定是否为真是存在的组 - """ - if self.id: - return True - else: - return False - - def get_asset(self): - return self.asset_group.asset_set.all() - - def get_asset_info(self, printable=False): - assets = self.get_asset() - for asset in assets: - if asset.comment: - print '%-15s -- %s' % (asset.ip, asset.comment) - else: - print '%-15s' % asset.ip - print '' - - def get_asset_num(self): - return len(self.get_asset()) - - def get_user_group(self): - perm_list = self.asset_group.perm_set.all() - user_group_list = [] - for perm in perm_list: - user_group_list.append(perm.user_group) - return user_group_list - - def get_user(self): - user_list = [] - user_group_list = self.get_user_group() - for user_group in user_group_list: - user_list.extend(user_group.user_set.all()) - return user_list - - def is_permed(self, user=None, user_group=None): - if user: - if user in self.get_user(): - return True - - if user_group: - if user_group in self.get_user_group(): - return True - return False +# class JassetGroup(object): +# """ +# Jumpserver AssetGroup class +# Jumpserver 资产组类 +# """ +# def __init__(self, name=None, id=None): +# if id: +# asset_group = BisGroup.objects.filter(id=int(id)) +# elif name: +# asset_group = BisGroup.objects.filter(name=name) +# else: +# asset_group = '' +# +# if asset_group: +# asset_group = asset_group[0] +# self.asset_group = asset_group +# # self.name = asset_group.name +# self.id = asset_group.id +# else: +# self.id = None +# +# def __repr__(self): +# if self.id: +# return '<%s JassetGroup instance>' % self.name +# else: +# return 'None' +# +# def validate(self): +# """ +# Validate it is a true asset group or not +# 鉴定是否为真是存在的组 +# """ +# if self.id: +# return True +# else: +# return False +# +# def get_asset(self): +# return self.asset_group.asset_set.all() +# +# def get_asset_info(self, printable=False): +# assets = self.get_asset() +# for asset in assets: +# if asset.comment: +# print '%-15s -- %s' % (asset.ip, asset.comment) +# else: +# print '%-15s' % asset.ip +# print '' +# +# def get_asset_num(self): +# return len(self.get_asset()) +# +# def get_user_group(self): +# perm_list = self.asset_group.perm_set.all() +# user_group_list = [] +# for perm in perm_list: +# user_group_list.append(perm.user_group) +# return user_group_list +# +# def get_user(self): +# user_list = [] +# user_group_list = self.get_user_group() +# for user_group in user_group_list: +# user_list.extend(user_group.user_set.all()) +# return user_list +# +# def is_permed(self, user=None, user_group=None): +# if user: +# if user in self.get_user(): +# return True +# +# if user_group: +# if user_group in self.get_user_group(): +# return True +# return False # def asset_perm_api(asset): diff --git a/juser/models.py b/juser/models.py index b309effe4..31dd9b663 100644 --- a/juser/models.py +++ b/juser/models.py @@ -1,3 +1,5 @@ +#coding: utf-8 + from django.db import models @@ -32,10 +34,90 @@ class User(models.Model): dept = models.ForeignKey(DEPT) group = models.ManyToManyField(UserGroup) ldap_pwd = models.CharField(max_length=128) - ssh_key_pwd = models.CharField(max_length=100) + ssh_key_pwd = models.CharField(max_length=200) is_active = models.BooleanField(default=True) last_login = models.DateTimeField(null=True) date_joined = models.DateTimeField(null=True) def __unicode__(self): return self.username + + def get_asset_group(self): + """ + Get user host_groups. + 获取用户有权限的主机组 + """ + host_group_list = [] + perm_list = [] + user_group_all = self.group.all() + for user_group in user_group_all: + perm_list.extend(user_group.perm_set.all()) + + for perm in perm_list: + host_group_list.append(perm.asset_group) + + return host_group_list + + def get_asset_group_info(self, printable=False): + """ + Get or print asset group info + 获取或打印用户授权资产组 + """ + asset_groups_info = {} + asset_groups = self.get_asset_group() + + for asset_group in asset_groups: + asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] + + if printable: + for group_id in asset_groups_info: + if asset_groups_info[group_id][1]: + print "[%3s] %s -- %s" % (group_id, + asset_groups_info[group_id][0], + asset_groups_info[group_id][1]) + else: + print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) + print '' + else: + return asset_groups_info + + def get_asset(self): + """ + Get the assets of under the user control. + 获取主机列表 + """ + assets = [] + asset_groups = self.get_asset_group() + + for asset_group in asset_groups: + assets.extend(asset_group.asset_set.all()) + + return assets + + def get_asset_info(self, printable=False): + """ + Get or print the user asset info + 获取或打印用户资产信息 + """ + from jasset.models import AssetAlias + assets_info = {} + assets = self.get_asset() + + for asset in assets: + asset_alias = AssetAlias.objects.filter(user=self.user, asset=asset) + if asset_alias and asset_alias[0].alias != '': + assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] + else: + assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] + + if printable: + ips = assets_info.keys() + ips.sort() + for ip in ips: + if assets_info[ip][2]: + print '%-15s -- %s' % (ip, assets_info[ip][2]) + else: + print '%-15s' % ip + print '' + else: + return assets_info diff --git a/juser/views.py b/juser/views.py index 122958927..83bca8921 100644 --- a/juser/views.py +++ b/juser/views.py @@ -13,6 +13,10 @@ from django.db.models import ObjectDoesNotExist from jumpserver.api import * +def md5_crypt(string): + return hashlib.new("md5", string).hexdigest() + + def gen_rand_pwd(num): """ generate random password From ad16d8b532e7c5271c8417b754b6b7b43faf2de7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 21 Aug 2015 23:45:41 +0800 Subject: [PATCH 010/385] =?UTF-8?q?=E4=BC=98=E5=8C=96connect.py=20?= =?UTF-8?q?=E5=92=8C=20api.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 70 ++++---- jlog/models.py | 2 +- jumpserver/api.py | 395 ++++++---------------------------------------- juser/models.py | 5 +- 4 files changed, 90 insertions(+), 382 deletions(-) diff --git a/connect.py b/connect.py index ddfd50cfc..fe83afcd4 100644 --- a/connect.py +++ b/connect.py @@ -26,8 +26,9 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() from jlog.models import Log -from jumpserver.api import CONF, BASE_DIR, ServerError, User, UserGroup, Asset, BisGroup +from jumpserver.api import CONF, BASE_DIR, ServerError, User, UserGroup, Asset, get_object from jumpserver.api import CRYPTOR, logger, is_dir +from jumpserver.api import BisGroup as AssetGroup try: import termios @@ -38,7 +39,7 @@ except ImportError: sys.exit() log_dir = os.path.join(BASE_DIR, 'logs') -login_user = User(username=getpass.getuser()) +login_user = get_object(User, username=getpass.getuser()) def color_print(msg, color='red', exits=False): @@ -98,14 +99,14 @@ class Jtty(object): timestamp_start = int(time.time()) date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) - today_connect_log_dir = os.path.join(tty_log_dir, date_start) log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start) + today_connect_log_dir = os.path.join(tty_log_dir, date_start) log_file_path = os.path.join(today_connect_log_dir, log_filename) dept_name = self.user.dept.name pid = os.getpid() pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() - remote_ip = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') + ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') try: is_dir(today_connect_log_dir) @@ -117,7 +118,7 @@ class Jtty(object): except IOError: raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - log = Log(user=self.username, host=self.ip, remote_ip=remote_ip, dept_name=dept_name, + log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name, log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) log_file.write('Start time is %s\n' % datetime.datetime.now()) log.save() @@ -164,18 +165,17 @@ class Jtty(object): log_file.write('End time is %s' % datetime.datetime.now()) log_file.close() log.is_finished = True - log.log_finished = False + log.handle_finished = False log.end_time = datetime.datetime.now() log.save() def get_connect_item(self): - port = int(self.asset.port) - + """获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码""" if not self.asset.is_active: - raise ServerError('Host %s is not active.' % self.ip) + raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) if not self.user.is_active: - raise ServerError('User %s is not active.' % self.username) + raise ServerError('该用户被禁用 User %s is not active.' % self.username) login_type_dict = { 'L': self.user.ldap_pwd, @@ -183,37 +183,38 @@ class Jtty(object): if self.asset.login_type in login_type_dict: password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type]) - return self.username, password, self.ip, port + return self.username, password, self.ip, int(self.asset.port) elif self.asset.login_type == 'M': username = self.asset.username password = CRYPTOR.decrypt(self.asset.password) - return username, password, self.ip, port + return username, password, self.ip, int(self.asset.port) else: - raise ServerError('Login type is not in ["L", "M"]') + raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]') def connect(self): """ Connect server. + 连接服务器 """ username, password, ip, port = self.get_connect_item() logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port)) ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % ip - # Make a ssh connection + # 发起ssh连接请求 Make a ssh connection ssh = paramiko.SSHClient() ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect(ip, port=port, username=username, password=password, compress=True) except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: - raise ServerError('Authentication Error.') + raise ServerError('认证错误 Authentication Error.') except socket.error: - raise ServerError('Connect SSH Socket Port Error, Please Correct it.') + raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.') - # Make a channel and set windows size + # 获取连接的隧道并设置窗口大小 Make a channel and set windows size global channel win_size = self.get_win_size() self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) @@ -222,7 +223,7 @@ class Jtty(object): except: pass - # Set PS1 and msg it + # 设置PS1并提示 Set PS1 and msg it channel.send(ps1) channel.send(login_msg) @@ -235,6 +236,7 @@ class Jtty(object): def verify_connect(user, option): + """鉴定用户是否有该主机权限 或 匹配到的ip是否唯一""" ip_matched = [] try: assets_info = login_user.get_asset_info() @@ -255,16 +257,20 @@ def verify_connect(user, option): ip_matched = list(set(ip_matched)) if len(ip_matched) > 1: + ip_comment = {} for ip in ip_matched: - if assets_info[ip][2]: - print '%-15s -- %s' % (ip, assets_info[ip][2]) + ip_comment[ip] = assets_info[ip][2] + + for ip in sorted(ip_comment): + if ip_comment[ip]: + print '%-15s -- %s' % (ip, ip_comment[ip]) else: print '%-15s' % ip - print '' + print '' elif len(ip_matched) < 1: - color_print('No Permission or No host.', 'red') + color_print('没有该主机,或者您没有该主机的权限 No Permission or No host.', 'red') else: - asset = Asset(ip=ip_matched[0]).asset + asset = get_object(Asset, ip=ip_matched[0]) jtty = Jtty(user, asset) jtty.connect() @@ -351,12 +357,13 @@ def print_prompt(): # multi_remote_exec_cmd(hosts, username, cmd) -if __name__ == '__main__': - if not login_user.validate(): - color_print(u'没有该用户 No that user.', exits=True) +def main(): + if not login_user: # 判断用户是否存在 + color_print(u'没有该用户,或许你是以root运行的 No that user.', exits=True) print_prompt() gid_pattern = re.compile(r'^g\d+$') + try: while True: try: @@ -374,8 +381,8 @@ if __name__ == '__main__': continue elif gid_pattern.match(option): gid = option[1:].strip() - asset_group = JassetGroup(id=gid) - if asset_group.validate() and asset_group.is_permed(user=login_user.user): + 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) continue elif option in ['E', 'e']: @@ -390,3 +397,8 @@ if __name__ == '__main__': color_print(e, 'red') except IndexError: pass + +if __name__ == '__main__': + main() + + diff --git a/jlog/models.py b/jlog/models.py index 3d2a319df..baaffb5a7 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -10,7 +10,7 @@ class Log(models.Model): start_time = models.DateTimeField(null=True) pid = models.IntegerField(max_length=10) is_finished = models.BooleanField(default=False) - log_finished = models.BooleanField(default=False) + handle_finished = models.BooleanField(default=False) end_time = models.DateTimeField(null=True) def __unicode__(self): diff --git a/jumpserver/api.py b/jumpserver/api.py index 811759f6d..2b133585a 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -41,16 +41,18 @@ SEND_IP = CONF.get('base', 'ip') SEND_PORT = CONF.get('base', 'port') MAIL_FROM = CONF.get('mail', 'email_host_user') -log_level = CONF.get('base', 'log') -log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR, - 'critical': logging.CRITICAL} -logger = logging.getLogger('jumpserver') -logger.setLevel(logging.DEBUG) -fh = logging.FileHandler(JLOG_FILE) -fh.setLevel(log_level_total.get(log_level, logging.DEBUG)) -formatter = logging.Formatter('%(asctime)s - %(filename)s - %(levelname)s - %(message)s') -fh.setFormatter(formatter) -logger.addHandler(fh) + +def set_log(level): + log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR, + 'critical': logging.CRITICAL} + logger = logging.getLogger('jumpserver') + logger.setLevel(logging.DEBUG) + fh = logging.FileHandler(JLOG_FILE) + fh.setLevel(log_level_total.get(level, logging.DEBUG)) + formatter = logging.Formatter('%(asctime)s - %(filename)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + logger.addHandler(fh) + return logger class LDAPMgmt(): @@ -205,68 +207,45 @@ def get_object(model, **kwargs): try: the_object = model.objects.get(**kwargs) except ObjectDoesNotExist: - raise ServerError('Object get %s failed.' % str(kwargs.values())) + the_object = None return the_object -def require_login(func): - """要求登录的装饰器""" - def _deco(request, *args, **kwargs): - if not request.session.get('user_id'): - return HttpResponseRedirect('/login/') - return func(request, *args, **kwargs) +def require_role(role='user'): + """ + 要求用户是某种角色 ["super", "admin", "user"] + """ + def _deco(func): + def __deco(request, *args, **kwargs): + if role == 'user': + if not request.session.get('user_id'): + return HttpResponseRedirect('/login/') + elif role == 'admin': + if request.session.get('role_id', 0) != 1: + return HttpResponseRedirect('/') + elif role == 'super': + if request.session.get('role_id', 0) != 2: + return HttpResponseRedirect('/') + return func(request, *args, **kwargs) + return __deco() return _deco -def require_super_user(func): - """要求是超级管理员""" - def _deco(request, *args, **kwargs): - if not request.session.get('user_id'): - return HttpResponseRedirect('/login/') - - if request.session.get('role_id', 0) != 2: - return HttpResponseRedirect('/') - return func(request, *args, **kwargs) - return _deco - - -def require_admin(func): - """要求是管理员""" - def _deco(request, *args, **kwargs): - if not request.session.get('user_id'): - return HttpResponseRedirect('/login/') - - if request.session.get('role_id', 0) < 1: - return HttpResponseRedirect('/') - return func(request, *args, **kwargs) - return _deco - - -def is_super_user(request): - """要求请求是超级管理员""" - if request.session.get('role_id') == 2: +def is_role_request(request, role='user'): + """ + :param request: 请求 + :param role: 角色 + :return: bool + 要求请求角色正确 + """ + role_all = {'user': 0, 'admin': 1, 'super': 2} + if request.session.get('role_id') == role_all.get(role, '0'): return True else: return False -def is_group_admin(request): - """要求请求是组管理员""" - if request.session.get('role_id') == 1: - return True - else: - return False - - -def is_common_user(request): - """要求用户是普通用户""" - if request.session.get('role_id') == 0: - return True - else: - return False - - -@require_login +@require_role def get_session_user_dept(request): user_id = request.session.get('user_id', 0) user = User.objects.filter(id=user_id) @@ -276,7 +255,7 @@ def get_session_user_dept(request): return user, dept -@require_login +@require_role def get_session_user_info(request): user_id = request.session.get('user_id', 0) user = User.objects.filter(id=user_id) @@ -310,295 +289,6 @@ def api_user(request): # return HttpResponseRedirect('/login/') -# def user_group_perm_asset_group_api(user_group): -# asset_group_list = [] -# perm_list = user_group.perm_set.all() -# for perm in perm_list: -# asset_group_list.append(perm.asset_group) -# return asset_group_list - - -# class Juser(object): -# """ -# Jumpserver user class -# 用户类 -# """ -# -# def __init__(self, username=None, uid=None): -# if username: -# user = User.objects.filter(username=username) -# elif uid: -# user = User.objects.filter(id=uid) -# else: -# user = '' -# -# if user: -# user = user[0] -# self.user = user -# self.id = user.id -# # self.id = user.id -# # self.username = user.username -# # self.name = user.name -# self.group = user.group.all() -# else: -# self.id = None -# -# def __repr__(self): -# if self.id: -# return '<%s Juser instance>' % getattr(self.user, 'username') -# else: -# return 'None' -# -# def __getattr__(self, item): -# if self.id: -# return getattr(self.user, item) -# else: -# return None -# -# def validate(self): -# """ -# Validate is or not a true user -# 鉴定用户 -# """ -# if self.id: -# return True -# else: -# return False -# -# def get_asset_group(self): -# """ -# Get user host_groups. -# 获取用户有权限的主机组 -# """ -# host_group_list = [] -# perm_list = [] -# user_group_all = self.user.group.all() -# for user_group in user_group_all: -# perm_list.extend(user_group.perm_set.all()) -# -# for perm in perm_list: -# host_group_list.append(perm.asset_group) -# -# return host_group_list -# -# def get_asset_group_info(self, printable=False): -# """ -# Get or print asset group info -# 获取或打印用户授权资产组 -# """ -# asset_groups_info = {} -# asset_groups = self.get_asset_group() -# -# for asset_group in asset_groups: -# asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] -# -# if printable: -# for group_id in asset_groups_info: -# if asset_groups_info[group_id][1]: -# print "[%3s] %s -- %s" % (group_id, -# asset_groups_info[group_id][0], -# asset_groups_info[group_id][1]) -# else: -# print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) -# print '' -# else: -# return asset_groups_info -# -# def get_asset(self): -# """ -# Get the assets of under the user control. -# 获取主机列表 -# """ -# assets = [] -# asset_groups = self.get_asset_group() -# -# for asset_group in asset_groups: -# assets.extend(asset_group.asset_set.all()) -# -# return assets -# -# def get_asset_info(self, printable=False): -# """ -# Get or print the user asset info -# 获取或打印用户资产信息 -# """ -# assets_info = {} -# assets = self.get_asset() -# -# for asset in assets: -# asset_alias = AssetAlias.objects.filter(user=self.user, asset=asset) -# if asset_alias and asset_alias[0].alias != '': -# assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] -# else: -# assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] -# -# if printable: -# ips = assets_info.keys() -# ips.sort() -# for ip in ips: -# if assets_info[ip][2]: -# print '%-15s -- %s' % (ip, assets_info[ip][2]) -# else: -# print '%-15s' % ip -# print '' -# else: -# return assets_info -# - -# class Jasset(object): -# """ -# Jumpserver asset class -# Jumpserver资产类 -# """ -# def __init__(self, ip=None, id=None): -# if ip: -# asset = Asset.objects.filter(ip=ip) -# elif id: -# asset = Asset.objects.filter(id=id) -# else: -# asset = '' -# -# if asset: -# asset = asset[0] -# self.asset = asset -# self.id = asset.id -# else: -# self.id = None -# -# def __repr__(self): -# if self.id: -# return '<%s Jasset instance>' % self.asset.ip -# else: -# return 'None' -# -# def __getattr__(self, item): -# if self.id: -# return getattr(self.asset, item) -# else: -# return None -# -# def validate(self): -# """ -# Validate is or not a true asset -# 判断是否存在 -# """ -# if self.id: -# return True -# else: -# return False -# -# def get_user(self): -# perm_list = [] -# asset_group_all = self.bis_group.all() -# for asset_group in asset_group_all: -# perm_list.extend(asset_group.perm_set.all()) -# -# user_group_list = [] -# for perm in perm_list: -# user_group_list.append(perm.user_group) -# -# user_permed_list = [] -# for user_group in user_group_list: -# user_permed_list.extend(user_group.user_set.all()) -# user_permed_list = list(set(user_permed_list)) -# return user_permed_list - - -# class JassetGroup(object): -# """ -# Jumpserver AssetGroup class -# Jumpserver 资产组类 -# """ -# def __init__(self, name=None, id=None): -# if id: -# asset_group = BisGroup.objects.filter(id=int(id)) -# elif name: -# asset_group = BisGroup.objects.filter(name=name) -# else: -# asset_group = '' -# -# if asset_group: -# asset_group = asset_group[0] -# self.asset_group = asset_group -# # self.name = asset_group.name -# self.id = asset_group.id -# else: -# self.id = None -# -# def __repr__(self): -# if self.id: -# return '<%s JassetGroup instance>' % self.name -# else: -# return 'None' -# -# def validate(self): -# """ -# Validate it is a true asset group or not -# 鉴定是否为真是存在的组 -# """ -# if self.id: -# return True -# else: -# return False -# -# def get_asset(self): -# return self.asset_group.asset_set.all() -# -# def get_asset_info(self, printable=False): -# assets = self.get_asset() -# for asset in assets: -# if asset.comment: -# print '%-15s -- %s' % (asset.ip, asset.comment) -# else: -# print '%-15s' % asset.ip -# print '' -# -# def get_asset_num(self): -# return len(self.get_asset()) -# -# def get_user_group(self): -# perm_list = self.asset_group.perm_set.all() -# user_group_list = [] -# for perm in perm_list: -# user_group_list.append(perm.user_group) -# return user_group_list -# -# def get_user(self): -# user_list = [] -# user_group_list = self.get_user_group() -# for user_group in user_group_list: -# user_list.extend(user_group.user_set.all()) -# return user_list -# -# def is_permed(self, user=None, user_group=None): -# if user: -# if user in self.get_user(): -# return True -# -# if user_group: -# if user_group in self.get_user_group(): -# return True -# return False - - -# def asset_perm_api(asset): -# if asset: -# perm_list = [] -# asset_group_all = asset.bis_group.all() -# for asset_group in asset_group_all: -# perm_list.extend(asset_group.perm_set.all()) -# -# user_group_list = [] -# for perm in perm_list: -# user_group_list.append(perm.user_group) -# -# user_permed_list = [] -# for user_group in user_group_list: -# user_permed_list.extend(user_group.user_set.all()) -# user_permed_list = list(set(user_permed_list)) -# return user_permed_list - - def validate(request, user_group=None, user=None, asset_group=None, asset=None, edept=None): dept = get_session_user_dept(request)[1] if edept: @@ -694,6 +384,7 @@ def bash(cmd): def is_dir(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)) @@ -720,3 +411,5 @@ if LDAP_ENABLE: else: ldap_conn = None +log_level = CONF.get('base', 'log') +logger = set_log(log_level) \ No newline at end of file diff --git a/juser/models.py b/juser/models.py index 31dd9b663..a507e1c63 100644 --- a/juser/models.py +++ b/juser/models.py @@ -19,6 +19,9 @@ class UserGroup(models.Model): def __unicode__(self): return self.name + def get_user(self): + return self.user_set.all() + class User(models.Model): USER_ROLE_CHOICES = ( @@ -104,7 +107,7 @@ class User(models.Model): assets = self.get_asset() for asset in assets: - asset_alias = AssetAlias.objects.filter(user=self.user, asset=asset) + asset_alias = AssetAlias.objects.filter(user=self, asset=asset) if asset_alias and asset_alias[0].alias != '': assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] else: From c1facb939e435d0dcbe37535f02152aa09e89570 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 22 Aug 2015 00:01:54 +0800 Subject: [PATCH 011/385] =?UTF-8?q?=E4=BC=98=E5=8C=96connect.py=E6=8E=92?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 198 +--------------------------------------------- jasset/models.py | 10 ++- jumpserver/api.py | 196 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 199 insertions(+), 205 deletions(-) diff --git a/connect.py b/connect.py index fe83afcd4..0039aa05f 100644 --- a/connect.py +++ b/connect.py @@ -5,40 +5,22 @@ import sys reload(sys) sys.setdefaultencoding('utf8') -import socket import os import re -import select import time -import paramiko -import struct -import fcntl -import signal import textwrap import getpass -import fnmatch import readline import django -import datetime from multiprocessing import Pool os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() -from jlog.models import Log -from jumpserver.api import CONF, BASE_DIR, ServerError, User, UserGroup, Asset, get_object +from jumpserver.api import BASE_DIR, ServerError, User, UserGroup, Asset, Jtty, get_object from jumpserver.api import CRYPTOR, logger, is_dir from jumpserver.api import BisGroup as AssetGroup -try: - import termios - import tty -except ImportError: - print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m' - time.sleep(3) - sys.exit() - -log_dir = os.path.join(BASE_DIR, 'logs') login_user = get_object(User, username=getpass.getuser()) @@ -57,184 +39,6 @@ def color_print(msg, color='red', exits=False): sys.exit() -class Jtty(object): - def __init__(self, user, asset): - self.chan = None - self.username = user.username - self.ip = asset.ip - self.user = user - self.asset = asset - - @staticmethod - def get_win_size(): - """ - This function use to get the size of the windows! - 获得terminal窗口大小 - """ - if 'TIOCGWINSZ' in dir(termios): - TIOCGWINSZ = termios.TIOCGWINSZ - else: - TIOCGWINSZ = 1074295912L - s = struct.pack('HHHH', 0, 0, 0, 0) - x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) - return struct.unpack('HHHH', x)[0:2] - - def set_win_size(self, sig, data): - """ - This function use to set the window size of the terminal! - 设置terminal窗口大小 - """ - try: - win_size = self.get_win_size() - self.chan.resize_pty(height=win_size[0], width=win_size[1]) - except Exception: - pass - - def log_record(self): - """ - Logging user command and output. - 记录用户的日志 - """ - tty_log_dir = os.path.join(log_dir, 'tty') - timestamp_start = int(time.time()) - date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) - time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) - log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start) - today_connect_log_dir = os.path.join(tty_log_dir, date_start) - log_file_path = os.path.join(today_connect_log_dir, log_filename) - dept_name = self.user.dept.name - - pid = os.getpid() - pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() - ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') - - try: - is_dir(today_connect_log_dir) - except OSError: - raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) - - try: - log_file = open(log_file_path, 'a') - except IOError: - raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - - log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name, - log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) - log_file.write('Start time is %s\n' % datetime.datetime.now()) - log.save() - return log_file, log - - def posix_shell(self): - """ - Use paramiko channel connect server interactive. - 使用paramiko模块的channel,连接后端,进入交互式 - """ - log_file, log = self.log_record() - old_tty = termios.tcgetattr(sys.stdin) - try: - tty.setraw(sys.stdin.fileno()) - tty.setcbreak(sys.stdin.fileno()) - self.chan.settimeout(0.0) - - while True: - try: - r, w, e = select.select([self.chan, sys.stdin], [], []) - except Exception: - pass - - if self.chan in r: - try: - x = self.chan.recv(1024) - if len(x) == 0: - break - sys.stdout.write(x) - sys.stdout.flush() - log_file.write(x) - log_file.flush() - except socket.timeout: - pass - - if sys.stdin in r: - x = os.read(sys.stdin.fileno(), 1) - if len(x) == 0: - break - self.chan.send(x) - - finally: - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - log_file.write('End time is %s' % datetime.datetime.now()) - log_file.close() - log.is_finished = True - log.handle_finished = False - log.end_time = datetime.datetime.now() - log.save() - - def get_connect_item(self): - """获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码""" - if not self.asset.is_active: - raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) - - if not self.user.is_active: - raise ServerError('该用户被禁用 User %s is not active.' % self.username) - - login_type_dict = { - 'L': self.user.ldap_pwd, - } - - if self.asset.login_type in login_type_dict: - password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type]) - return self.username, password, self.ip, int(self.asset.port) - - elif self.asset.login_type == 'M': - username = self.asset.username - password = CRYPTOR.decrypt(self.asset.password) - return username, password, self.ip, int(self.asset.port) - - else: - raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]') - - def connect(self): - """ - Connect server. - 连接服务器 - """ - username, password, ip, port = self.get_connect_item() - logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port)) - ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % ip - - # 发起ssh连接请求 Make a ssh connection - ssh = paramiko.SSHClient() - ssh.load_system_host_keys() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - try: - ssh.connect(ip, port=port, username=username, password=password, compress=True) - except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: - raise ServerError('认证错误 Authentication Error.') - except socket.error: - raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.') - - # 获取连接的隧道并设置窗口大小 Make a channel and set windows size - global channel - win_size = self.get_win_size() - self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) - try: - signal.signal(signal.SIGWINCH, self.set_win_size) - except: - pass - - # 设置PS1并提示 Set PS1 and msg it - channel.send(ps1) - channel.send(login_msg) - - # Make ssh interactive tunnel - self.posix_shell() - - # Shutdown channel socket - channel.close() - ssh.close() - - def verify_connect(user, option): """鉴定用户是否有该主机权限 或 匹配到的ip是否唯一""" ip_matched = [] diff --git a/jasset/models.py b/jasset/models.py index 58bab367e..9874616b8 100644 --- a/jasset/models.py +++ b/jasset/models.py @@ -28,11 +28,15 @@ class BisGroup(models.Model): def get_asset_info(self, printable=False): assets = self.get_asset() + ip_comment = {} for asset in assets: - if asset.comment: - print '%-15s -- %s' % (asset.ip, asset.comment) + ip_comment[asset.ip] = asset.comment + + for ip in sorted(ip_comment): + if ip_comment[ip]: + print '%-15s -- %s' % (ip, ip_comment[ip]) else: - print '%-15s' % asset.ip + print '%-15s' % ip print '' def get_asset_num(self): diff --git a/jumpserver/api.py b/jumpserver/api.py index 2b133585a..6cff43abd 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -1,9 +1,6 @@ # coding: utf-8 - -from django.http import HttpResponseRedirect -import json -import os +import os, sys, time from ConfigParser import ConfigParser import getpass from Crypto.Cipher import AES @@ -14,6 +11,8 @@ import hashlib import datetime import random import subprocess +import paramiko +import struct, fcntl, signal,socket, select, fnmatch from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.http import HttpResponse, Http404 from django.shortcuts import render_to_response @@ -22,10 +21,19 @@ from jasset.models import Asset, BisGroup, IDC from jlog.models import Log from jasset.models import AssetAlias from django.core.exceptions import ObjectDoesNotExist +from django.http import HttpResponseRedirect from django.core.mail import send_mail import json import logging +try: + import termios + import tty +except ImportError: + print '\033[1;31m仅支持类Unix系统 Only unix like supported.\033[0m' + time.sleep(3) + sys.exit() + BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) CONF = ConfigParser() @@ -40,7 +48,7 @@ LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') SEND_IP = CONF.get('base', 'ip') SEND_PORT = CONF.get('base', 'port') MAIL_FROM = CONF.get('mail', 'email_host_user') - +log_dir = os.path.join(BASE_DIR, 'logs') def set_log(level): log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR, @@ -139,6 +147,184 @@ def pages(posts, r): return contact_list, p, contacts, page_range, current_page, show_first, show_end +class Jtty(object): + def __init__(self, user, asset): + self.chan = None + self.username = user.username + self.ip = asset.ip + self.user = user + self.asset = asset + + @staticmethod + def get_win_size(): + """ + This function use to get the size of the windows! + 获得terminal窗口大小 + """ + if 'TIOCGWINSZ' in dir(termios): + TIOCGWINSZ = termios.TIOCGWINSZ + else: + TIOCGWINSZ = 1074295912L + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(sys.stdout.fileno(), TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] + + def set_win_size(self, sig, data): + """ + This function use to set the window size of the terminal! + 设置terminal窗口大小 + """ + try: + win_size = self.get_win_size() + self.chan.resize_pty(height=win_size[0], width=win_size[1]) + except Exception: + pass + + def log_record(self): + """ + Logging user command and output. + 记录用户的日志 + """ + tty_log_dir = os.path.join(log_dir, 'tty') + timestamp_start = int(time.time()) + date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) + time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) + log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start) + today_connect_log_dir = os.path.join(tty_log_dir, date_start) + log_file_path = os.path.join(today_connect_log_dir, log_filename) + dept_name = self.user.dept.name + + pid = os.getpid() + pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() + ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') + + try: + is_dir(today_connect_log_dir) + except OSError: + raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) + + try: + log_file = open(log_file_path, 'a') + except IOError: + raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) + + log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name, + log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) + log_file.write('Start time is %s\n' % datetime.datetime.now()) + log.save() + return log_file, log + + def posix_shell(self): + """ + Use paramiko channel connect server interactive. + 使用paramiko模块的channel,连接后端,进入交互式 + """ + log_file, log = self.log_record() + old_tty = termios.tcgetattr(sys.stdin) + try: + tty.setraw(sys.stdin.fileno()) + tty.setcbreak(sys.stdin.fileno()) + self.chan.settimeout(0.0) + + while True: + try: + r, w, e = select.select([self.chan, sys.stdin], [], []) + except Exception: + pass + + if self.chan in r: + try: + x = self.chan.recv(1024) + if len(x) == 0: + break + sys.stdout.write(x) + sys.stdout.flush() + log_file.write(x) + log_file.flush() + except socket.timeout: + pass + + if sys.stdin in r: + x = os.read(sys.stdin.fileno(), 1) + if len(x) == 0: + break + self.chan.send(x) + + finally: + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) + log_file.write('End time is %s' % datetime.datetime.now()) + log_file.close() + log.is_finished = True + log.handle_finished = False + log.end_time = datetime.datetime.now() + log.save() + + def get_connect_item(self): + """获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码""" + if not self.asset.is_active: + raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) + + if not self.user.is_active: + raise ServerError('该用户被禁用 User %s is not active.' % self.username) + + login_type_dict = { + 'L': self.user.ldap_pwd, + } + + if self.asset.login_type in login_type_dict: + password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type]) + return self.username, password, self.ip, int(self.asset.port) + + elif self.asset.login_type == 'M': + username = self.asset.username + password = CRYPTOR.decrypt(self.asset.password) + return username, password, self.ip, int(self.asset.port) + + else: + raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]') + + def connect(self): + """ + Connect server. + 连接服务器 + """ + username, password, ip, port = self.get_connect_item() + logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port)) + ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % ip + + # 发起ssh连接请求 Make a ssh connection + ssh = paramiko.SSHClient() + ssh.load_system_host_keys() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(ip, port=port, username=username, password=password, compress=True) + except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: + raise ServerError('认证错误 Authentication Error.') + except socket.error: + raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.') + + # 获取连接的隧道并设置窗口大小 Make a channel and set windows size + global channel + win_size = self.get_win_size() + self.chan = channel = ssh.invoke_shell(height=win_size[0], width=win_size[1]) + try: + signal.signal(signal.SIGWINCH, self.set_win_size) + except: + pass + + # 设置PS1并提示 Set PS1 and msg it + channel.send(ps1) + channel.send(login_msg) + + # Make ssh interactive tunnel + self.posix_shell() + + # Shutdown channel socket + channel.close() + ssh.close() + + class PyCrypt(object): """ This class used to encrypt and decrypt password. From 913f93b91f3cad8acc29d8c9263ec9bd35da455c Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 26 Aug 2015 23:31:32 +0800 Subject: [PATCH 012/385] =?UTF-8?q?=E5=88=A0=E9=99=A4=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E5=89=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 25 +++- jumpserver/api.py | 171 ++++++++++++++++++---- jumpserver/context_processors.py | 2 + jumpserver/settings.py | 6 +- jumpserver/urls.py | 6 +- jumpserver/views.py | 126 ++++++++-------- juser/views.py | 238 +++++-------------------------- templates/base.html | 2 + templates/juser/dept_add.html | 47 +----- 9 files changed, 279 insertions(+), 344 deletions(-) diff --git a/connect.py b/connect.py index 0039aa05f..7994205ec 100644 --- a/connect.py +++ b/connect.py @@ -17,8 +17,8 @@ from multiprocessing import Pool os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() -from jumpserver.api import BASE_DIR, ServerError, User, UserGroup, Asset, Jtty, get_object -from jumpserver.api import CRYPTOR, logger, is_dir +from jumpserver.api import ServerError, User, Asset, Jtty, get_object +from jumpserver.api import logger from jumpserver.api import BisGroup as AssetGroup login_user = get_object(User, username=getpass.getuser()) @@ -27,7 +27,7 @@ login_user = get_object(User, username=getpass.getuser()) def color_print(msg, color='red', exits=False): """ Print colorful string. - 颜色打印 + 颜色打印字符或者退出 """ color_msg = {'blue': '\033[1;36m%s\033[0m', 'green': '\033[1;32m%s\033[0m', @@ -40,7 +40,10 @@ def color_print(msg, color='red', exits=False): def verify_connect(user, option): - """鉴定用户是否有该主机权限 或 匹配到的ip是否唯一""" + """ + Check user was permed or not . Check ip is unique or not. + 鉴定用户是否有该主机权限 或 匹配到的ip是否唯一 + """ ip_matched = [] try: assets_info = login_user.get_asset_info() @@ -60,7 +63,7 @@ def verify_connect(user, option): logger.debug('%s matched input %s: %s' % (login_user.username, option, ip_matched)) ip_matched = list(set(ip_matched)) - if len(ip_matched) > 1: + if len(ip_matched) > 1: # 如果匹配ip不唯一 ip_comment = {} for ip in ip_matched: ip_comment[ip] = assets_info[ip][2] @@ -71,15 +74,19 @@ def verify_connect(user, option): else: print '%-15s' % ip print '' - elif len(ip_matched) < 1: + elif len(ip_matched) < 1: # 如果没匹配到 color_print('没有该主机,或者您没有该主机的权限 No Permission or No host.', 'red') - else: + else: # 恰好是1个 asset = get_object(Asset, ip=ip_matched[0]) jtty = Jtty(user, asset) jtty.connect() 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. @@ -162,6 +169,10 @@ def print_prompt(): def main(): + """ + he he + 主程序 + """ if not login_user: # 判断用户是否存在 color_print(u'没有该用户,或许你是以root运行的 No that user.', exits=True) diff --git a/jumpserver/api.py b/jumpserver/api.py index 6cff43abd..35fd8063e 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -4,6 +4,7 @@ import os, sys, time from ConfigParser import ConfigParser import getpass from Crypto.Cipher import AES +import crypt from binascii import b2a_hex, a2b_hex import ldap from ldap import modlist @@ -13,15 +14,18 @@ import random import subprocess import paramiko import struct, fcntl, signal,socket, select, fnmatch +from functools import partial + from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.http import HttpResponse, Http404 -from django.shortcuts import render_to_response +from django.template import RequestContext from juser.models import User, UserGroup, DEPT from jasset.models import Asset, BisGroup, IDC from jlog.models import Log from jasset.models import AssetAlias from django.core.exceptions import ObjectDoesNotExist from django.http import HttpResponseRedirect +from django.shortcuts import render_to_response from django.core.mail import send_mail import json import logging @@ -50,20 +54,29 @@ SEND_PORT = CONF.get('base', 'port') MAIL_FROM = CONF.get('mail', 'email_host_user') log_dir = os.path.join(BASE_DIR, 'logs') + def set_log(level): + """ + return a log file object + 根据提示设置log打印 + """ log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR, 'critical': logging.CRITICAL} - logger = logging.getLogger('jumpserver') - logger.setLevel(logging.DEBUG) + logger_f = logging.getLogger('jumpserver') + logger_f.setLevel(logging.DEBUG) fh = logging.FileHandler(JLOG_FILE) fh.setLevel(log_level_total.get(level, logging.DEBUG)) formatter = logging.Formatter('%(asctime)s - %(filename)s - %(levelname)s - %(message)s') fh.setFormatter(formatter) - logger.addHandler(fh) - return logger + logger_f.addHandler(fh) + return logger_f class LDAPMgmt(): + """ + LDAP class for add, select, del, update + LDAP 管理类,增删改查 + """ def __init__(self, host_url, base_dn, @@ -77,6 +90,10 @@ class LDAPMgmt(): self.conn.simple_bind_s(root_cn, root_pw) def list(self, filter, scope=ldap.SCOPE_SUBTREE, attr=None): + """ + query + 查询 + """ result = {} try: ldap_result = self.conn.search_s(self.ldap_base_dn, scope, filter, attr) @@ -90,6 +107,10 @@ class LDAPMgmt(): print e def add(self, dn, attrs): + """ + add + 添加 + """ try: ldif = modlist.addModlist(attrs) self.conn.add_s(dn, ldif) @@ -97,6 +118,10 @@ class LDAPMgmt(): print e def modify(self, dn, attrs): + """ + modify + 更改 + """ try: attr_s = [] for k, v in attrs.items(): @@ -106,6 +131,10 @@ class LDAPMgmt(): print e def delete(self, dn): + """ + delete + 删除 + """ try: self.conn.delete_s(dn) except ldap.LDAPError, e: @@ -113,6 +142,10 @@ class LDAPMgmt(): def page_list_return(total, current=1): + """ + page + 分页,返回本次分页的最小页数到最大页数列表 + """ min_page = current - 2 if current - 4 > 0 else 1 max_page = min_page + 4 if min_page + 4 < total else total @@ -120,7 +153,10 @@ def page_list_return(total, current=1): def pages(posts, r): - """分页公用函数""" + """ + page public function , return page's object tuple + 分页公用函数,返回分页的对象元组 + """ contact_list = posts p = paginator = Paginator(contact_list, 10) try: @@ -148,6 +184,10 @@ def pages(posts, r): class Jtty(object): + """ + A virtual tty class + 一个虚拟终端类,实现连接ssh和记录日志 + """ def __init__(self, user, asset): self.chan = None self.username = user.username @@ -260,7 +300,10 @@ class Jtty(object): log.save() def get_connect_item(self): - """获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码""" + """ + get args for connect: ip, port, username, passwd + 获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码 + """ if not self.asset.is_active: raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) @@ -283,15 +326,13 @@ class Jtty(object): else: raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]') - def connect(self): + def get_connection(self): """ - Connect server. - 连接服务器 + Get the ssh connection for reuse + 获取连接套接字 """ username, password, ip, port = self.get_connect_item() logger.debug("username: %s, password: %s, ip: %s, port: %s" % (username, password, ip, port)) - ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % ip # 发起ssh连接请求 Make a ssh connection ssh = paramiko.SSHClient() @@ -303,6 +344,19 @@ class Jtty(object): raise ServerError('认证错误 Authentication Error.') except socket.error: raise ServerError('端口可能不对 Connect SSH Socket Port Error, Please Correct it.') + else: + return ssh + + def connect(self): + """ + Connect server. + 连接服务器 + """ + ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.asset.ip + + # 发起ssh连接请求 Make a ssh connection + ssh = self.get_connection() # 获取连接的隧道并设置窗口大小 Make a channel and set windows size global channel @@ -324,11 +378,18 @@ class Jtty(object): channel.close() ssh.close() + def execute(self, cmd): + """ + execute cmd on the asset + 执行命令 + """ + pass + class PyCrypt(object): """ This class used to encrypt and decrypt password. - 对称加密库 + 加密类 """ def __init__(self, key): @@ -353,12 +414,24 @@ class PyCrypt(object): @staticmethod def md5_crypt(string): + """ + md5 encrypt method + md5非对称加密方法 + """ return hashlib.new("md5", string).hexdigest() + @staticmethod + def gen_sha512(salt, password): + """ + generate sha512 format password + 生成sha512加密密码 + """ + return crypt.crypt(password, '$6$%s$' % salt) + def encrypt(self, passwd=None): """ encrypt gen password - 加密生成密码 + 对称加密之加密生成密码 """ if not passwd: passwd = self.random_pass() @@ -376,6 +449,10 @@ class PyCrypt(object): return b2a_hex(cipher_text) def decrypt(self, text): + """ + decrypt pass base the same key + 对称加密之解密,同一个加密随机数 + """ cryptor = AES.new(self.key, self.mode, b'8122ca7d906ad5e1') try: plain_text = cryptor.decrypt(a2b_hex(text)) @@ -386,10 +463,18 @@ class PyCrypt(object): class ServerError(Exception): + """ + self define exception + 自定义异常 + """ pass def get_object(model, **kwargs): + """ + use this function for query + 使用改封装函数查询数据库 + """ try: the_object = model.objects.get(**kwargs) except ObjectDoesNotExist: @@ -399,7 +484,8 @@ def get_object(model, **kwargs): def require_role(role='user'): """ - 要求用户是某种角色 ["super", "admin", "user"] + decorator for require user role in ["super", "admin", "user"] + 要求用户是某种角色 ["super", "admin", "user"]的装饰器 """ def _deco(func): def __deco(request, *args, **kwargs): @@ -413,27 +499,30 @@ def require_role(role='user'): if request.session.get('role_id', 0) != 2: return HttpResponseRedirect('/') return func(request, *args, **kwargs) - return __deco() + return __deco return _deco def is_role_request(request, role='user'): """ - :param request: 请求 - :param role: 角色 - :return: bool + require this request of user is right 要求请求角色正确 """ role_all = {'user': 0, 'admin': 1, 'super': 2} - if request.session.get('role_id') == role_all.get(role, '0'): + if request.session.get('role_id') == role_all.get(role, 0): return True else: return False -@require_role def get_session_user_dept(request): + """ + get department of the user in session + 获取session中用户的部门 + """ user_id = request.session.get('user_id', 0) + print '#' * 20 + print user_id user = User.objects.filter(id=user_id) if user: user = user[0] @@ -443,6 +532,10 @@ def get_session_user_dept(request): @require_role def get_session_user_info(request): + """ + get the user info of the user in session, for example id, username etc. + 获取用户的信息 + """ user_id = request.session.get('user_id', 0) user = User.objects.filter(id=user_id) if user: @@ -452,6 +545,10 @@ def get_session_user_info(request): def get_user_dept(request): + """ + get the user dept id + 获取用户的部门id + """ user_id = request.session.get('user_id') if user_id: user_dept = User.objects.get(id=user_id).dept @@ -466,16 +563,24 @@ def api_user(request): return HttpResponse(json_data) -# def view_splitter(request, su=None, adm=None): -# if is_super_user(request): -# return su(request) -# elif is_group_admin(request): -# return adm(request) -# else: -# return HttpResponseRedirect('/login/') +def view_splitter(request, su=None, adm=None): + """ + for different user use different view + 视图分页器 + """ + if is_role_request(request, 'super'): + return su(request) + elif is_role_request(request, 'admin'): + return adm(request) + else: + return HttpResponseRedirect('/login/') def validate(request, user_group=None, user=None, asset_group=None, asset=None, edept=None): + """ + validate the user request + 判定用户请求是否合法 + """ dept = get_session_user_dept(request)[1] if edept: if dept.id != int(edept[0]): @@ -565,12 +670,18 @@ def verify(request, user_group=None, user=None, asset_group=None, asset=None, ed def bash(cmd): - """执行bash命令""" + """ + run a bash shell command + 执行bash命令 + """ return subprocess.call(cmd, shell=True) def is_dir(dir_name, username='root', mode=0755): - """目录存在,如果不存在就建立,并且权限正确""" + """ + insure the dir exist and mode ok + 目录存在,如果不存在就建立,并且权限正确 + """ if not os.path.isdir(dir_name): os.makedirs(dir_name) bash("chown %s:%s '%s'" % (username, username, dir_name)) diff --git a/jumpserver/context_processors.py b/jumpserver/context_processors.py index aac09c7a7..2beeed3a0 100644 --- a/jumpserver/context_processors.py +++ b/jumpserver/context_processors.py @@ -14,10 +14,12 @@ def name_proc(request): host_active_num = Asset.objects.filter(is_active=True).count() else: user, dept = get_session_user_dept(request) + print user, dept user_total_num = dept.user_set.all().count() user_active_num = dept.user_set.filter(is_active=True).count() host_total_num = dept.asset_set.all().count() host_active_num = dept.asset_set.all().filter(is_active=True).count() + pass username = User.objects.get(id=user_id).name apply_info = Apply.objects.filter(admin=username, status=0, read=0) diff --git a/jumpserver/settings.py b/jumpserver/settings.py index ce4d7e8b5..96f1617a8 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -56,9 +56,9 @@ INSTALLED_APPS = ( 'django.contrib.humanize', 'jumpserver', 'juser', - 'jasset', - 'jperm', - 'jlog', + # 'jasset', + # 'jperm', + # 'jlog', ) MIDDLEWARE_CLASSES = ( diff --git a/jumpserver/urls.py b/jumpserver/urls.py index bd60d04ba..c7a3eaf31 100644 --- a/jumpserver/urls.py +++ b/jumpserver/urls.py @@ -14,9 +14,9 @@ urlpatterns = patterns('', (r'^file/download/$', 'jumpserver.views.download'), (r'^error/$', 'jumpserver.views.httperror'), (r'^juser/', include('juser.urls')), - (r'^jasset/', include('jasset.urls')), - (r'^jlog/', include('jlog.urls')), - (r'^jperm/', include('jperm.urls')), + # (r'^jasset/', include('jasset.urls')), + # (r'^jlog/', include('jlog.urls')), + # (r'^jperm/', include('jperm.urls')), (r'^node_auth/', 'jumpserver.views.node_auth'), ) diff --git a/jumpserver/views.py b/jumpserver/views.py index fd151d824..e4706eab3 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -1,15 +1,18 @@ # coding: utf-8 from __future__ import division +import uuid +import urllib + from django.db.models import Count from django.shortcuts import render_to_response from django.template import RequestContext from django.http import HttpResponseNotFound +from django.http import HttpResponse from jperm.models import Apply import paramiko from jumpserver.api import * -import uuid -import urllib + def getDaysByNum(num): @@ -44,7 +47,7 @@ def get_data(data, items, option): return dic -@require_login +@require_role(role='user') def index_cu(request): user_id = request.session.get('user_id') user = User.objects.filter(id=user_id) @@ -53,7 +56,7 @@ def index_cu(request): login_types = {'L': 'LDAP', 'M': 'MAP'} user_id = request.session.get('user_id') username = User.objects.get(id=user_id).username - posts = user_perm_asset_api(username) + posts = user.get_asset() host_count = len(posts) new_posts = [] post_five = [] @@ -68,16 +71,16 @@ def index_cu(request): return render_to_response('index_cu.html', locals(), context_instance=RequestContext(request)) -@require_login +@require_role(role='user') def index(request): li_date, li_str = getDaysByNum(7) today = datetime.datetime.now().day from_week = datetime.datetime.now() - datetime.timedelta(days=7) - if is_common_user(request): + if is_role_request(request, 'user'): return index_cu(request) - elif is_super_user(request): + elif is_role_request(request, 'super'): users = User.objects.all() hosts = Asset.objects.all() online = Log.objects.filter(is_finished=0) @@ -87,7 +90,7 @@ def index(request): active_hosts = Asset.objects.filter(is_active=1) week_data = Log.objects.filter(start_time__range=[from_week, datetime.datetime.now()]) - elif is_group_admin(request): + elif is_role_request(request, 'admin'): user = get_session_user_info(request)[2] dept_name, dept = get_session_user_info(request)[4:] users = User.objects.filter(dept=dept) @@ -205,7 +208,7 @@ def login(request): user_filter = User.objects.filter(username=username) if user_filter: user = user_filter[0] - if md5_crypt(password) == user.password: + if PyCrypt.md5_crypt(password) == user.password: request.session['user_id'] = user.id user_filter.update(last_login=datetime.datetime.now()) if user.role == 'SU': @@ -216,7 +219,7 @@ def login(request): request.session['role_id'] = 0 response = HttpResponseRedirect('/', ) response.set_cookie('username', username, expires=604800) - response.set_cookie('seed', md5_crypt(password), expires=604800) + response.set_cookie('seed', PyCrypt.md5_crypt(password), expires=604800) return response else: error = '密码错误,请重新输入。' @@ -248,7 +251,7 @@ def filter_ajax_api(request): def install(request): from juser.models import DEPT, User if User.objects.filter(id=5000): - return httperror(request, 'Jumpserver已初始化,不能重复安装!') + return http_error(request, 'Jumpserver已初始化,不能重复安装!') dept = DEPT(id=1, name="超管部", comment="超级管理部门") dept.save() @@ -257,7 +260,7 @@ def install(request): IDC(id=1, name="默认", comment="默认IDC").save() BisGroup(id=1, name="ALL", dept=dept, comment="所有主机组").save() - User(id=5000, username="admin", password=md5_crypt('admin'), + User(id=5000, username="admin", password=PyCrypt.md5_crypt('admin'), name='admin', email='admin@jumpserver.org', role='SU', is_active=True, dept=dept).save() return http_success(request, u'Jumpserver初始化成功') @@ -278,55 +281,56 @@ def transfer(sftp, filenames): def upload(request): - user, dept = get_session_user_dept(request) - if request.method == 'POST': - hosts = request.POST.get('hosts') - upload_files = request.FILES.getlist('file[]', None) - upload_dir = "/tmp/%s" % user.username - is_dir(upload_dir) - date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") - hosts_list = hosts.split(',') - user_hosts = get_user_host(user.username).keys() - unperm_hosts = [] - filenames = {} - for ip in hosts_list: - if ip not in user_hosts: - unperm_hosts.append(ip) - - if not hosts: - return HttpResponseNotFound(u'地址不能为空') - - if unperm_hosts: - print hosts_list - return HttpResponseNotFound(u'%s 没有权限.' % ', '.join(unperm_hosts)) - - for upload_file in upload_files: - file_path = '%s/%s.%s' % (upload_dir, upload_file.name, date_now) - filenames[upload_file.name] = file_path - f = open(file_path, 'w') - for chunk in upload_file.chunks(): - f.write(chunk) - f.close() - - sftps = [] - for host in hosts_list: - username, password, host, port = get_connect_item(user.username, host) - try: - t = paramiko.Transport((host, port)) - t.connect(username=username, password=password) - sftp = paramiko.SFTPClient.from_transport(t) - sftps.append(sftp) - except paramiko.AuthenticationException: - return HttpResponseNotFound(u'%s 连接失败.' % host) - - # pool = Pool(processes=5) - for sftp in sftps: - transfer(sftp, filenames) - # pool.close() - # pool.join() - return HttpResponse('传送成功') - - return render_to_response('upload.html', locals(), context_instance=RequestContext(request)) + pass +# user, dept = get_session_user_dept(request) +# if request.method == 'POST': +# hosts = request.POST.get('hosts') +# upload_files = request.FILES.getlist('file[]', None) +# upload_dir = "/tmp/%s" % user.username +# is_dir(upload_dir) +# date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") +# hosts_list = hosts.split(',') +# user_hosts = [asset.ip for asset in user.get_asset()] +# unperm_hosts = [] +# filenames = {} +# for ip in hosts_list: +# if ip not in user_hosts: +# unperm_hosts.append(ip) +# +# if not hosts: +# return HttpResponseNotFound(u'地址不能为空') +# +# if unperm_hosts: +# print hosts_list +# return HttpResponseNotFound(u'%s 没有权限.' % ', '.join(unperm_hosts)) +# +# for upload_file in upload_files: +# file_path = '%s/%s.%s' % (upload_dir, upload_file.name, date_now) +# filenames[upload_file.name] = file_path +# f = open(file_path, 'w') +# for chunk in upload_file.chunks(): +# f.write(chunk) +# f.close() +# +# sftps = [] +# for host in hosts_list: +# username, password, host, port = get_connect_item(user.username, host) +# try: +# t = paramiko.Transport((host, port)) +# t.connect(username=username, password=password) +# sftp = paramiko.SFTPClient.from_transport(t) +# sftps.append(sftp) +# except paramiko.AuthenticationException: +# return HttpResponseNotFound(u'%s 连接失败.' % host) +# +# # pool = Pool(processes=5) +# for sftp in sftps: +# transfer(sftp, filenames) +# # pool.close() +# # pool.join() +# return HttpResponse('传送成功') +# +# return render_to_response('upload.html', locals(), context_instance=RequestContext(request)) def node_auth(request): diff --git a/juser/views.py b/juser/views.py index 83bca8921..f3d5525f4 100644 --- a/juser/views.py +++ b/juser/views.py @@ -4,174 +4,15 @@ import random from Crypto.PublicKey import RSA -import crypt from django.db.models import Q from django.template import RequestContext from django.db.models import ObjectDoesNotExist -from jumpserver.api import * +from juser.user_api import * -def md5_crypt(string): - return hashlib.new("md5", string).hexdigest() - - -def gen_rand_pwd(num): - """ - generate random password - 生成随机密码 - """ - seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - salt_list = [] - for i in range(num): - salt_list.append(random.choice(seed)) - salt = ''.join(salt_list) - return salt - - -def gen_sha512(salt, password): - """ - generate sha512 format password - 生成sha512加密密码 - """ - return crypt.crypt(password, '$6$%s$' % salt) - - -def group_add_user(group, user_id=None, username=None): - """ - 用户组中添加用户 - UserGroup Add a user - """ - if user_id: - user = get_object(User, id=user_id) - else: - user = get_object(User, username=username) - group.user_set.add(user) - - -def db_add_group(**kwargs): - name = kwargs.get('name') - group = UserGroup.objects.filter(name=name) - users = kwargs.pop('users') - if group: - raise ServerError(u'用户组 %s 已经存在' % name) - group = UserGroup(**kwargs) - group.save() - for user_id in users: - group_add_user(group, user_id) - - -def db_add_user(**kwargs): - groups_post = kwargs.pop('groups') - user = User(**kwargs) - user.save() - if groups_post: - group_select = [] - for group_id in groups_post: - group = UserGroup.objects.filter(id=group_id) - group_select.extend(group) - user.group = group_select - return user - - -def db_update_user(**kwargs): - groups_post = kwargs.pop('groups') - user_id = kwargs.pop('user_id') - user = User.objects.filter(id=user_id) - if user: - user.update(**kwargs) - user = User.objects.get(id=user_id) - user.save() - - if groups_post: - group_select = [] - for group_id in groups_post: - group = UserGroup.objects.filter(id=group_id) - group_select.extend(group) - user.group = group_select - - -def db_del_user(username): - try: - user = User.objects.get(username=username) - user.delete() - except ObjectDoesNotExist: - pass - - -def gen_ssh_key(username, password=None, length=2048): - private_key_dir = os.path.join(BASE_DIR, 'keys/jumpserver/') - private_key_file = os.path.join(private_key_dir, username+".pem") - public_key_dir = '/home/%s/.ssh/' % username - public_key_file = os.path.join(public_key_dir, 'authorized_keys') - is_dir(private_key_dir) - is_dir(public_key_dir, username, mode=0700) - - key = RSA.generate(length) - with open(private_key_file, 'w') as pri_f: - pri_f.write(key.exportKey('PEM', password)) - os.chmod(private_key_file, 0600) - - pub_key = key.publickey() - with open(public_key_file, 'w') as pub_f: - pub_f.write(pub_key.exportKey('OpenSSH')) - os.chmod(public_key_file, 0600) - bash('chown %s:%s %s' % (username, username, public_key_file)) - - -def server_add_user(username, password, ssh_key_pwd): - bash("useradd '%s'; echo '%s' | passwd --stdin '%s'" % (username, password, username)) - gen_ssh_key(username, ssh_key_pwd) - - -def server_del_user(username): - bash('userdel -r %s' % username) - - -def ldap_add_user(username, ldap_pwd): - user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) - password_sha512 = gen_sha512(gen_rand_pwd(6), ldap_pwd) - user = User.objects.filter(username=username) - if user: - user = user[0] - else: - raise ServerError(u'用户 %s 不存在' % username) - - user_attr = {'uid': [str(username)], - 'cn': [str(username)], - 'objectClass': ['account', 'posixAccount', 'top', 'shadowAccount'], - 'userPassword': ['{crypt}%s' % password_sha512], - 'shadowLastChange': ['16328'], - 'shadowMin': ['0'], - 'shadowMax': ['99999'], - 'shadowWarning': ['7'], - 'loginShell': ['/bin/bash'], - 'uidNumber': [str(user.id)], - 'gidNumber': [str(user.id)], - 'homeDirectory': [str('/home/%s' % username)]} - - group_dn = "cn=%s,ou=Group,%s" % (username, LDAP_BASE_DN) - group_attr = {'objectClass': ['posixGroup', 'top'], - 'cn': [str(username)], - 'userPassword': ['{crypt}x'], - 'gidNumber': [str(user.id)]} - - ldap_conn.add(user_dn, user_attr) - ldap_conn.add(group_dn, group_attr) - - -def ldap_del_user(username): - user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) - group_dn = "cn=%s,ou=Group,%s" % (username, LDAP_BASE_DN) - sudo_dn = 'cn=%s,ou=Sudoers,%s' % (username, LDAP_BASE_DN) - - ldap_conn.delete(user_dn) - ldap_conn.delete(group_dn) - ldap_conn.delete(sudo_dn) - - -@require_super_user +@require_role(role='super') def dept_add(request): header_title, path1, path2 = '添加部门', '用户管理', '添加部门' if request.method == 'POST': @@ -192,7 +33,7 @@ def dept_add(request): return render_to_response('juser/dept_add.html', locals(), context_instance=RequestContext(request)) -@require_super_user +@require_role(role='super') def dept_list(request): header_title, path1, path2 = '查看部门', '用户管理', '查看部门' keyword = request.GET.get('search') @@ -206,7 +47,7 @@ def dept_list(request): return render_to_response('juser/dept_list.html', locals(), context_instance=RequestContext(request)) -@require_admin +@require_role(role='admin') def dept_list_adm(request): header_title, path1, path2 = '查看部门', '用户管理', '查看部门' user, dept = get_session_user_dept(request) @@ -226,7 +67,7 @@ def chg_role(request): return HttpResponseRedirect('/') -@require_super_user +@require_role(role='super') def dept_detail(request): dept_id = request.GET.get('id', None) if not dept_id: @@ -238,7 +79,7 @@ def dept_detail(request): return render_to_response('juser/dept_detail.html', locals(), context_instance=RequestContext(request)) -@require_super_user +@require_role(role='super') def dept_del(request): dept_id = request.GET.get('id', None) if not dept_id or dept_id in ['1', '2']: @@ -276,7 +117,7 @@ def dept_member_update(dept, users_id_list): user.save() -@require_super_user +@require_role(role='super') def dept_del_ajax(request): dept_ids = request.POST.get('dept_ids') for dept_id in dept_ids.split(','): @@ -285,7 +126,7 @@ def dept_del_ajax(request): return HttpResponse("删除成功") -@require_super_user +@require_role(role='super') def dept_edit(request): header_title, path1, path2 = '部门编辑', '用户管理', '部门编辑' if request.method == 'GET': @@ -331,7 +172,7 @@ def dept_user_ajax(request): -@require_super_user +@require_role(role='super') def group_add(request): error = '' msg = '' @@ -372,7 +213,7 @@ def group_add(request): return render_to_response('juser/group_add.html', locals(), context_instance=RequestContext(request)) -@require_admin +@require_role(role='admin') def group_add_adm(request): error = '' msg = '' @@ -403,7 +244,7 @@ def group_add_adm(request): return render_to_response('juser/group_add.html', locals(), context_instance=RequestContext(request)) -@require_super_user +@require_role(role='super') def group_list(request): header_title, path1, path2 = '查看小组', '用户管理', '查看小组' keyword = request.GET.get('search', '') @@ -423,7 +264,7 @@ def group_list(request): return render_to_response('juser/group_list.html', locals(), context_instance=RequestContext(request)) -@require_admin +@require_role(role='admin') def group_list_adm(request): header_title, path1, path2 = '查看部门小组', '用户管理', '查看小组' keyword = request.GET.get('search', '') @@ -438,7 +279,7 @@ def group_list_adm(request): return render_to_response('juser/group_list.html', locals(), context_instance=RequestContext(request)) -@require_admin +@require_role(role='admin') def group_detail(request): group_id = request.GET.get('id', None) if not group_id: @@ -448,7 +289,7 @@ def group_detail(request): return render_to_response('juser/group_detail.html', locals(), context_instance=RequestContext(request)) -@require_super_user +@require_role(role='super') def group_del(request): group_id = request.GET.get('id', '') if not group_id: @@ -457,7 +298,7 @@ def group_del(request): return HttpResponseRedirect('/juser/group_list/') -@require_admin +@require_role(role='admin') def group_del_adm(request): group_id = request.GET.get('id', '') if not validate(request, user_group=[group_id]): @@ -468,7 +309,7 @@ def group_del_adm(request): return HttpResponseRedirect('/juser/group_list/') -@require_admin +@require_role(role='admin') def group_del_ajax(request): group_ids = request.POST.get('group_ids') group_ids = group_ids.split(',') @@ -490,7 +331,7 @@ def group_update_member(group_id, users_id_list): group.user_set.add(user) -@require_super_user +@require_role(role='super') def group_edit(request): error = '' msg = '' @@ -538,7 +379,7 @@ def group_edit(request): return HttpResponseRedirect('/juser/group_list/') -@require_admin +@require_role(role='admin') def group_edit_adm(request): error = '' msg = '' @@ -586,7 +427,7 @@ def group_edit_adm(request): return HttpResponseRedirect('/juser/group_list/') -@require_super_user +@require_role(role='super') def user_add(request): error = '' msg = '' @@ -597,15 +438,15 @@ def user_add(request): if request.method == 'POST': username = request.POST.get('username', '') - password = gen_rand_pwd(16) + password = PyCrypt.gen_rand_pwd(16) name = request.POST.get('name', '') email = request.POST.get('email', '') dept_id = request.POST.get('dept_id') groups = request.POST.getlist('groups', []) role_post = request.POST.get('role', 'CU') - ssh_key_pwd = gen_rand_pwd(16) + ssh_key_pwd = PyCrypt.gen_rand_pwd(16) is_active = True if request.POST.get('is_active', '1') == '1' else False - ldap_pwd = gen_rand_pwd(16) + ldap_pwd = PyCrypt.gen_rand_pwd(16) try: if '' in [username, password, ssh_key_pwd, name, groups, role_post, is_active]: @@ -667,7 +508,7 @@ def user_add(request): return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) -@require_admin +@require_role(role='admin') def user_add_adm(request): error = '' msg = '' @@ -677,13 +518,13 @@ def user_add_adm(request): if request.method == 'POST': username = request.POST.get('username', '') - password = gen_rand_pwd(16) + password = PyCrypt.gen_rand_pwd(16) name = request.POST.get('name', '') email = request.POST.get('email', '') groups = request.POST.getlist('groups', []) - ssh_key_pwd = gen_rand_pwd(16) + ssh_key_pwd = PyCrypt.gen_rand_pwd(16) is_active = True if request.POST.get('is_active', '1') == '1' else False - ldap_pwd = gen_rand_pwd(16) + ldap_pwd = PyCrypt.gen_rand_pwd(16) try: if '' in [username, password, ssh_key_pwd, name, groups, is_active]: @@ -739,7 +580,7 @@ def user_add_adm(request): return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) -@require_super_user +@require_role(role='super') def user_list(request): user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} header_title, path1, path2 = '查看用户', '用户管理', '用户列表' @@ -768,7 +609,7 @@ def user_list(request): return render_to_response('juser/user_list.html', locals(), context_instance=RequestContext(request)) -@require_admin +@require_role(role='admin') def user_list_adm(request): user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} header_title, path1, path2 = '查看用户', '用户管理', '用户列表' @@ -793,7 +634,7 @@ def user_list_adm(request): return render_to_response('juser/user_list.html', locals(), context_instance=RequestContext(request)) -@require_login +@require_role(role='user') def user_detail(request): header_title, path1, path2 = '查看用户', '用户管理', '用户详情' if request.session.get('role_id') == 0: @@ -810,7 +651,7 @@ def user_detail(request): user = User.objects.filter(id=user_id) if user: user = user[0] - asset_group_permed = user_perm_group_api(user) + asset_group_permed = user.get_asset_group() logs_last = Log.objects.filter(user=user.name).order_by('-start_time')[0:10] logs_all = Log.objects.filter(user=user.name).order_by('-start_time') logs_num = len(logs_all) @@ -818,7 +659,7 @@ def user_detail(request): return render_to_response('juser/user_detail.html', locals(), context_instance=RequestContext(request)) -@require_admin +@require_role(role='admin') def user_del(request): user_id = request.GET.get('id', '') if not user_id: @@ -838,7 +679,7 @@ def user_del(request): return HttpResponseRedirect('/juser/user_list/') -@require_admin +@require_role(role='admin') def user_del_ajax(request): user_ids = request.POST.get('ids') user_ids = user_ids.split(',') @@ -857,7 +698,7 @@ def user_del_ajax(request): return HttpResponse('删除成功') -@require_super_user +@require_role(role='super') def user_edit(request): header_title, path1, path2 = '编辑用户', '用户管理', '用户编辑' if request.method == 'GET': @@ -920,7 +761,7 @@ def user_edit(request): return render_to_response('juser/user_edit.html', locals(), context_instance=RequestContext(request)) -@require_admin +@require_role(role='admin') def user_edit_adm(request): header_title, path1, path2 = '编辑用户', '用户管理', '用户编辑' user, dept = get_session_user_dept(request) @@ -1020,21 +861,18 @@ def chg_info(request): return render_to_response('juser/chg_info.html', locals(), context_instance=RequestContext(request)) - - - -@require_login +@require_role(role='user') def down_key(request): user_id = '' - if is_super_user(request): + if is_role_request(request, 'super'): user_id = request.GET.get('id') - if is_group_admin(request): + if is_role_request(request, 'admin'): user_id = request.GET.get('id') if not validate(request, user=[user_id]): user_id = request.session.get('user_id') - if is_common_user(request): + if is_role_request(request, 'user'): user_id = request.session.get('user_id') if user_id: diff --git a/templates/base.html b/templates/base.html index 729ec6fb7..d325e9ff1 100644 --- a/templates/base.html +++ b/templates/base.html @@ -11,6 +11,7 @@ {% include 'link_css.html' %} {% include 'head_script.html' %} + {% block self_head_css_js %} {% endblock %} @@ -30,4 +31,5 @@ {% include 'foot_script.html' %} + {% block self_footer_js %} {% endblock %} diff --git a/templates/juser/dept_add.html b/templates/juser/dept_add.html index 799336f81..322df22e5 100644 --- a/templates/juser/dept_add.html +++ b/templates/juser/dept_add.html @@ -15,12 +15,7 @@ - + @@ -38,37 +33,13 @@
{% if error %} - + {% else %} - + {% endif %}
- -{#
#} -{#
#} -{# #} -{#
#} -{# #} -{#
#} -{#
#} -{#
#} -{# #} -{# #} -{#
#} -{#
#} -{#
#} -{#
#} -{# #} -{#
#} -{#
#} -{#
#}
@@ -95,6 +66,9 @@
+{% endblock %} + +{% block self_footer_js %} -{% endblock %} \ No newline at end of file diff --git a/templates/juser/dept_detail.html b/templates/juser/dept_detail.html deleted file mode 100644 index 81aab3f79..000000000 --- a/templates/juser/dept_detail.html +++ /dev/null @@ -1,116 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
查看部门
- -
- -
-
- 添加部门 - 删除所选 - -
- - - - - - - - - - - - - - - {% for dept in contacts.object_list %} - - - - - - - - - - {% endfor %} - -
- - 部门名称小组数目成员数目主机数目备注操作
- - {{ dept.name }} {{ dept.id | dept_group_num }} {{ dept.id | dept_user_num}} {{ dept.id | dept_asset_num}} {{ dept.comment }} - 详情 - 编辑 - 删除 -
-
-
-
- Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries -
-
- {% include 'paginator.html' %} -
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/juser/dept_edit.html b/templates/juser/dept_edit.html deleted file mode 100644 index 5ade4bad8..000000000 --- a/templates/juser/dept_edit.html +++ /dev/null @@ -1,133 +0,0 @@ -{% extends 'base.html' %} - -{% block content %} - {% include 'nav_cat_bar.html' %} -
-
-
-
-
-
修改信息
- -
-
-
- {% if error %} -
{{ error }}
- {% endif %} - {% if msg %} -
{{ msg }}
- {% endif %} -
- -
- - -
- -
- -
-
- -
- - - 用户部门是唯一的 - -
-
-
- - -
-
-
-
- -
-
- -
-
-
- -
- -
-
- -
-
-
- - -
-
-
-
-
-
-
-
- - -{% endblock %} \ No newline at end of file diff --git a/templates/juser/dept_list.html b/templates/juser/dept_list.html deleted file mode 100644 index 82bf9d539..000000000 --- a/templates/juser/dept_list.html +++ /dev/null @@ -1,126 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
查看部门
- -
- -
-
- 添加部门 - 删除所选 - -
- - - - - - - - - - - - - - - {% for dept in contacts.object_list %} - - - - - - - - - - {% endfor %} - -
- - 部门名称小组数目成员数目主机数目备注操作
- - {{ dept.name }} {{ dept.id | dept_group_num }} {{ dept.id | dept_user_num}} {{ dept.id | dept_asset_num}} {{ dept.comment }} -{# 详情#} - {% ifequal session_role_id 2 %} - 编辑 - 删除 - {% else %} - 编辑 - 删除 - {% endifequal %} -
-
-
-
- Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries -
-
- {% include 'paginator.html' %} -
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/juser/dept_user_ajax.html b/templates/juser/dept_user_ajax.html deleted file mode 100644 index 3d73f4eae..000000000 --- a/templates/juser/dept_user_ajax.html +++ /dev/null @@ -1,3 +0,0 @@ -{% for user in users %} - -{% endfor %} \ No newline at end of file diff --git a/templates/juser/group_add.html b/templates/juser/group_add.html index c6867e08c..e2f1c61ce 100644 --- a/templates/juser/group_add.html +++ b/templates/juser/group_add.html @@ -15,12 +15,6 @@ - @@ -35,27 +29,15 @@
{{ msg }}
{% endif %}
- +
- +
- {% ifequal session_role_id 2 %} +
- -
- -
-
- {% endifequal %} -
-
- +
@@ -51,7 +46,6 @@ 组名 - 所属部门 成员数目 备注 操作 @@ -64,8 +58,7 @@ {{ group.name }} - {{ group.dept.name }} - {{ group.id | member_count }} + {{ group.id | members_count }} {{ group.comment }} 编辑 @@ -88,25 +81,28 @@
+{% endblock %} +{% block self_footer_js %} - {% endblock %} \ No newline at end of file From 1e170714c07b3e490ac027768109093fe5e5c185 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 29 Aug 2015 00:09:36 +0800 Subject: [PATCH 016/385] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A7=86=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jumpserver.conf | 5 +- jumpserver/api.py | 17 +++-- juser/models.py | 10 +++ juser/urls.py | 2 +- juser/user_api.py | 38 ++++++++-- juser/views.py | 138 +++++++++++++++------------------- templates/juser/user_add.html | 113 ++++++++++------------------ 7 files changed, 156 insertions(+), 167 deletions(-) diff --git a/jumpserver.conf b/jumpserver.conf index 306da1e77..a0a489e54 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -28,8 +28,9 @@ web_socket_host = 192.168.40.140:3000 [mail] +mail_enable = 1 email_host = smtp.qq.com email_port = 25 -email_host_user = 1152704203@qq.com -email_host_password = xxxxx +email_host_user = xxxxxxxxxx@qq.com +email_host_password = xxxxxxxxx email_use_tls = False diff --git a/jumpserver/api.py b/jumpserver/api.py index 43cdf6aae..23988b781 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -51,6 +51,7 @@ LOGIN_NAME = getpass.getuser() LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') SEND_IP = CONF.get('base', 'ip') SEND_PORT = CONF.get('base', 'port') +MAIL_ENABLE = CONF.get('mail', 'mail_enable') MAIL_FROM = CONF.get('mail', 'email_host_user') log_dir = os.path.join(BASE_DIR, 'logs') @@ -397,18 +398,22 @@ class PyCrypt(object): self.mode = AES.MODE_CBC @staticmethod - def random_pass(): + def random_pass(length, especial=False): """ random password 随机生成密码 """ - salt_key = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@$%^&*()_' + salt_key = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' symbol = '!@$%^&*()_' salt_list = [] - for i in range(60): - salt_list.append(random.choice(salt_key)) - for i in range(4): - salt_list.append(random.choice(symbol)) + if especial: + for i in range(length-4): + salt_list.append(random.choice(salt_key)) + for i in range(4): + salt_list.append(random.choice(symbol)) + else: + for i in range(length): + salt_list.append(random.choice(salt_key)) salt = ''.join(salt_list) return salt diff --git a/juser/models.py b/juser/models.py index 1e18f6845..a7bd79943 100644 --- a/juser/models.py +++ b/juser/models.py @@ -119,3 +119,13 @@ class User(models.Model): print '' else: return assets_info + + +class AdminGroup(models.Model): + """ + under the user control group + 用户可以管理的用户组,或组的管理员是该用户 + """ + + user = models.ForeignKey(User) + group = models.ForeignKey(UserGroup) \ No newline at end of file diff --git a/juser/urls.py b/juser/urls.py index 5ff976839..0baa60576 100644 --- a/juser/urls.py +++ b/juser/urls.py @@ -11,7 +11,7 @@ urlpatterns = patterns('juser.views', (r'^group_list/$', group_list), (r'^group_del/$', group_del), (r'^group_del_ajax', group_del_ajax), - (r'^group_edit/$', view_splitter, {'su': group_edit, 'adm': group_edit_adm}), + (r'^group_edit/$',group_edit), (r'^user_add/$', view_splitter, {'su': user_add, 'adm': user_add_adm}), (r'^user_list/$', view_splitter, {'su': user_list, 'adm': user_list_adm}), (r'^user_detail/$', 'user_detail'), diff --git a/juser/user_api.py b/juser/user_api.py index 84c0f915f..c0307938d 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -2,6 +2,7 @@ from Crypto.PublicKey import RSA +from juser.models import AdminGroup from jumpserver.api import * @@ -55,6 +56,8 @@ def db_add_user(**kwargs): 数据库中添加用户 """ groups_post = kwargs.pop('groups') + admin_groups = kwargs.pop('admin_groups') + role = kwargs.get('role', 'CU') user = User(**kwargs) user.save() if groups_post: @@ -63,6 +66,12 @@ def db_add_user(**kwargs): group = UserGroup.objects.filter(id=group_id) group_select.extend(group) user.group = group_select + + if admin_groups and role == 'GA': # 如果是组管理员就要添加组管理员和组到管理组中 + for group_id in admin_groups: + group = get_object(UserGroup, id=group_id) + if group: + AdminGroup(user=user, group=group).save() return user @@ -132,6 +141,27 @@ def server_add_user(username, password, ssh_key_pwd): gen_ssh_key(username, ssh_key_pwd) +def user_add_mail(user, kwargs): + """ + add user send mail + 发送用户添加邮件 + """ + print kwargs + user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} + mail_title = u'恭喜你的跳板机用户 %s 添加成功 Jumpserver' % user.name + mail_msg = u""" + Hi, %s + 您的用户名: %s + 您的角色: %s + 您的web登录密码: %s + 您的ssh密钥文件密码: %s + 密钥下载地址: http://%s:%s/juser/down_key/?id=%s + 说明: 请登陆后再下载密钥! + """ % (user.name, user.username, user_role.get(user.role, u'普通用户'), + kwargs.get('password'), kwargs.get('ssh_key_pwd'), SEND_IP, SEND_PORT, user.id) + send_mail(mail_title, mail_msg, MAIL_FROM, [user.email], fail_silently=False) + + def server_del_user(username): """ delete a user from jumpserver linux system @@ -146,11 +176,9 @@ def ldap_add_user(username, ldap_pwd): 在LDAP中添加用户 """ user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) - password_sha512 = PyCrypt.gen_sha512(PyCrypt.gen_rand_pwd(6), ldap_pwd) - user = User.objects.filter(username=username) - if user: - user = user[0] - else: + password_sha512 = PyCrypt.gen_sha512(PyCrypt.random_pass(6), ldap_pwd) + user = get_object(UserGroup, username=username) + if not user: raise ServerError(u'用户 %s 不存在' % username) user_attr = {'uid': [str(username)], diff --git a/juser/views.py b/juser/views.py index 575f56530..1f02cf486 100644 --- a/juser/views.py +++ b/juser/views.py @@ -171,52 +171,52 @@ def group_edit(request): return render_to_response('juser/group_edit.html', locals(), context_instance=RequestContext(request)) -@require_role(role='admin') -def group_edit_adm(request): - error = '' - msg = '' - header_title, path1, path2 = '修改小组信息', '用户管理', '编辑小组' - user, dept = get_session_user_dept(request) - if request.method == 'GET': - group_id = request.GET.get('id', '') - if not validate(request, user_group=[group_id]): - return HttpResponseRedirect('/juser/group_list/') - group = UserGroup.objects.filter(id=group_id) - if group: - group = group[0] - users_all = dept.user_set.all() - users_selected = group.user_set.all() - users = [user for user in users_all if user not in users_selected] - - return render_to_response('juser/group_edit.html', locals(), context_instance=RequestContext(request)) - else: - group_id = request.POST.get('group_id', '') - group_name = request.POST.get('group_name', '') - comment = request.POST.get('comment', '') - users_selected = request.POST.getlist('users_selected') - - users = [] - try: - if not validate(request, user=users_selected): - raise ServerError(u'右侧非部门用户') - - if not validate(request, user_group=[group_id]): - raise ServerError(u'没有权限修改本组') - - for user_id in users_selected: - users.extend(User.objects.filter(id=user_id)) - - user_group = UserGroup.objects.filter(id=group_id) - if user_group: - user_group.update(name=group_name, comment=comment, dept=dept) - user_group = user_group[0] - user_group.user_set.clear() - user_group.user_set = users - - except ServerError, e: - error = e - - return HttpResponseRedirect('/juser/group_list/') +# @require_role(role='admin') +# def group_edit_adm(request): +# error = '' +# msg = '' +# header_title, path1, path2 = '修改小组信息', '用户管理', '编辑小组' +# user, dept = get_session_user_dept(request) +# if request.method == 'GET': +# group_id = request.GET.get('id', '') +# if not validate(request, user_group=[group_id]): +# return HttpResponseRedirect('/juser/group_list/') +# group = UserGroup.objects.filter(id=group_id) +# if group: +# group = group[0] +# users_all = dept.user_set.all() +# users_selected = group.user_set.all() +# users = [user for user in users_all if user not in users_selected] +# +# return render_to_response('juser/group_edit.html', locals(), context_instance=RequestContext(request)) +# else: +# group_id = request.POST.get('group_id', '') +# group_name = request.POST.get('group_name', '') +# comment = request.POST.get('comment', '') +# users_selected = request.POST.getlist('users_selected') +# +# users = [] +# try: +# if not validate(request, user=users_selected): +# raise ServerError(u'右侧非部门用户') +# +# if not validate(request, user_group=[group_id]): +# raise ServerError(u'没有权限修改本组') +# +# for user_id in users_selected: +# users.extend(User.objects.filter(id=user_id)) +# +# user_group = UserGroup.objects.filter(id=group_id) +# if user_group: +# user_group.update(name=group_name, comment=comment, dept=dept) +# user_group = user_group[0] +# user_group.user_set.clear() +# user_group.user_set = users +# +# except ServerError, e: +# error = e +# +# return HttpResponseRedirect('/juser/group_list/') @require_role(role='super') @@ -224,46 +224,39 @@ def user_add(request): error = '' msg = '' header_title, path1, path2 = '添加用户', '用户管理', '添加用户' - user_role = {'SU': u'超级管理员', 'DA': u'部门管理员', 'CU': u'普通用户'} - dept_all = DEPT.objects.all() + user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} group_all = UserGroup.objects.all() if request.method == 'POST': username = request.POST.get('username', '') - password = PyCrypt.gen_rand_pwd(16) + password = PyCrypt.random_pass(16) name = request.POST.get('name', '') email = request.POST.get('email', '') dept_id = request.POST.get('dept_id') groups = request.POST.getlist('groups', []) - role_post = request.POST.get('role', 'CU') - ssh_key_pwd = PyCrypt.gen_rand_pwd(16) + admin_groups = request.POST.getlist('admin_groups', []) + role = request.POST.get('role', 'CU') + ssh_key_pwd = PyCrypt.random_pass(16) is_active = True if request.POST.get('is_active', '1') == '1' else False - ldap_pwd = PyCrypt.gen_rand_pwd(16) + ldap_pwd = PyCrypt.random_pass(32, especial=True) try: - if '' in [username, password, ssh_key_pwd, name, groups, role_post, is_active]: + if '' in [username, password, ssh_key_pwd, name, groups, role, is_active]: error = u'带*内容不能为空' raise ServerError - user = User.objects.filter(username=username) - if user: + user_test = get_object(User, username=username) + if user_test: error = u'用户 %s 已存在' % username raise ServerError - dept = DEPT.objects.filter(id=dept_id) - if dept: - dept = dept[0] - else: - error = u'部门不存在' - raise ServerError(error) - except ServerError: pass else: try: user = db_add_user(username=username, password=CRYPTOR.md5_crypt(password), - name=name, email=email, dept=dept, - groups=groups, role=role_post, + name=name, email=email, role=role, + groups=groups, admin_groups=admin_groups, ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd), ldap_pwd=CRYPTOR.encrypt(ldap_pwd), is_active=is_active, @@ -272,18 +265,6 @@ def user_add(request): server_add_user(username, password, ssh_key_pwd) if LDAP_ENABLE: ldap_add_user(username, ldap_pwd) - mail_title = u'恭喜你的跳板机用户添加成功 Jumpserver' - mail_msg = """ - Hi, %s - 您的用户名: %s - 您的部门: %s - 您的角色: %s - 您的web登录密码: %s - 您的ssh密钥文件密码: %s - 密钥下载地址: http://%s:%s/juser/down_key/?id=%s - 说明: 请登陆后再下载密钥! - """ % (name, username, dept.name, user_role.get(role_post, ''), - password, ssh_key_pwd, SEND_IP, SEND_PORT, user.id) except Exception, e: error = u'添加用户 %s 失败 %s ' % (username, e) @@ -295,8 +276,9 @@ def user_add(request): except Exception: pass else: - send_mail(mail_title, mail_msg, MAIL_FROM, [email], fail_silently=False) - msg = u'添加用户 %s 成功! 用户密码已发送到 %s 邮箱!' % (username, email) + if MAIL_ENABLE: + user_add_mail(user, kwargs=locals()) + msg = u'添加用户 %s 成功! 用户密码已发送到 %s 邮箱!' % (username, email) return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) diff --git a/templates/juser/user_add.html b/templates/juser/user_add.html index 2a097fae8..c26c1fa5e 100644 --- a/templates/juser/user_add.html +++ b/templates/juser/user_add.html @@ -17,12 +17,6 @@ - @@ -42,26 +36,6 @@ -{#
#} -{#
#} -{# #} -{#
#} -{# #} -{# #} -{# 登陆web的密码#} -{# #} -{#
#} -{#
#} -{#
#} -{#
#} -{# #} -{#
#} -{# #} -{# #} -{# 登陆 Jumpserver 使用的SSH密钥的密码#} -{# #} -{#
#} -{#
#}
@@ -69,22 +43,9 @@
- {% ifequal session_role_id 2 %}
- -
- -
-
- {% endifequal %} -
-
- +
- {% for r, role_name in user_role.items %} - {% ifequal r role_post %} - - {% else %} - - {% endifequal %} - {% endfor %} - + {% for r, role_name in user_role.items %} +
+
+ +
+
+ {% endfor %} +
+
+ {% endifequal %} @@ -118,7 +89,7 @@
- +
@@ -135,7 +106,7 @@
- +
@@ -145,21 +116,22 @@ +{% endblock %} +{% block self_footer_js %} - - {% endblock %} \ No newline at end of file From 44c69ded78204ac6d63ea285c6a05974b7aca3b6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 30 Aug 2015 14:03:10 +0800 Subject: [PATCH 017/385] =?UTF-8?q?=E5=B9=B2=E6=8E=89ldap=E5=89=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jumpserver.conf | 6 +- juser/urls.py | 4 +- juser/user_api.py | 9 ++- juser/views.py | 148 +++++++++++++++++----------------- templates/juser/user_add.html | 19 +++-- 5 files changed, 96 insertions(+), 90 deletions(-) diff --git a/jumpserver.conf b/jumpserver.conf index a0a489e54..dfd5a787c 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -29,8 +29,8 @@ web_socket_host = 192.168.40.140:3000 [mail] mail_enable = 1 -email_host = smtp.qq.com +email_host = smtp.exmail.qq.com email_port = 25 -email_host_user = xxxxxxxxxx@qq.com -email_host_password = xxxxxxxxx +email_host_user = noreply@jumpserver.org +email_host_password = jumpserver1234 email_use_tls = False diff --git a/juser/urls.py b/juser/urls.py index 0baa60576..c359a4f4f 100644 --- a/juser/urls.py +++ b/juser/urls.py @@ -11,8 +11,8 @@ urlpatterns = patterns('juser.views', (r'^group_list/$', group_list), (r'^group_del/$', group_del), (r'^group_del_ajax', group_del_ajax), - (r'^group_edit/$',group_edit), - (r'^user_add/$', view_splitter, {'su': user_add, 'adm': user_add_adm}), + (r'^group_edit/$', group_edit), + (r'^user_add/$', user_add), (r'^user_list/$', view_splitter, {'su': user_list, 'adm': user_list_adm}), (r'^user_detail/$', 'user_detail'), (r'^user_del/$', 'user_del'), diff --git a/juser/user_api.py b/juser/user_api.py index c0307938d..b837a6b6f 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -111,8 +111,9 @@ def db_del_user(username): def gen_ssh_key(username, password=None, length=2048): """ generate a user ssh key in a property dir - 生成一个用户密钥 + 生成一个用户ssh密钥对 """ + print "gen_ssh_key" + str(time.time()) private_key_dir = os.path.join(BASE_DIR, 'keys/jumpserver/') private_key_file = os.path.join(private_key_dir, username+".pem") public_key_dir = '/home/%s/.ssh/' % username @@ -124,12 +125,13 @@ def gen_ssh_key(username, password=None, length=2048): with open(private_key_file, 'w') as pri_f: pri_f.write(key.exportKey('PEM', password)) os.chmod(private_key_file, 0600) - + print "gen_ssh_pub_key" + str(time.time()) pub_key = key.publickey() with open(public_key_file, 'w') as pub_f: pub_f.write(pub_key.exportKey('OpenSSH')) os.chmod(public_key_file, 0600) bash('chown %s:%s %s' % (username, username, public_key_file)) + print "gen_ssh_key_end" + str(time.time()) def server_add_user(username, password, ssh_key_pwd): @@ -146,7 +148,6 @@ def user_add_mail(user, kwargs): add user send mail 发送用户添加邮件 """ - print kwargs user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} mail_title = u'恭喜你的跳板机用户 %s 添加成功 Jumpserver' % user.name mail_msg = u""" @@ -177,7 +178,7 @@ def ldap_add_user(username, ldap_pwd): """ user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) password_sha512 = PyCrypt.gen_sha512(PyCrypt.random_pass(6), ldap_pwd) - user = get_object(UserGroup, username=username) + user = get_object(User, username=username) if not user: raise ServerError(u'用户 %s 不存在' % username) diff --git a/juser/views.py b/juser/views.py index 1f02cf486..4e13104d1 100644 --- a/juser/views.py +++ b/juser/views.py @@ -232,12 +232,12 @@ def user_add(request): password = PyCrypt.random_pass(16) name = request.POST.get('name', '') email = request.POST.get('email', '') - dept_id = request.POST.get('dept_id') groups = request.POST.getlist('groups', []) admin_groups = request.POST.getlist('admin_groups', []) role = request.POST.get('role', 'CU') ssh_key_pwd = PyCrypt.random_pass(16) - is_active = True if request.POST.get('is_active', '1') == '1' else False + extra = request.POST.getlist('extra', []) + is_active = True if '0' in extra else False ldap_pwd = PyCrypt.random_pass(32, especial=True) try: @@ -261,12 +261,10 @@ def user_add(request): ldap_pwd=CRYPTOR.encrypt(ldap_pwd), is_active=is_active, date_joined=datetime.datetime.now()) - - server_add_user(username, password, ssh_key_pwd) if LDAP_ENABLE: ldap_add_user(username, ldap_pwd) - except Exception, e: + except IndexError, e: error = u'添加用户 %s 失败 %s ' % (username, e) try: db_del_user(username) @@ -282,76 +280,76 @@ def user_add(request): return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) -@require_role(role='admin') -def user_add_adm(request): - error = '' - msg = '' - header_title, path1, path2 = '添加用户', '用户管理', '添加用户' - user, dept = get_session_user_dept(request) - group_all = dept.usergroup_set.all() - - if request.method == 'POST': - username = request.POST.get('username', '') - password = PyCrypt.gen_rand_pwd(16) - name = request.POST.get('name', '') - email = request.POST.get('email', '') - groups = request.POST.getlist('groups', []) - ssh_key_pwd = PyCrypt.gen_rand_pwd(16) - is_active = True if request.POST.get('is_active', '1') == '1' else False - ldap_pwd = PyCrypt.gen_rand_pwd(16) - - try: - if '' in [username, password, ssh_key_pwd, name, groups, is_active]: - error = u'带*内容不能为空' - raise ServerError - user = User.objects.filter(username=username) - if user: - error = u'用户 %s 已存在' % username - raise ServerError - - except ServerError: - pass - else: - try: - user = db_add_user(username=username, - password=CRYPTOR.md5_crypt(password), - name=name, email=email, dept=dept, - groups=groups, role='CU', - ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd), - ldap_pwd=CRYPTOR.encrypt(ldap_pwd), - is_active=is_active, - date_joined=datetime.datetime.now()) - - server_add_user(username, password, ssh_key_pwd) - if LDAP_ENABLE: - ldap_add_user(username, ldap_pwd) - - except Exception, e: - error = u'添加用户 %s 失败 %s ' % (username, e) - try: - db_del_user(username) - server_del_user(username) - if LDAP_ENABLE: - ldap_del_user(username) - except Exception: - pass - else: - mail_title = u'恭喜你的跳板机用户添加成功 Jumpserver' - mail_msg = """ - Hi, %s - 您的用户名: %s - 您的部门: %s - 您的角色: %s - 您的web登录密码: %s - 您的ssh密钥文件密码: %s - 密钥下载地址: http://%s:%s/juser/down_key/?id=%s - 说明: 请登陆后再下载密钥! - """ % (name, username, dept.name, '普通用户', - password, ssh_key_pwd, SEND_IP, SEND_PORT, user.id) - send_mail(mail_title, mail_msg, MAIL_FROM, [email], fail_silently=False) - msg = u'添加用户 %s 成功! 用户密码已发送到 %s 邮箱!' % (username, email) - - return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) +# @require_role(role='admin') +# def user_add_adm(request): +# error = '' +# msg = '' +# header_title, path1, path2 = '添加用户', '用户管理', '添加用户' +# user, dept = get_session_user_dept(request) +# group_all = dept.usergroup_set.all() +# +# if request.method == 'POST': +# username = request.POST.get('username', '') +# password = PyCrypt.gen_rand_pwd(16) +# name = request.POST.get('name', '') +# email = request.POST.get('email', '') +# groups = request.POST.getlist('groups', []) +# ssh_key_pwd = PyCrypt.gen_rand_pwd(16) +# is_active = True if request.POST.get('is_active', '1') == '1' else False +# ldap_pwd = PyCrypt.gen_rand_pwd(16) +# +# try: +# if '' in [username, password, ssh_key_pwd, name, groups, is_active]: +# error = u'带*内容不能为空' +# raise ServerError +# user = User.objects.filter(username=username) +# if user: +# error = u'用户 %s 已存在' % username +# raise ServerError +# +# except ServerError: +# pass +# else: +# try: +# user = db_add_user(username=username, +# password=CRYPTOR.md5_crypt(password), +# name=name, email=email, dept=dept, +# groups=groups, role='CU', +# ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd), +# ldap_pwd=CRYPTOR.encrypt(ldap_pwd), +# is_active=is_active, +# date_joined=datetime.datetime.now()) +# +# server_add_user(username, password, ssh_key_pwd) +# if LDAP_ENABLE: +# ldap_add_user(username, ldap_pwd) +# +# except Exception, e: +# error = u'添加用户 %s 失败 %s ' % (username, e) +# try: +# db_del_user(username) +# server_del_user(username) +# if LDAP_ENABLE: +# ldap_del_user(username) +# except Exception: +# pass +# else: +# mail_title = u'恭喜你的跳板机用户添加成功 Jumpserver' +# mail_msg = """ +# Hi, %s +# 您的用户名: %s +# 您的部门: %s +# 您的角色: %s +# 您的web登录密码: %s +# 您的ssh密钥文件密码: %s +# 密钥下载地址: http://%s:%s/juser/down_key/?id=%s +# 说明: 请登陆后再下载密钥! +# """ % (name, username, dept.name, '普通用户', +# password, ssh_key_pwd, SEND_IP, SEND_PORT, user.id) +# send_mail(mail_title, mail_msg, MAIL_FROM, [email], fail_silently=False) +# msg = u'添加用户 %s 成功! 用户密码已发送到 %s 邮箱!' % (username, email) +# +# return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) @require_role(role='super') diff --git a/templates/juser/user_add.html b/templates/juser/user_add.html index c26c1fa5e..62863fc2c 100644 --- a/templates/juser/user_add.html +++ b/templates/juser/user_add.html @@ -93,13 +93,20 @@
-
-
-
- +
+
+
+
-
- +
+
+
+ +
+
+
+
+
From c21cdc2131bb9d919d5ab0d43db98eeb40e3ca40 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 31 Aug 2015 23:04:53 +0800 Subject: [PATCH 018/385] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BF=98=E8=AE=B0?= =?UTF-8?q?=E5=AF=86=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/views.py | 126 +++++++++--------- jumpserver.conf | 3 +- jumpserver/api.py | 174 ++++++++++++------------ jumpserver/views.py | 2 +- juser/models.py | 6 +- juser/urls.py | 5 +- juser/user_api.py | 123 ++++++++++------- juser/views.py | 192 +++++++++++++++++---------- static/css/style.css | 4 +- templates/juser/forget_password.html | 53 ++++++++ templates/juser/reset_password.html | 50 +++++++ templates/juser/user_add.html | 4 +- templates/juser/user_edit.html | 23 ---- templates/juser/user_list.html | 29 ++-- templates/login.html | 2 +- 15 files changed, 475 insertions(+), 321 deletions(-) create mode 100644 templates/juser/forget_password.html create mode 100644 templates/juser/reset_password.html diff --git a/jperm/views.py b/jperm/views.py index c5bfdfb03..0fd0d4720 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -262,70 +262,70 @@ def unicode2str(unicode_list): return [str(i) for i in unicode_list] -def sudo_ldap_add(user_group, user_runas, asset_groups_select, - cmd_groups_select): - if not LDAP_ENABLE: - return True - - assets = [] - cmds = [] - user_runas = user_runas.split(',') - if len(asset_groups_select) == 1 and asset_groups_select[0].name == 'ALL': - asset_all = True - else: - asset_all = False - for asset_group in asset_groups_select: - assets.extend(asset_group.asset_set.all()) +# def sudo_ldap_add(user_group, user_runas, asset_groups_select, +# cmd_groups_select): +# if not LDAP_ENABLE: +# return True +# +# assets = [] +# cmds = [] +# user_runas = user_runas.split(',') +# if len(asset_groups_select) == 1 and asset_groups_select[0].name == 'ALL': +# asset_all = True +# else: +# asset_all = False +# for asset_group in asset_groups_select: +# assets.extend(asset_group.asset_set.all()) +# +# if user_group.name == 'ALL': +# user_all = True +# users = [] +# else: +# user_all = False +# users = user_group.user_set.all() +# +# for cmd_group in cmd_groups_select: +# cmds.extend(cmd_group.cmd.split(',')) +# +# if user_all: +# users_name = ['ALL'] +# else: +# users_name = list(set([user.username for user in users])) +# +# if asset_all: +# assets_ip = ['ALL'] +# else: +# assets_ip = list(set([asset.ip for asset in assets])) +# +# name = 'sudo%s' % user_group.id +# sudo_dn = 'cn=%s,ou=Sudoers,%s' % (name, LDAP_BASE_DN) +# sudo_attr = {'objectClass': ['top', 'sudoRole'], +# 'cn': ['%s' % name], +# 'sudoCommand': unicode2str(cmds), +# 'sudoHost': unicode2str(assets_ip), +# 'sudoOption': ['!authenticate'], +# 'sudoRunAsUser': unicode2str(user_runas), +# 'sudoUser': unicode2str(users_name)} +# ldap_conn.delete(sudo_dn) +# ldap_conn.add(sudo_dn, sudo_attr) - if user_group.name == 'ALL': - user_all = True - users = [] - else: - user_all = False - users = user_group.user_set.all() - - for cmd_group in cmd_groups_select: - cmds.extend(cmd_group.cmd.split(',')) - - if user_all: - users_name = ['ALL'] - else: - users_name = list(set([user.username for user in users])) - - if asset_all: - assets_ip = ['ALL'] - else: - assets_ip = list(set([asset.ip for asset in assets])) - - name = 'sudo%s' % user_group.id - sudo_dn = 'cn=%s,ou=Sudoers,%s' % (name, LDAP_BASE_DN) - sudo_attr = {'objectClass': ['top', 'sudoRole'], - 'cn': ['%s' % name], - 'sudoCommand': unicode2str(cmds), - 'sudoHost': unicode2str(assets_ip), - 'sudoOption': ['!authenticate'], - 'sudoRunAsUser': unicode2str(user_runas), - 'sudoUser': unicode2str(users_name)} - ldap_conn.delete(sudo_dn) - ldap_conn.add(sudo_dn, sudo_attr) - - -def sudo_update(user_group, user_runas, asset_groups_select, cmd_groups_select, comment): - asset_groups_select_list, cmd_groups_select_list = \ - asset_cmd_groups_get(asset_groups_select, cmd_groups_select) - sudo_perm = user_group.sudoperm_set.all() - if sudo_perm: - sudo_perm.update(user_runas=user_runas, comment=comment) - sudo_perm = sudo_perm[0] - sudo_perm.asset_group = asset_groups_select_list - sudo_perm.cmd_group = cmd_groups_select_list - else: - sudo_perm = SudoPerm(user_group=user_group, user_runas=user_runas, comment=comment) - sudo_perm.save() - sudo_perm.asset_group = asset_groups_select_list - sudo_perm.cmd_group = cmd_groups_select_list - - sudo_ldap_add(user_group, user_runas, asset_groups_select_list, cmd_groups_select_list) +# +# def sudo_update(user_group, user_runas, asset_groups_select, cmd_groups_select, comment): +# asset_groups_select_list, cmd_groups_select_list = \ +# asset_cmd_groups_get(asset_groups_select, cmd_groups_select) +# sudo_perm = user_group.sudoperm_set.all() +# if sudo_perm: +# sudo_perm.update(user_runas=user_runas, comment=comment) +# sudo_perm = sudo_perm[0] +# sudo_perm.asset_group = asset_groups_select_list +# sudo_perm.cmd_group = cmd_groups_select_list +# else: +# sudo_perm = SudoPerm(user_group=user_group, user_runas=user_runas, comment=comment) +# sudo_perm.save() +# sudo_perm.asset_group = asset_groups_select_list +# sudo_perm.cmd_group = cmd_groups_select_list +# +# sudo_ldap_add(user_group, user_runas, asset_groups_select_list, cmd_groups_select_list) @require_super_user diff --git a/jumpserver.conf b/jumpserver.conf index dfd5a787c..94ee1653f 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -1,8 +1,7 @@ #coding: utf8 [base] -ip = 192.168.20.209 -port = 80 +url = http://192.168.244.129 key = 88aaaf7ffe3c6c04 log = debug diff --git a/jumpserver/api.py b/jumpserver/api.py index 23988b781..c920137f1 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -6,15 +6,12 @@ import getpass from Crypto.Cipher import AES import crypt from binascii import b2a_hex, a2b_hex -import ldap -from ldap import modlist import hashlib import datetime import random import subprocess import paramiko import struct, fcntl, signal,socket, select, fnmatch -from functools import partial from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.http import HttpResponse, Http404 @@ -23,7 +20,7 @@ from juser.models import User, UserGroup from jasset.models import Asset, BisGroup, IDC from jlog.models import Log from jasset.models import AssetAlias -from django.core.exceptions import ObjectDoesNotExist +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 @@ -48,9 +45,8 @@ SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys') # SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') KEY = CONF.get('base', 'key') LOGIN_NAME = getpass.getuser() -LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') -SEND_IP = CONF.get('base', 'ip') -SEND_PORT = CONF.get('base', 'port') +# LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') +URL = CONF.get('base', 'url') MAIL_ENABLE = CONF.get('mail', 'mail_enable') MAIL_FROM = CONF.get('mail', 'email_host_user') log_dir = os.path.join(BASE_DIR, 'logs') @@ -73,73 +69,73 @@ def set_log(level): return logger_f -class LDAPMgmt(): - """ - LDAP class for add, select, del, update - LDAP 管理类,增删改查 - """ - def __init__(self, - host_url, - base_dn, - root_cn, - root_pw): - self.ldap_host = host_url - self.ldap_base_dn = base_dn - self.conn = ldap.initialize(host_url) - self.conn.set_option(ldap.OPT_REFERRALS, 0) - self.conn.protocol_version = ldap.VERSION3 - self.conn.simple_bind_s(root_cn, root_pw) - - def list(self, filter, scope=ldap.SCOPE_SUBTREE, attr=None): - """ - query - 查询 - """ - result = {} - try: - ldap_result = self.conn.search_s(self.ldap_base_dn, scope, filter, attr) - for entry in ldap_result: - name, data = entry - for k, v in data.items(): - print '%s: %s' % (k, v) - result[k] = v - return result - except ldap.LDAPError, e: - print e - - def add(self, dn, attrs): - """ - add - 添加 - """ - try: - ldif = modlist.addModlist(attrs) - self.conn.add_s(dn, ldif) - except ldap.LDAPError, e: - print e - - def modify(self, dn, attrs): - """ - modify - 更改 - """ - try: - attr_s = [] - for k, v in attrs.items(): - attr_s.append((2, k, v)) - self.conn.modify_s(dn, attr_s) - except ldap.LDAPError, e: - print e - - def delete(self, dn): - """ - delete - 删除 - """ - try: - self.conn.delete_s(dn) - except ldap.LDAPError, e: - print e +# class LDAPMgmt(): +# """ +# LDAP class for add, select, del, update +# LDAP 管理类,增删改查 +# """ +# def __init__(self, +# host_url, +# base_dn, +# root_cn, +# root_pw): +# self.ldap_host = host_url +# self.ldap_base_dn = base_dn +# self.conn = ldap.initialize(host_url) +# self.conn.set_option(ldap.OPT_REFERRALS, 0) +# self.conn.protocol_version = ldap.VERSION3 +# self.conn.simple_bind_s(root_cn, root_pw) +# +# def list(self, filter, scope=ldap.SCOPE_SUBTREE, attr=None): +# """ +# query +# 查询 +# """ +# result = {} +# try: +# ldap_result = self.conn.search_s(self.ldap_base_dn, scope, filter, attr) +# for entry in ldap_result: +# name, data = entry +# for k, v in data.items(): +# print '%s: %s' % (k, v) +# result[k] = v +# return result +# except ldap.LDAPError, e: +# print e +# +# def add(self, dn, attrs): +# """ +# add +# 添加 +# """ +# try: +# ldif = modlist.addModlist(attrs) +# self.conn.add_s(dn, ldif) +# except ldap.LDAPError, e: +# print e +# +# def modify(self, dn, attrs): +# """ +# modify +# 更改 +# """ +# try: +# attr_s = [] +# for k, v in attrs.items(): +# attr_s.append((2, k, v)) +# self.conn.modify_s(dn, attr_s) +# except ldap.LDAPError, e: +# print e +# +# def delete(self, dn): +# """ +# delete +# 删除 +# """ +# try: +# self.conn.delete_s(dn) +# except ldap.LDAPError, e: +# print e def page_list_return(total, current=1): @@ -480,9 +476,10 @@ def get_object(model, **kwargs): use this function for query 使用改封装函数查询数据库 """ - try: - the_object = model.objects.get(**kwargs) - except ObjectDoesNotExist: + the_object = model.objects.filter(**kwargs) + if len(the_object) == 1: + the_object = the_object[0] + else: the_object = None return the_object @@ -498,10 +495,10 @@ def require_role(role='user'): if not request.session.get('user_id'): return HttpResponseRedirect('/login/') elif role == 'admin': - if request.session.get('role_id', 0) != 1: + if request.session.get('role_id', 0) < 1: return HttpResponseRedirect('/') elif role == 'super': - if request.session.get('role_id', 0) != 2: + if request.session.get('role_id', 0) < 2: return HttpResponseRedirect('/') return func(request, *args, **kwargs) return __deco @@ -531,8 +528,7 @@ def get_session_user_dept(request): user = User.objects.filter(id=user_id) if user: user = user[0] - dept = user.dept - return user, dept + return user, None @require_role @@ -704,14 +700,14 @@ def http_error(request, emg): CRYPTOR = PyCrypt(KEY) -if LDAP_ENABLE: - LDAP_HOST_URL = CONF.get('ldap', 'host_url') - LDAP_BASE_DN = CONF.get('ldap', 'base_dn') - LDAP_ROOT_DN = CONF.get('ldap', 'root_dn') - LDAP_ROOT_PW = CONF.get('ldap', 'root_pw') - ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) -else: - ldap_conn = None +# if LDAP_ENABLE: +# LDAP_HOST_URL = CONF.get('ldap', 'host_url') +# LDAP_BASE_DN = CONF.get('ldap', 'base_dn') +# LDAP_ROOT_DN = CONF.get('ldap', 'root_dn') +# LDAP_ROOT_PW = CONF.get('ldap', 'root_pw') +# ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) +# else: +# ldap_conn = None log_level = CONF.get('base', 'log') logger = set_log(log_level) \ No newline at end of file diff --git a/jumpserver/views.py b/jumpserver/views.py index e4706eab3..e9d2819a2 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -213,7 +213,7 @@ def login(request): user_filter.update(last_login=datetime.datetime.now()) if user.role == 'SU': request.session['role_id'] = 2 - elif user.role == 'DA': + elif user.role == 'GA': request.session['role_id'] = 1 else: request.session['role_id'] = 0 diff --git a/juser/models.py b/juser/models.py index a7bd79943..cb7a4b248 100644 --- a/juser/models.py +++ b/juser/models.py @@ -30,8 +30,8 @@ class User(models.Model): name = models.CharField(max_length=80) email = models.EmailField(max_length=75) role = models.CharField(max_length=2, choices=USER_ROLE_CHOICES, default='CU') + uuid = models.CharField(max_length=100) group = models.ManyToManyField(UserGroup) - ldap_pwd = models.CharField(max_length=128) ssh_key_pwd = models.CharField(max_length=200) is_active = models.BooleanField(default=True) last_login = models.DateTimeField(null=True) @@ -128,4 +128,6 @@ class AdminGroup(models.Model): """ user = models.ForeignKey(User) - group = models.ForeignKey(UserGroup) \ No newline at end of file + group = models.ForeignKey(UserGroup) + + diff --git a/juser/urls.py b/juser/urls.py index c359a4f4f..4f3e5daa5 100644 --- a/juser/urls.py +++ b/juser/urls.py @@ -13,7 +13,10 @@ urlpatterns = patterns('juser.views', (r'^group_del_ajax', group_del_ajax), (r'^group_edit/$', group_edit), (r'^user_add/$', user_add), - (r'^user_list/$', view_splitter, {'su': user_list, 'adm': user_list_adm}), + (r'^user_list/$', user_list), + (r'^send_mail_retry/$', send_mail_retry), + (r'^reset_password/$', reset_password), + (r'^forget_password/$', forget_password), (r'^user_detail/$', 'user_detail'), (r'^user_del/$', 'user_del'), (r'^user_del_ajax/$', 'user_del_ajax'), diff --git a/juser/user_api.py b/juser/user_api.py index b837a6b6f..0597e8f05 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -101,11 +101,9 @@ def db_del_user(username): delete a user from database 从数据库中删除用户 """ - try: - user = User.objects.get(username=username) + user = get_object(User, username=username) + if user: user.delete() - except ObjectDoesNotExist: - pass def gen_ssh_key(username, password=None, length=2048): @@ -134,13 +132,14 @@ def gen_ssh_key(username, password=None, length=2048): print "gen_ssh_key_end" + str(time.time()) -def server_add_user(username, password, ssh_key_pwd): +def server_add_user(username, password, ssh_key_pwd, ssh_key_login_need): """ add a system user in jumpserver 在jumpserver服务器上添加一个用户 """ - bash("useradd '%s'; echo '%s' | passwd --stdin '%s'" % (username, password, username)) - gen_ssh_key(username, ssh_key_pwd) + bash("useradd '%s'; echo '%s'; echo '%s' | passwd --stdin '%s'" % (username, password, password, username)) + if ssh_key_login_need: + gen_ssh_key(username, ssh_key_pwd) def user_add_mail(user, kwargs): @@ -156,10 +155,10 @@ def user_add_mail(user, kwargs): 您的角色: %s 您的web登录密码: %s 您的ssh密钥文件密码: %s - 密钥下载地址: http://%s:%s/juser/down_key/?id=%s + 密钥下载地址: %s/juser/down_key/?uuid=%s 说明: 请登陆后再下载密钥! """ % (user.name, user.username, user_role.get(user.role, u'普通用户'), - kwargs.get('password'), kwargs.get('ssh_key_pwd'), SEND_IP, SEND_PORT, user.id) + kwargs.get('password'), kwargs.get('ssh_key_pwd'), URL, user.uuid) send_mail(mail_title, mail_msg, MAIL_FROM, [user.email], fail_silently=False) @@ -171,49 +170,73 @@ def server_del_user(username): bash('userdel -r %s' % username) -def ldap_add_user(username, ldap_pwd): - """ - add a user in ldap database - 在LDAP中添加用户 - """ - user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) - password_sha512 = PyCrypt.gen_sha512(PyCrypt.random_pass(6), ldap_pwd) - user = get_object(User, username=username) - if not user: - raise ServerError(u'用户 %s 不存在' % username) +def get_display_msg(user, password, ssh_key_pwd, ssh_key_login_need, send_mail_need): + if send_mail_need: + msg = u'添加用户 %s 成功! 用户密码已发送到 %s 邮箱!' % (user.name, user.email) + return msg - user_attr = {'uid': [str(username)], - 'cn': [str(username)], - 'objectClass': ['account', 'posixAccount', 'top', 'shadowAccount'], - 'userPassword': ['{crypt}%s' % password_sha512], - 'shadowLastChange': ['16328'], - 'shadowMin': ['0'], - 'shadowMax': ['99999'], - 'shadowWarning': ['7'], - 'loginShell': ['/bin/bash'], - 'uidNumber': [str(user.id)], - 'gidNumber': [str(user.id)], - 'homeDirectory': [str('/home/%s' % username)]} + if ssh_key_login_need: + msg = u""" + 跳板机地址: %s + 用户名:%s + 密码:%s + 密钥密码:%s + 密钥下载url: %s/juser/down_key/?id=%s + 该账号密码可以登陆web和跳板机。 + """ % (URL, user.username, password, ssh_key_pwd, URL, user.id) + else: + msg = u""" + 跳板机地址: %s \n + 用户名:%s \n + 密码:%s \n + 该账号密码可以登陆web和跳板机。 + """ % (URL, user.username, password) - group_dn = "cn=%s,ou=Group,%s" % (username, LDAP_BASE_DN) - group_attr = {'objectClass': ['posixGroup', 'top'], - 'cn': [str(username)], - 'userPassword': ['{crypt}x'], - 'gidNumber': [str(user.id)]} + return msg - ldap_conn.add(user_dn, user_attr) - ldap_conn.add(group_dn, group_attr) +# def ldap_add_user(username, ldap_pwd): +# """ +# add a user in ldap database +# 在LDAP中添加用户 +# """ +# user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) +# password_sha512 = PyCrypt.gen_sha512(PyCrypt.random_pass(6), ldap_pwd) +# user = get_object(User, username=username) +# if not user: +# raise ServerError(u'用户 %s 不存在' % username) +# +# user_attr = {'uid': [str(username)], +# 'cn': [str(username)], +# 'objectClass': ['account', 'posixAccount', 'top', 'shadowAccount'], +# 'userPassword': ['{crypt}%s' % password_sha512], +# 'shadowLastChange': ['16328'], +# 'shadowMin': ['0'], +# 'shadowMax': ['99999'], +# 'shadowWarning': ['7'], +# 'loginShell': ['/bin/bash'], +# 'uidNumber': [str(user.id)], +# 'gidNumber': [str(user.id)], +# 'homeDirectory': [str('/home/%s' % username)]} +# +# group_dn = "cn=%s,ou=Group,%s" % (username, LDAP_BASE_DN) +# group_attr = {'objectClass': ['posixGroup', 'top'], +# 'cn': [str(username)], +# 'userPassword': ['{crypt}x'], +# 'gidNumber': [str(user.id)]} +# +# ldap_conn.add(user_dn, user_attr) +# ldap_conn.add(group_dn, group_attr) -def ldap_del_user(username): - """ - delete a user in ldap database - 在ldap中删除某用户 - """ - user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) - group_dn = "cn=%s,ou=Group,%s" % (username, LDAP_BASE_DN) - sudo_dn = 'cn=%s,ou=Sudoers,%s' % (username, LDAP_BASE_DN) - - ldap_conn.delete(user_dn) - ldap_conn.delete(group_dn) - ldap_conn.delete(sudo_dn) \ No newline at end of file +# def ldap_del_user(username): +# """ +# delete a user in ldap database +# 在ldap中删除某用户 +# """ +# user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) +# group_dn = "cn=%s,ou=Group,%s" % (username, LDAP_BASE_DN) +# sudo_dn = 'cn=%s,ou=Sudoers,%s' % (username, LDAP_BASE_DN) +# +# ldap_conn.delete(user_dn) +# ldap_conn.delete(group_dn) +# ldap_conn.delete(sudo_dn) \ No newline at end of file diff --git a/juser/views.py b/juser/views.py index 4e13104d1..b9c225c17 100644 --- a/juser/views.py +++ b/juser/views.py @@ -4,6 +4,7 @@ import random from Crypto.PublicKey import RSA +import uuid as uuid_r from django.db.models import Q from django.template import RequestContext @@ -235,13 +236,15 @@ def user_add(request): groups = request.POST.getlist('groups', []) admin_groups = request.POST.getlist('admin_groups', []) role = request.POST.get('role', 'CU') + uuid = uuid_r.uuid1() ssh_key_pwd = PyCrypt.random_pass(16) extra = request.POST.getlist('extra', []) is_active = True if '0' in extra else False - ldap_pwd = PyCrypt.random_pass(32, especial=True) + ssh_key_login_need = True if '1' in extra else False + send_mail_need = True if '2' in extra else False try: - if '' in [username, password, ssh_key_pwd, name, groups, role, is_active]: + if '' in [username, password, ssh_key_pwd, name, role]: error = u'带*内容不能为空' raise ServerError user_test = get_object(User, username=username) @@ -253,30 +256,25 @@ def user_add(request): pass else: try: - user = db_add_user(username=username, + user = db_add_user(username=username, name=name, password=CRYPTOR.md5_crypt(password), - name=name, email=email, role=role, + email=email, role=role, uuid=uuid, groups=groups, admin_groups=admin_groups, ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd), - ldap_pwd=CRYPTOR.encrypt(ldap_pwd), is_active=is_active, date_joined=datetime.datetime.now()) - if LDAP_ENABLE: - ldap_add_user(username, ldap_pwd) - - except IndexError, e: + server_add_user(username, password, ssh_key_pwd, ssh_key_login_need) + except Exception, e: error = u'添加用户 %s 失败 %s ' % (username, e) try: db_del_user(username) server_del_user(username) - if LDAP_ENABLE: - ldap_del_user(username) except Exception: pass else: - if MAIL_ENABLE: + if MAIL_ENABLE and send_mail_need: user_add_mail(user, kwargs=locals()) - msg = u'添加用户 %s 成功! 用户密码已发送到 %s 邮箱!' % (username, email) + msg = get_display_msg(user, password, ssh_key_pwd, ssh_key_login_need, send_mail_need) return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) @@ -358,7 +356,6 @@ def user_list(request): header_title, path1, path2 = '查看用户', '用户管理', '用户列表' keyword = request.GET.get('keyword', '') gid = request.GET.get('gid', '') - did = request.GET.get('did', '') contact_list = User.objects.all().order_by('name') if gid: @@ -367,12 +364,6 @@ def user_list(request): user_group = user_group[0] contact_list = user_group.user_set.all() - if did: - dept = DEPT.objects.filter(id=did) - if dept: - dept = dept[0] - contact_list = dept.user_set.all().order_by('name') - if keyword: contact_list = contact_list.filter(Q(username__icontains=keyword) | Q(name__icontains=keyword)).order_by('name') @@ -381,49 +372,48 @@ def user_list(request): return render_to_response('juser/user_list.html', locals(), context_instance=RequestContext(request)) -@require_role(role='admin') -def user_list_adm(request): - user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} - header_title, path1, path2 = '查看用户', '用户管理', '用户列表' - keyword = request.GET.get('keyword', '') - user, dept = get_session_user_dept(request) - gid = request.GET.get('gid', '') - contact_list = dept.user_set.all().order_by('name') - - if gid: - if not validate(request, user_group=[gid]): - return HttpResponseRedirect('/juser/user_list/') - user_group = UserGroup.objects.filter(id=gid) - if user_group: - user_group = user_group[0] - contact_list = user_group.user_set.all() - - if keyword: - contact_list = contact_list.filter(Q(username__icontains=keyword) | Q(name__icontains=keyword)).order_by('name') - - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(contact_list, request) - - return render_to_response('juser/user_list.html', locals(), context_instance=RequestContext(request)) +# @require_role(role='admin') +# def user_list_adm(request): +# user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} +# header_title, path1, path2 = '查看用户', '用户管理', '用户列表' +# keyword = request.GET.get('keyword', '') +# user, dept = get_session_user_dept(request) +# gid = request.GET.get('gid', '') +# contact_list = dept.user_set.all().order_by('name') +# +# if gid: +# if not validate(request, user_group=[gid]): +# return HttpResponseRedirect('/juser/user_list/') +# user_group = UserGroup.objects.filter(id=gid) +# if user_group: +# user_group = user_group[0] +# contact_list = user_group.user_set.all() +# +# if keyword: +# contact_list = contact_list.filter(Q(username__icontains=keyword) | Q(name__icontains=keyword)).order_by('name') +# +# contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(contact_list, request) +# +# return render_to_response('juser/user_list.html', locals(), context_instance=RequestContext(request)) @require_role(role='user') def user_detail(request): - header_title, path1, path2 = '查看用户', '用户管理', '用户详情' + header_title, path1, path2 = '用户详情', '用户管理', '用户详情' if request.session.get('role_id') == 0: user_id = request.session.get('user_id') else: user_id = request.GET.get('id', '') - if request.session.get('role_id') == 1: - user, dept = get_session_user_dept(request) - if not validate(request, user=[user_id]): - return HttpResponseRedirect('/') - if not user_id: - return HttpResponseRedirect('/juser/user_list/') + # if request.session.get('role_id') == 1: + # user, dept = get_session_user_dept(request) + # if not validate(request, user=[user_id]): + # return HttpResponseRedirect('/') + # if not user_id: + # return HttpResponseRedirect('/juser/user_list/') - user = User.objects.filter(id=user_id) + user = get_object(User, id=user_id) if user: - user = user[0] - asset_group_permed = user.get_asset_group() + # asset_group_permed = user.get_asset_group() logs_last = Log.objects.filter(user=user.name).order_by('-start_time')[0:10] logs_all = Log.objects.filter(user=user.name).order_by('-start_time') logs_num = len(logs_all) @@ -437,17 +427,10 @@ def user_del(request): if not user_id: return HttpResponseRedirect('/juser/user_list/') - if request.session.get('role_id', '') == '1': - if not validate(request, user=[user_id]): - return HttpResponseRedirect('/juser/user_list/') - - user = User.objects.filter(id=user_id) - if user and user[0].username != 'admin': - user = user[0] + user = get_object(User, id=user_id) + if user and user.username != 'admin': user.delete() server_del_user(user.username) - if LDAP_ENABLE: - ldap_del_user(user.username) return HttpResponseRedirect('/juser/user_list/') @@ -458,32 +441,97 @@ def user_del_ajax(request): if request.session.get('role_id', '') == 1: if not validate(request, user=user_ids): return "error" + for user_id in user_ids: - user = User.objects.filter(id=user_id) - if user and user[0].username != 'admin': - user = user[0] + user = get_object(User, id=user_id) + if user and user.username != 'admin': user.delete() server_del_user(user.username) - if LDAP_ENABLE: - ldap_del_user(user.username) return HttpResponse('删除成功') +@require_role('admin') +def send_mail_retry(request): + user_uuid = request.GET.get('uuid', '1') + user = get_object(User, uuid=user_uuid) + msg = u""" + 跳板机地址: %s + 用户名:%s + 重设密码:%s/juser/forget_password/ + 请登录web重新生成key + """ % (URL, user.username, URL) + + try: + send_mail(u'邮件重发', msg, MAIL_FROM, [user.email], fail_silently=False) + except IndexError: + return Http404 + return HttpResponse('发送成功') + + +def forget_password(request): + if request.method == 'POST': + email = request.POST.get('email', '') + username = request.POST.get('username', '') + user = get_object(User, username=username, email=email) + if user: + timestamp = int(time.time()) + hash_encode = PyCrypt.md5_crypt(str(user.uuid) + str(timestamp) + KEY) + msg = u""" + Hi %s, 请点击下面链接重设密码! + %s/juser/reset_password/?uuid=%s×tamp=%s&hash=%s + """ % (user.name, URL, user.uuid, timestamp, hash_encode) + send_mail('忘记跳板机密码', msg, MAIL_FROM, [email], fail_silently=False) + msg = u'请登陆邮箱,点击邮件重设密码' + return HttpResponse(msg) + else: + error = u'用户不存在或邮件地址错误' + + return render_to_response('juser/forget_password.html', locals()) + + +def reset_password(request): + uuid = request.GET.get('uuid', '') + timestamp = request.GET.get('timestamp', '') + hash_encode = request.GET.get('hash', '') + action = '/juser/reset_password/?uuid=%s×tamp=%s&hash=%s' % (uuid, timestamp, hash_encode) + + if request.method == 'POST': + password = request.POST.get('password') + password_confirm = request.POST.get('password_confirm') + if password != password_confirm: + return HttpResponse('密码不匹配') + else: + user = get_object(User, uuid=uuid) + if user: + user.password = PyCrypt.md5_crypt(password) + user.save() + return HttpResponse('密码重设成功') + else: + return HttpResponse('用户不存在') + + if hash_encode == PyCrypt.md5_crypt(uuid + timestamp + KEY): + if int(time.time()) - int(timestamp) > 600: + return HttpResponse('链接已超时') + else: + return render_to_response('juser/reset_password.html', locals()) + + return http_error(request, u'错误请求') + + @require_role(role='super') def user_edit(request): - header_title, path1, path2 = '编辑用户', '用户管理', '用户编辑' + header_title, path1, path2 = '编辑用户', '用户管理', '编辑用户' if request.method == 'GET': user_id = request.GET.get('id', '') if not user_id: return HttpResponseRedirect('/') - user_role = {'SU': u'超级管理员', 'DA': u'部门管理员', 'CU': u'普通用户'} - user = User.objects.filter(id=user_id) - dept_all = DEPT.objects.all() + user_role = {'SU': u'超级管理员', 'DA': u'组管理员', 'CU': u'普通用户'} + user = get_object(User, id=user_id) group_all = UserGroup.objects.all() + if user: - user = user[0] groups_str = ' '.join([str(group.id) for group in user.group.all()]) else: diff --git a/static/css/style.css b/static/css/style.css index 5b0e5745d..ee9d0978a 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -3516,8 +3516,8 @@ body.modal-open { z-index: 100; } .lockscreen.middle-box { - width: 200px; - margin-left: -100px; + width: 400px; + margin-left: -200px; margin-top: -190px; } .loginscreen.middle-box { diff --git a/templates/juser/forget_password.html b/templates/juser/forget_password.html new file mode 100644 index 000000000..78606a98d --- /dev/null +++ b/templates/juser/forget_password.html @@ -0,0 +1,53 @@ + + + + + + 忘记密码 + + + + + + + + + + + +
+ Jumperver +
+
+
+
+{# image#} + {% if error %} +
{{ error }}
+ {% endif %} + {% if msg %} +
{{ msg }}
+ {% endif %} +
+

忘记密码

+

请输入您原来的信息

+
+
+ +
+
+ +
+ +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/templates/juser/reset_password.html b/templates/juser/reset_password.html new file mode 100644 index 000000000..4ce31d433 --- /dev/null +++ b/templates/juser/reset_password.html @@ -0,0 +1,50 @@ + + + + + + 重置{{ name }} + + + + + + + + + + + +
+ JumpServer +
+
+
+
+{# image#} + {% if error %} +
{{ error }}
+ {% endif %} + {% if msg %} +
{{ msg }}
+ {% endif %} +
+

请输入新密码

+
+
+ + +
+ +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/templates/juser/user_add.html b/templates/juser/user_add.html index 62863fc2c..43cb39478 100644 --- a/templates/juser/user_add.html +++ b/templates/juser/user_add.html @@ -96,7 +96,7 @@
- +
@@ -106,7 +106,7 @@
- +
diff --git a/templates/juser/user_edit.html b/templates/juser/user_edit.html index c8ad656a3..dffc941fd 100644 --- a/templates/juser/user_edit.html +++ b/templates/juser/user_edit.html @@ -17,12 +17,6 @@ - @@ -71,23 +65,6 @@
- {% ifequal session_role_id 2 %} -
- -
- -
-
-
- {% endifequal %}
diff --git a/templates/juser/user_list.html b/templates/juser/user_list.html index f9ef7c017..0f9c2827c 100644 --- a/templates/juser/user_list.html +++ b/templates/juser/user_list.html @@ -37,7 +37,7 @@
@@ -52,7 +52,6 @@ 用户名 姓名 - 部门 小组 角色 激活 @@ -68,20 +67,15 @@ {{ user.username }} {{ user.name }} - {{ user.dept.name }} {{ user.group.all | group_str2 }} {{ user.id | get_role }} {{ user.is_active|bool2str }} 下载 详情 - {% ifequal session_role_id 2 %} 编辑 + 删除 - {% else %} - 编辑 - 删除 - {% endifequal %} {% endfor %} @@ -101,6 +95,8 @@
+{% endblock %} +{% block self_head_css_js %} - -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/templates/login.html b/templates/login.html index 00b070ddd..2e00c003d 100644 --- a/templates/login.html +++ b/templates/login.html @@ -31,7 +31,7 @@
- Forgot password? Contact Administrator. + Forgot password?

Copyright Jumpserver.org Organization © 2014-2015

From 3efd810fe16d0a2499ef45c9f99c70bc28c4aeb5 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 2 Sep 2015 21:42:31 +0800 Subject: [PATCH 019/385] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E7=94=A8=E6=88=B7bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jumpserver/templatetags/mytags.py | 5 +- juser/models.py | 3 + juser/user_api.py | 15 +++- juser/views.py | 52 ++++++++------ templates/footer.html | 2 +- templates/juser/user_edit.html | 116 +++++++++++++++--------------- 6 files changed, 103 insertions(+), 90 deletions(-) diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index f5ed765e2..b8b236dfe 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -27,10 +27,9 @@ def int2str(value): @register.filter(name='get_role') def get_role(user_id): - user_role = {'SU': u'超级管理员', 'DA': u'部门管理员', 'CU': u'普通用户'} - user = User.objects.filter(id=user_id) + user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} + user = get_object(User, id=user_id) if user: - user = user[0] return user_role.get(str(user.role), u"普通用户") else: return u"普通用户" diff --git a/juser/models.py b/juser/models.py index cb7a4b248..d2a0d9f8d 100644 --- a/juser/models.py +++ b/juser/models.py @@ -130,4 +130,7 @@ class AdminGroup(models.Model): user = models.ForeignKey(User) group = models.ForeignKey(UserGroup) + def __unicode__(self): + return '%s: %s' % (self.user.username, self.group.name) + diff --git a/juser/user_api.py b/juser/user_api.py index 0597e8f05..04a638a0f 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -81,19 +81,28 @@ def db_update_user(**kwargs): 数据库更新用户信息 """ groups_post = kwargs.pop('groups') + admin_groups_post = kwargs.pop('admin_groups') user_id = kwargs.pop('user_id') user = User.objects.filter(id=user_id) if user: user.update(**kwargs) - user = User.objects.get(id=user_id) + user = user[0] user.save() + else: + return None + group_select = [] if groups_post: - group_select = [] for group_id in groups_post: group = UserGroup.objects.filter(id=group_id) group_select.extend(group) - user.group = group_select + user.group = group_select + + if admin_groups_post != '': + user.admingroup_set.all().delete() + for group_id in admin_groups_post: + group = get_object(UserGroup, id=group_id) + AdminGroup(user=user, group=group).save() def db_del_user(username): diff --git a/juser/views.py b/juser/views.py index b9c225c17..c77f90cd2 100644 --- a/juser/views.py +++ b/juser/views.py @@ -527,54 +527,60 @@ def user_edit(request): if not user_id: return HttpResponseRedirect('/') - user_role = {'SU': u'超级管理员', 'DA': u'组管理员', 'CU': u'普通用户'} + user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} user = get_object(User, id=user_id) group_all = UserGroup.objects.all() - if user: groups_str = ' '.join([str(group.id) for group in user.group.all()]) + admin_groups_str = ' '.join([str(admin_group.group.id) for admin_group in user.admingroup_set.all()]) else: - user_id = request.POST.get('user_id', '') + user_id = request.GET.get('id', '') password = request.POST.get('password', '') name = request.POST.get('name', '') email = request.POST.get('email', '') - dept_id = request.POST.get('dept_id') groups = request.POST.getlist('groups', []) role_post = request.POST.get('role', 'CU') - ssh_key_pwd = request.POST.get('ssh_key_pwd', '') - is_active = True if request.POST.get('is_active', '1') == '1' else False - + admin_groups = request.POST.getlist('admin_groups', []) + extra = request.POST.getlist('extra', []) + is_active = True if '0' in extra else False + email_need = True if '2' in extra else False user_role = {'SU': u'超级管理员', 'DA': u'部门管理员', 'CU': u'普通用户'} - dept = DEPT.objects.filter(id=dept_id) - if dept: - dept = dept[0] - else: - dept = DEPT.objects.get(id='2') - + print '#'*10 + str(email_need) + print extra if user_id: - user = User.objects.filter(id=user_id) - if user: - user = user[0] + user = get_object(User, id=user_id) else: return HttpResponseRedirect('/juser/user_list/') if password != user.password: + password_decode = password password = CRYPTOR.md5_crypt(password) - - if ssh_key_pwd != user.ssh_key_pwd: - gen_ssh_key(user.username, ssh_key_pwd) - ssh_key_pwd = CRYPTOR.encrypt(ssh_key_pwd) + else: + password_decode = None db_update_user(user_id=user_id, password=password, name=name, email=email, groups=groups, - dept=dept, + admin_groups=admin_groups, role=role_post, - is_active=is_active, - ssh_key_pwd=ssh_key_pwd) + is_active=is_active) + + if email_need: + + msg = u""" + Hi %s: + 您的信息已修改,请登录跳板机查看详细信息 + 地址:%s + 用户名: %s + 密码:%s (如果密码为None代表密码为原密码) + 角色:%s + + """ % (user.name, URL, user.username, password_decode, user_role.get(role_post, u'')) + + send_mail('您的信息已修改', msg, MAIL_FROM, [email], fail_silently=False) return HttpResponseRedirect('/juser/user_list/') diff --git a/templates/footer.html b/templates/footer.html index 969e5cfe6..0f1f6f76e 100644 --- a/templates/footer.html +++ b/templates/footer.html @@ -1,6 +1,6 @@
-
+ {% if error %}
{{ error }}
{% endif %} @@ -33,7 +33,6 @@
-
@@ -48,16 +47,6 @@
-
- -
- - - 登陆 Jumpserver 使用的SSH密钥的密码,更改密钥密码需要重新下载密钥 - -
-
-
@@ -79,23 +68,38 @@
- {% ifequal session_role_id 2 %}
- + {% for r, role_name in user_role.items %} +
+
+ +
+
+ {% endfor %} +
+
+ - {% endifequal %}
@@ -104,15 +108,15 @@
-
-
-
- +
+
+
+
-
- +
+
+
+
@@ -129,18 +133,20 @@
+{% endblock %} +{% block self_footer_js %} - - {% endblock %} \ No newline at end of file From 4eb78e151e9de9795067c9a5734a16278e7c37d9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 6 Sep 2015 21:37:22 +0800 Subject: [PATCH 020/385] =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=91=8A=E4=B8=80=E6=AE=B5=E8=90=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jasset/views.py | 7 - jumpserver/api.py | 10 +- jumpserver/context_processors.py | 20 +- jumpserver/templatetags/mytags.py | 4 +- jumpserver/views.py | 79 ++++---- juser/models.py | 5 + juser/urls.py | 3 +- juser/views.py | 183 +++++++++--------- templates/index_cu.html | 10 - .../juser/{chg_info.html => change_info.html} | 41 ++-- templates/juser/group_list.html | 2 +- templates/juser/profile.html | 2 - templates/juser/user_detail.html | 4 - templates/juser/user_list.html | 10 +- templates/nav_li_profile.html | 6 +- 15 files changed, 178 insertions(+), 208 deletions(-) rename templates/juser/{chg_info.html => change_info.html} (82%) diff --git a/jasset/views.py b/jasset/views.py index 22c72f8e5..57401f909 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -9,15 +9,8 @@ from django.shortcuts import get_object_or_404 from jperm.models import Perm from jumpserver.api import * -cryptor = PyCrypt(KEY) -class RaiseError(Exception): - pass - - -def my_render(template, data, request): - return render_to_response(template, data, context_instance=RequestContext(request)) def get_host_groups(groups): diff --git a/jumpserver/api.py b/jumpserver/api.py index c920137f1..72ac4fb1e 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -538,11 +538,9 @@ def get_session_user_info(request): 获取用户的信息 """ user_id = request.session.get('user_id', 0) - user = User.objects.filter(id=user_id) + user = get_object(User, id=user_id) if user: - user = user[0] - dept = user.dept - return [user.id, user.username, user, dept.id, dept.name, dept] + return [user.id, user.username, user] def get_user_dept(request): @@ -698,6 +696,10 @@ def http_error(request, emg): return render_to_response('error.html', locals()) +def my_render(template, data, request): + return render_to_response(template, data, context_instance=RequestContext(request)) + + CRYPTOR = PyCrypt(KEY) # if LDAP_ENABLE: diff --git a/jumpserver/context_processors.py b/jumpserver/context_processors.py index 2beeed3a0..9485fb12c 100644 --- a/jumpserver/context_processors.py +++ b/jumpserver/context_processors.py @@ -7,19 +7,13 @@ from jperm.models import Apply def name_proc(request): user_id = request.session.get('user_id') role_id = request.session.get('role_id') - if role_id == 2: - user_total_num = User.objects.all().count() - user_active_num = User.objects.filter().count() - host_total_num = Asset.objects.all().count() - host_active_num = Asset.objects.filter(is_active=True).count() - else: - user, dept = get_session_user_dept(request) - print user, dept - user_total_num = dept.user_set.all().count() - user_active_num = dept.user_set.filter(is_active=True).count() - host_total_num = dept.asset_set.all().count() - host_active_num = dept.asset_set.all().filter(is_active=True).count() - pass + # if role_id == 2: + user_total_num = User.objects.all().count() + user_active_num = User.objects.filter().count() + host_total_num = Asset.objects.all().count() + host_active_num = Asset.objects.filter(is_active=True).count() + # else: + # pass username = User.objects.get(id=user_id).name apply_info = Apply.objects.filter(admin=username, status=0, read=0) diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index b8b236dfe..c0d5dedbc 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -290,7 +290,7 @@ def to_dept_name(user_id): @register.filter(name='to_role_name') def to_role_name(role_id): - role_dict = {'0': '普通用户', '1': '部门管理员', '2': '超级管理员'} + role_dict = {'0': '普通用户', '1': '组管理员', '2': '超级管理员'} return role_dict.get(str(role_id), '未知') @@ -302,7 +302,7 @@ def to_avatar(role_id='0'): @register.filter(name='get_user_asset_group') def get_user_asset_group(user): - return user_perm_group_api(user) + return user.get_asset_group() @register.filter(name='group_asset_list') diff --git a/jumpserver/views.py b/jumpserver/views.py index e9d2819a2..e56452084 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -50,12 +50,9 @@ def get_data(data, items, option): @require_role(role='user') def index_cu(request): user_id = request.session.get('user_id') - user = User.objects.filter(id=user_id) - if user: - user = user[0] + user = get_object(User, id=user_id) login_types = {'L': 'LDAP', 'M': 'MAP'} - user_id = request.session.get('user_id') - username = User.objects.get(id=user_id).username + username = user.username posts = user.get_asset() host_count = len(posts) new_posts = [] @@ -91,16 +88,16 @@ def index(request): week_data = Log.objects.filter(start_time__range=[from_week, datetime.datetime.now()]) elif is_role_request(request, 'admin'): - user = get_session_user_info(request)[2] - dept_name, dept = get_session_user_info(request)[4:] - users = User.objects.filter(dept=dept) - hosts = Asset.objects.filter(dept=dept) - online = Log.objects.filter(dept_name=dept_name, is_finished=0) - online_host = online.values('host').distinct() - online_user = online.values('user').distinct() - active_users = users.filter(is_active=1) - active_hosts = hosts.filter(is_active=1) - week_data = Log.objects.filter(dept_name=dept_name, start_time__range=[from_week, datetime.datetime.now()]) + return index_cu(request) + # user = get_session_user_info(request)[2] + # users = User.objects.filter(dept=dept) + # hosts = Asset.objects.filter(dept=dept) + # online = Log.objects.filter(dept_name=dept_name, is_finished=0) + # online_host = online.values('host').distinct() + # online_user = online.values('user').distinct() + # active_users = users.filter(is_active=1) + # active_hosts = hosts.filter(is_active=1) + # week_data = Log.objects.filter(dept_name=dept_name, start_time__range=[from_week, datetime.datetime.now()]) # percent of dashboard if users.count() == 0: @@ -157,32 +154,32 @@ def skin_config(request): return render_to_response('skin_config.html') -def pages(posts, r): - """分页公用函数""" - contact_list = posts - p = paginator = Paginator(contact_list, 10) - try: - current_page = int(r.GET.get('page', '1')) - except ValueError: - current_page = 1 - - page_range = page_list_return(len(p.page_range), current_page) - - try: - contacts = paginator.page(current_page) - except (EmptyPage, InvalidPage): - contacts = paginator.page(paginator.num_pages) - - if current_page >= 5: - show_first = 1 - else: - show_first = 0 - if current_page <= (len(p.page_range) - 3): - show_end = 1 - else: - show_end = 0 - - return contact_list, p, contacts, page_range, current_page, show_first, show_end +# def pages(posts, r): +# """分页公用函数""" +# contact_list = posts +# p = paginator = Paginator(contact_list, 10) +# try: +# current_page = int(r.GET.get('page', '1')) +# except ValueError: +# current_page = 1 +# +# page_range = page_list_return(len(p.page_range), current_page) +# +# try: +# contacts = paginator.page(current_page) +# except (EmptyPage, InvalidPage): +# contacts = paginator.page(paginator.num_pages) +# +# if current_page >= 5: +# show_first = 1 +# else: +# show_first = 0 +# if current_page <= (len(p.page_range) - 3): +# show_end = 1 +# else: +# show_end = 0 +# +# return contact_list, p, contacts, page_range, current_page, show_first, show_end def is_latest(): diff --git a/juser/models.py b/juser/models.py index d2a0d9f8d..67325ef7f 100644 --- a/juser/models.py +++ b/juser/models.py @@ -120,6 +120,11 @@ class User(models.Model): else: return assets_info + def update(self, **kwargs): + for key, value in kwargs.items(): + self.__setattr__(key, value) + self.save() + class AdminGroup(models.Model): """ diff --git a/juser/urls.py b/juser/urls.py index 4f3e5daa5..1be8d0f5e 100644 --- a/juser/urls.py +++ b/juser/urls.py @@ -22,7 +22,8 @@ urlpatterns = patterns('juser.views', (r'^user_del_ajax/$', 'user_del_ajax'), (r'^user_edit/$', view_splitter, {'su': user_edit, 'adm': user_edit_adm}), (r'^profile/$', 'profile'), - (r'^chg_info/$', 'chg_info'), + (r'^change_info/$', 'change_info'), + (r'^regen_ssh_key/$', 'regen_ssh_key'), (r'^chg_role/$', 'chg_role'), (r'^down_key/$', 'down_key'), ) diff --git a/juser/views.py b/juser/views.py index c77f90cd2..833500b33 100644 --- a/juser/views.py +++ b/juser/views.py @@ -55,7 +55,7 @@ def group_add(request): else: msg = u'添加组 %s 成功' % group_name - return render_to_response('juser/group_add.html', locals(), context_instance=RequestContext(request)) + return my_render('juser/group_add.html', locals(), request) @require_role(role='super') @@ -72,7 +72,7 @@ def group_list(request): user_group_list = user_group_list.filter(Q(name__icontains=keyword) | Q(comment__icontains=keyword)) contacts, p, contacts, page_range, current_page, show_first, show_end = pages(user_group_list, request) - return render_to_response('juser/group_list.html', locals(), context_instance=RequestContext(request)) + return my_render('juser/group_list.html', locals(), request) @require_role(role='super') @@ -169,7 +169,7 @@ def group_edit(request): users_selected = user_group.user_set.all() users_remain = [user for user in users_all if user not in users_selected] - return render_to_response('juser/group_edit.html', locals(), context_instance=RequestContext(request)) + return my_render('juser/group_edit.html', locals(), request) # @require_role(role='admin') @@ -275,7 +275,7 @@ def user_add(request): if MAIL_ENABLE and send_mail_need: user_add_mail(user, kwargs=locals()) msg = get_display_msg(user, password, ssh_key_pwd, ssh_key_login_need, send_mail_need) - return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) + return my_render('juser/user_add.html', locals(), request) # @require_role(role='admin') @@ -369,7 +369,7 @@ def user_list(request): contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(contact_list, request) - return render_to_response('juser/user_list.html', locals(), context_instance=RequestContext(request)) + return my_render('juser/user_list.html', locals(), request) # @require_role(role='admin') @@ -413,12 +413,13 @@ def user_detail(request): user = get_object(User, id=user_id) if user: + pass # asset_group_permed = user.get_asset_group() - logs_last = Log.objects.filter(user=user.name).order_by('-start_time')[0:10] - logs_all = Log.objects.filter(user=user.name).order_by('-start_time') - logs_num = len(logs_all) + # logs_last = Log.objects.filter(user=user.name).order_by('-start_time')[0:10] + # logs_all = Log.objects.filter(user=user.name).order_by('-start_time') + # logs_num = len(logs_all) - return render_to_response('juser/user_detail.html', locals(), context_instance=RequestContext(request)) + return my_render('juser/user_detail.html', locals(), request) @require_role(role='admin') @@ -545,9 +546,8 @@ def user_edit(request): extra = request.POST.getlist('extra', []) is_active = True if '0' in extra else False email_need = True if '2' in extra else False - user_role = {'SU': u'超级管理员', 'DA': u'部门管理员', 'CU': u'普通用户'} - print '#'*10 + str(email_need) - print extra + user_role = {'SU': u'超级管理员', 'GA': u'部门管理员', 'CU': u'普通用户'} + if user_id: user = get_object(User, id=user_id) else: @@ -568,8 +568,9 @@ def user_edit(request): role=role_post, is_active=is_active) - if email_need: + print '#'* 10 + role_post + if email_need: msg = u""" Hi %s: 您的信息已修改,请登录跳板机查看详细信息 @@ -579,68 +580,69 @@ def user_edit(request): 角色:%s """ % (user.name, URL, user.username, password_decode, user_role.get(role_post, u'')) - send_mail('您的信息已修改', msg, MAIL_FROM, [email], fail_silently=False) return HttpResponseRedirect('/juser/user_list/') - return render_to_response('juser/user_edit.html', locals(), context_instance=RequestContext(request)) + return my_render('juser/user_edit.html', locals(), request) -@require_role(role='admin') +# @require_role(role='admin') def user_edit_adm(request): - header_title, path1, path2 = '编辑用户', '用户管理', '用户编辑' - user, dept = get_session_user_dept(request) - if request.method == 'GET': - user_id = request.GET.get('id', '') - if not user_id: - return HttpResponseRedirect('/juser/user_list/') - - if not validate(request, user=[user_id]): - return HttpResponseRedirect('/juser/user_list/') - - user = User.objects.filter(id=user_id) - dept_all = DEPT.objects.all() - group_all = dept.usergroup_set.all() - if user: - user = user[0] - groups_str = ' '.join([str(group.id) for group in user.group.all()]) - - else: - user_id = request.POST.get('user_id', '') - password = request.POST.get('password', '') - name = request.POST.get('name', '') - email = request.POST.get('email', '') - groups = request.POST.getlist('groups', []) - ssh_key_pwd = request.POST.get('ssh_key_pwd', '') - is_active = True if request.POST.get('is_active', '1') == '1' else False - - if not validate(request, user=[user_id], user_group=groups): - return HttpResponseRedirect('/juser/user_edit/') - if user_id: - user = User.objects.filter(id=user_id) - if user: - user = user[0] - else: - return HttpResponseRedirect('/juser/user_list/') - - if password != user.password: - password = CRYPTOR.md5_crypt(password) - - if ssh_key_pwd != user.ssh_key_pwd: - ssh_key_pwd = CRYPTOR.encrypt(ssh_key_pwd) - - db_update_user(user_id=user_id, - password=password, - name=name, - email=email, - groups=groups, - is_active=is_active, - ssh_key_pwd=ssh_key_pwd) - - return HttpResponseRedirect('/juser/user_list/') - - return render_to_response('juser/user_edit.html', locals(), context_instance=RequestContext(request)) + pass +# header_title, path1, path2 = '编辑用户', '用户管理', '用户编辑' +# user, dept = get_session_user_dept(request) +# if request.method == 'GET': +# user_id = request.GET.get('id', '') +# if not user_id: +# return HttpResponseRedirect('/juser/user_list/') +# +# if not validate(request, user=[user_id]): +# return HttpResponseRedirect('/juser/user_list/') +# +# user = User.objects.filter(id=user_id) +# dept_all = DEPT.objects.all() +# group_all = dept.usergroup_set.all() +# if user: +# user = user[0] +# groups_str = ' '.join([str(group.id) for group in user.group.all()]) +# +# else: +# user_id = request.POST.get('user_id', '') +# password = request.POST.get('password', '') +# name = request.POST.get('name', '') +# email = request.POST.get('email', '') +# groups = request.POST.getlist('groups', []) +# ssh_key_pwd = request.POST.get('ssh_key_pwd', '') +# is_active = True if request.POST.get('is_active', '1') == '1' else False +# +# if not validate(request, user=[user_id], user_group=groups): +# return HttpResponseRedirect('/juser/user_edit/') +# if user_id: +# user = User.objects.filter(id=user_id) +# if user: +# user = user[0] +# else: +# return HttpResponseRedirect('/juser/user_list/') +# +# if password != user.password: +# password = CRYPTOR.md5_crypt(password) +# +# if ssh_key_pwd != user.ssh_key_pwd: +# ssh_key_pwd = CRYPTOR.encrypt(ssh_key_pwd) +# +# db_update_user(user_id=user_id, +# password=password, +# name=name, +# email=email, +# groups=groups, +# is_active=is_active, +# ssh_key_pwd=ssh_key_pwd) +# +# return HttpResponseRedirect('/juser/user_list/') +# +# return render_to_response('juser/user_edit.html', locals(), context_instance=RequestContext(request)) +# def profile(request): @@ -651,40 +653,46 @@ def profile(request): return render_to_response('juser/profile.html', locals(), context_instance=RequestContext(request)) -def chg_info(request): +def change_info(request): header_title, path1, path2 = '修改信息', '用户管理', '修改个人信息' user_id = request.session.get('user_id') - user_set = User.objects.filter(id=user_id) + user = get_object(User, id=user_id) error = '' - if user_set: - user = user_set[0] - else: + if not user: return HttpResponseRedirect('/') if request.method == 'POST': name = request.POST.get('name', '') password = request.POST.get('password', '') - ssh_key_pwd = request.POST.get('ssh_key_pwd', '') email = request.POST.get('email', '') - if '' in [name, password, ssh_key_pwd, email]: + if '' in [name, password, email]: error = '不能为空' - if len(password) < 6 or len(ssh_key_pwd) < 6: + if len(password) < 6 : error = '密码须大于6位' if not error: if password != user.password: password = CRYPTOR.md5_crypt(password) - if ssh_key_pwd != user.ssh_key_pwd: - gen_ssh_key(user.username, ssh_key_pwd) - ssh_key_pwd = CRYPTOR.md5_crypt(ssh_key_pwd) - - user_set.update(name=name, password=password, ssh_key_pwd=ssh_key_pwd, email=email) + user.update(name=name, password=password, email=email) msg = '修改成功' - return render_to_response('juser/chg_info.html', locals(), context_instance=RequestContext(request)) + return render_to_response('juser/change_info.html', locals(), context_instance=RequestContext(request)) + + +@require_role(role='user') +def regen_ssh_key(request): + uuid = request.GET.get('uuid', '') + user = get_object(User, uuid=uuid) + if not user: + return HttpResponse('没有该用户') + + username = user.username + ssh_key_pass = PyCrypt.random_pass(16) + gen_ssh_key(username, ssh_key_pass) + return HttpResponse('ssh密钥已生成,密码为 %s, 请到下载页面下载' % ssh_key_pass) @require_role(role='user') @@ -693,21 +701,14 @@ def down_key(request): if is_role_request(request, 'super'): user_id = request.GET.get('id') - if is_role_request(request, 'admin'): - user_id = request.GET.get('id') - if not validate(request, user=[user_id]): - user_id = request.session.get('user_id') - if is_role_request(request, 'user'): user_id = request.session.get('user_id') if user_id: - user = User.objects.filter(id=user_id) + user = get_object(User, id=user_id) if user: - user = user[0] username = user.username - private_key_dir = os.path.join(BASE_DIR, 'keys/jumpserver/') - private_key_file = os.path.join(private_key_dir, username+".pem") + private_key_file = os.path.join(BASE_DIR, 'keys/jumpserver', username+".pem") if os.path.isfile(private_key_file): f = open(private_key_file) data = f.read() diff --git a/templates/index_cu.html b/templates/index_cu.html index 116613fab..b52f2b4dc 100644 --- a/templates/index_cu.html +++ b/templates/index_cu.html @@ -16,12 +16,6 @@ - @@ -117,10 +111,6 @@ 角色 {{ user.id | get_role }} - - 部门 - {{ user.dept.name }} - Email {{ user.email }} diff --git a/templates/juser/chg_info.html b/templates/juser/change_info.html similarity index 82% rename from templates/juser/chg_info.html rename to templates/juser/change_info.html index 1658d6a4b..521e96ec4 100644 --- a/templates/juser/chg_info.html +++ b/templates/juser/change_info.html @@ -14,15 +14,7 @@ - - - - + @@ -59,11 +51,11 @@
- +
- + 重新生成 - 登陆 Jumpserver 使用的SSH密钥的密码,更改密钥密码需要重新下载密钥 + 重新生成密钥,需要重新下载并导入
@@ -89,7 +81,9 @@ +{% endblock %} +{% block self_footer_js %} - - {% endblock %} \ No newline at end of file diff --git a/templates/juser/group_list.html b/templates/juser/group_list.html index 6958e47b6..4085685c9 100644 --- a/templates/juser/group_list.html +++ b/templates/juser/group_list.html @@ -32,7 +32,7 @@
diff --git a/templates/juser/profile.html b/templates/juser/profile.html index 7be33cce4..80d880ba7 100644 --- a/templates/juser/profile.html +++ b/templates/juser/profile.html @@ -24,7 +24,6 @@ 用户名 姓名 角色 - 部门 Email 激活 {# 添加时间#} @@ -38,7 +37,6 @@ {{ user.username }} {{ user.name }} {{ user.id | get_role }} - {{ user.dept.name }} {{ user.email }} {{ user.is_active|bool2str }} {# {{ user.date_joined }}#} diff --git a/templates/juser/user_detail.html b/templates/juser/user_detail.html index 883085ed8..88b284c4f 100644 --- a/templates/juser/user_detail.html +++ b/templates/juser/user_detail.html @@ -48,10 +48,6 @@ 角色 {{ user.id | get_role }} - - 部门 - {{ user.dept.name }} - Email {{ user.email }} diff --git a/templates/juser/user_list.html b/templates/juser/user_list.html index 0f9c2827c..b470c5e84 100644 --- a/templates/juser/user_list.html +++ b/templates/juser/user_list.html @@ -10,18 +10,12 @@
查看用户
- + - @@ -37,7 +31,7 @@
diff --git a/templates/nav_li_profile.html b/templates/nav_li_profile.html index 47c8dcd70..14c55cdba 100644 --- a/templates/nav_li_profile.html +++ b/templates/nav_li_profile.html @@ -7,7 +7,7 @@ - {{ session_user_id | to_name}} {{ session_user_id | to_dept_name}} + {{ session_user_id | to_name}} {{ session_role_id | to_role_name }} @@ -16,8 +16,8 @@
diff --git a/templates/jasset/host_add.html b/templates/jasset/asset_add.html similarity index 50% rename from templates/jasset/host_add.html rename to templates/jasset/asset_add.html index bbfa730b5..fe6d72f5a 100644 --- a/templates/jasset/host_add.html +++ b/templates/jasset/asset_add.html @@ -7,7 +7,7 @@
-
填写主机基本信息
+
填写资产基本信息
@@ -15,12 +15,6 @@ - @@ -31,87 +25,61 @@
- {% if emg %} -
{{ emg }}
+ {% if error %} +
{{ error }}
{% endif %} - {% if smg %} -
{{ smg }}
+ {% if msg %} +
{{ msg }}
{% endif %}
-
+
-
-
-
- -
-
+
+
-
-
-
- -
- -
- + +
+
+ +
- {% ifequal session_role_id 2 %} -
-
- -
- -
+ -
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/jasset/idc_edit.html b/templates/jasset/idc_edit.html deleted file mode 100644 index 0751a4114..000000000 --- a/templates/jasset/idc_edit.html +++ /dev/null @@ -1,109 +0,0 @@ -{% extends 'base.html' %} -{% block content %} -{% include 'nav_cat_bar.html' %} -
-
-
-
-
-
填写IDC基本信息
- -
- -
- {% if emg %} -
{{ emg }}
- {% endif %} - {% if smg %} -
{{ smg }}
- {% endif %} -
-
-
- - -
-
- -
-
-
-
- -
-
- -
- -
-
-
- - -
-
-
-
- -
-
-
- -
-
-
- - -
-
-
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/jasset/idc_list.html b/templates/jasset/idc_list.html deleted file mode 100644 index e1f5dd465..000000000 --- a/templates/jasset/idc_list.html +++ /dev/null @@ -1,125 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
IDC详细信息列表
- -
-
-
- {% ifequal session_role_id 2 %} - 添加IDC - {% endifequal %} - -
- -
- - - - {% ifequal session_role_id 2 %} - - {% endifequal %} - - - - - - - - {% for post in contacts.object_list %} - - {% ifequal session_role_id 2 %} - - {% endifequal %} - - {% ifequal session_role_id 2 %} - - {% else %} - - {% endifequal %} - - - - {% endfor %} - -
机房名 主机数量 备注 操作
{{ post.name }} {{ post.asset_set.count }} {{ post|get_idc_count:dept }} {{ post.comment }} - 详情 - {% ifequal session_role_id 2 %} - 编辑 - 删除 - {% endifequal %} -
-
-
- {% ifequal session_role_id 2 %} - - - {% endifequal %} -
- {% include 'paginator.html' %} -
-
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/juser/group_list.html b/templates/juser/group_list.html index 4085685c9..0755016fd 100644 --- a/templates/juser/group_list.html +++ b/templates/juser/group_list.html @@ -52,7 +52,7 @@ - {% for group in contacts.object_list %} + {% for group in user_groups.object_list %} diff --git a/templates/juser/user_detail.html b/templates/juser/user_detail.html index 88b284c4f..479dd991d 100644 --- a/templates/juser/user_detail.html +++ b/templates/juser/user_detail.html @@ -109,30 +109,30 @@ 这里包含了用户所有的主机组和组下的主机.
- {% for group in user|get_user_asset_group %} -
-
-
- - {{ group.name }} -
- 共: {{ group | group_asset_list_count }}台 -
-
-

{{ group.comment }}

-

- {% for asset in group|group_asset_list %} - {{ asset.ip }}
- {% endfor %} -

-

-
-
-
- {% endfor %} - {% if not user|get_user_asset_group %} - (无) - {% endif %} +{# {% for group in user|get_user_asset_group %}#} +{#
#} +{#
#} +{#
#} +{# #} +{# {{ group.name }}#} +{#
#} +{# 共: {{ group | group_asset_list_count }}台#} +{#
#} +{#
#} +{#

{{ group.comment }}

#} +{#

#} +{# {% for asset in group|group_asset_list %}#} +{# {{ asset.ip }}
#} +{# {% endfor %}#} +{#

#} +{#

#} +{#
#} +{#
#} +{#
#} +{# {% endfor %}#} +{# {% if not user|get_user_asset_group %}#} +{# (无)#} +{# {% endif %}#}
@@ -154,7 +154,8 @@ image
- {{ log.start_time|time_delta }} +{# {{ log.start_time|time_delta }}#} + {{ log.start_time }} {{ log.user }} 登录了 {{ log.host }}.
{{ log.start_time|date:"Y-m-d H:i:s" }}
@@ -177,21 +178,18 @@ image
- {{ log.start_time|time_delta }} + {{ log.start_time }} +{# {{ log.start_time|time_delta }}#} {{ log.user }} 登录了 {{ log.host }}.
{{ log.start_time|date:"Y-m-d H:i:s" }}
{% endfor %}
-
-
-
- diff --git a/templates/juser/user_list.html b/templates/juser/user_list.html index b470c5e84..32e940868 100644 --- a/templates/juser/user_list.html +++ b/templates/juser/user_list.html @@ -54,7 +54,7 @@ - {% for user in contacts.object_list %} + {% for user in users.object_list %} @@ -63,7 +63,7 @@ {{ user.name }} {{ user.group.all | group_str2 }} {{ user.id | get_role }} - {{ user.is_active|bool2str }} + {{ user.is_active | bool2str }} 下载 详情 @@ -78,7 +78,7 @@
- Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries + Showing {{ users.start_index }} to {{ users.end_index }} of {{ p.count }} entries
{% include 'paginator.html' %} @@ -93,25 +93,27 @@ {% block self_head_css_js %} {% endblock %} diff --git a/templates/juser/group_list.html b/templates/juser/group_list.html index 0755016fd..092c75776 100644 --- a/templates/juser/group_list.html +++ b/templates/juser/group_list.html @@ -26,7 +26,7 @@
添加用户组 - 删除所选组 + 删除所选 - - - {% endblock %} \ No newline at end of file From 06eedff49f79f21a57c0da6096c8832547e87513 Mon Sep 17 00:00:00 2001 From: "ibuler@qq.com" Date: Wed, 7 Oct 2015 20:14:07 +0800 Subject: [PATCH 043/385] fix common --- jperm/perm_api.py | 97 ++++++++++++++++++----------- jperm/views.py | 2 +- playbook/user_perm.yaml | 10 ++- templates/jperm/perm_user_edit.html | 2 +- 4 files changed, 66 insertions(+), 45 deletions(-) diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 185fe969d..21a69ac09 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -54,19 +54,18 @@ def get_playbook(template, var): return path -def playbook_run(inventory, playbook, default_user=None, default_port=None, default_pri_key_path=None): +def playbook_run(inventory, playbook, settings): stats = callbacks.AggregateStats() playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY) # run the playbook - print default_user, default_port, default_pri_key_path, inventory, playbook - if default_user and default_port and default_pri_key_path: + if settings: playbook = PlayBook(host_list=inventory, playbook=playbook, forks=5, - remote_user=default_user, - remote_port=default_port, - private_key_file=default_pri_key_path, + remote_user=settings.default_user, + remote_port=settings.default_port, + private_key_file=settings.default_pri_key_path, callbacks=playbook_cb, runner_callbacks=runner_cb, stats=stats, @@ -98,43 +97,67 @@ def playbook_run(inventory, playbook, default_user=None, default_port=None, defa return results_r -def perm_user_api(asset_new, asset_del, asset_group_new, asset_group_del, user=None, user_group=None): - """用户授权api,通过调用ansible API完成用户新建等""" - asset_new_ip = [] # 新授权的ip列表 - asset_del_ip = [] # 回收授权的ip列表 +def perm_user_api(perm_info): + """ + 用户授权api,通过调用ansible API完成用户新建等,传入参数必须如下,列表中可以是对象,也可以是用户名和ip + perm_info = {'del': {'users': [], + 'assets': [], + }, + 'new': {'users': [], + 'assets': []}} + """ + try: + new_users = perm_info['new']['users'] + new_assets = perm_info['new']['assets'] + del_users = perm_info['del']['users'] + del_assets = perm_info['del']['assets'] + except IndexError: + raise ServerError("Error: function perm_user_api传入参数错误") - asset_new_ip.extend([asset.ip for asset in asset_new]) # 查库,获取新授权ip - for asset_group in asset_group_new: - asset_new_ip.extend([asset.ip for asset in asset_group.asset_set.all()]) # 同理 - asset_del_ip.extend([asset.ip for asset in asset_del]) # 查库,获取回收授权的ip - for asset_group in asset_group_del: - asset_del_ip.extend([asset.ip for asset in asset_group.asset_set.all()]) # 同理 - - if asset_new_ip or asset_del_ip: - host_group = {'new': asset_new_ip, 'del': asset_del_ip} - inventory = get_inventory(host_group) - if user: - the_items = user.username, - elif user_group: - users = user_group.user_set.all() - the_items = ','.join([user.username for user in users]) + # 检查传入的是字符串还是对象 + check_users = new_users + del_users + try: + if isinstance(check_users[0], str): + var_type = 'str' else: - return HttpResponse('Argument error.') + var_type = 'obj' - playbook = get_playbook(os.path.join(BASE_DIR, 'playbook', 'user_perm.yaml'), - {'the_new_group': 'new', 'the_del_group': 'del', - 'the_items': the_items, 'the_pub_key': '/tmp/id_rsa.pub'}) + except IndexError: + raise ServerError("Error: function perm_user_api传入参数错误") - settings = get_object(Setting, id=1) - if settings: - default_user = settings.default_user - default_port = settings.default_port - default_pri_key_path = settings.default_pri_key_path + print new_assets, del_assets + print new_users, del_users + try: + if var_type == 'str': + new_ip = new_assets + del_ip = del_assets + new_username = new_users + del_username = del_users else: - default_user = default_port = default_pri_key_path = '' + new_ip = [asset.ip for asset in new_assets if isinstance(asset, Asset)] + del_ip = [asset.ip for asset in del_assets if isinstance(asset, Asset)] + new_username = [user.username for user in new_users if isinstance(user, User)] + del_username = [user.username for user in del_users if isinstance(user, User)] + except IndexError: + raise ServerError("Error: function perm_user_api传入参数类型错误") - results_r = playbook_run(inventory, playbook, default_user, default_port, default_pri_key_path) - return results_r + print new_ip, del_ip + print new_username, del_username + + host_group = {'new': new_ip, 'del': del_ip} + inventory = get_inventory(host_group) + + the_new_users = ','.join(new_username) + the_del_users = ','.join(del_username) + + playbook = get_playbook(os.path.join(BASE_DIR, 'playbook', 'user_perm.yaml'), + {'the_new_group': 'new', 'the_del_group': 'del', + 'the_new_users': the_new_users, 'the_del_users': the_del_users, + 'the_pub_key': '/tmp/id_rsa.pub'}) + + settings = get_object(Setting, name='default') + results_r = playbook_run(inventory, playbook, settings) + return results_r def refresh_group_api(user_group=None, asset_group=None): diff --git a/jperm/views.py b/jperm/views.py index 15f375d79..98c1b3bb7 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -1 +1 @@ -# # coding: utf-8 # import sys # # reload(sys) # sys.setdefaultencoding('utf8') # # from django.shortcuts import render_to_response # from django.template import RequestContext # from jperm.models import Perm, SudoPerm, CmdGroup, Apply import json from django.db.models import Q from jperm.models import * from jumpserver.api import * from jperm.perm_api import * @require_role('admin') def perm_user_list(request): header_title, path1, path2 = '用户授权', '授权管理', '用户授权' keyword = request.GET.get('search', '') users_list = User.objects.all() # 获取所有用户 if keyword: users_list = users_list.filter(Q(name=keyword) | Q(username=keyword)) # 搜索 users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) # 分页 return my_render('jperm/perm_user_list.html', locals(), request) @require_role('admin') def perm_user_edit(request): header_title, path1, path2 = '用户授权', '授权管理', '授权更改' user_id = request.GET.get('id', '') user = get_object(User, id=user_id) asset_all = Asset.objects.all() # 获取所有资产 asset_group_all = AssetGroup.objects.all() # 获取所有资产组 asset_permed = user.asset.all() # 获取授权的资产对象列表 asset_group_permed = user.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user: assets = [asset for asset in asset_all if asset not in asset_permed] # 获取没有授权的资产对象列表 asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] # 同理 return my_render('jperm/perm_user_edit.html', locals(), request) elif request.method == 'POST' and user: asset_id_select = request.POST.getlist('asset_select', []) # 获取选择的资产id列表 asset_group_id_select = request.POST.getlist('asset_groups_select', []) # 获取选择的资产组id列表 asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 results = perm_user_api(asset_new, asset_del, asset_group_new, asset_group_del, user=user) # 通过API授权或回收 unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user.asset = asset_select user.asset_group = asset_group_select user.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') @require_role('admin') def perm_group_list(request): header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' keyword = request.GET.get('search', '') user_groups_list = UserGroup.objects.all() if keyword: request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) return my_render('jperm/perm_group_list.html', locals(), request) @require_role('admin') def perm_group_edit(request): header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' user_group_id = request.GET.get('id', '') user_group = get_object(UserGroup, id=user_group_id) asset_all = Asset.objects.all() asset_group_all = AssetGroup.objects.all() asset_permed = user_group.asset.all() # 获取授权的资产对象列表 asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user_group: assets = [asset for asset in asset_all if asset not in asset_permed] asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] return my_render('jperm/perm_group_edit.html', locals(), request) elif request.method == 'POST' and user_group: asset_id_select = request.POST.getlist('asset_select', []) asset_group_id_select = request.POST.getlist('asset_groups_select', []) asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 results = perm_user_api(asset_new, asset_del, asset_group_new, asset_group_del, user_group=user_group) # 通过API授权或回收 unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user_group.asset = asset_select user_group.asset_group = asset_group_select user_group.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') \ No newline at end of file +# # coding: utf-8 # import sys # # reload(sys) # sys.setdefaultencoding('utf8') # # from django.shortcuts import render_to_response # from django.template import RequestContext # from jperm.models import Perm, SudoPerm, CmdGroup, Apply from django.db.models import Q from jperm.models import * from jumpserver.api import * from jperm.perm_api import * @require_role('admin') def perm_user_list(request): header_title, path1, path2 = '用户授权', '授权管理', '用户授权' keyword = request.GET.get('search', '') users_list = User.objects.all() # 获取所有用户 if keyword: users_list = users_list.filter(Q(name=keyword) | Q(username=keyword)) # 搜索 users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) # 分页 return my_render('jperm/perm_user_list.html', locals(), request) @require_role('admin') def perm_user_edit(request): header_title, path1, path2 = '用户授权', '授权管理', '授权更改' user_id = request.GET.get('id', '') user = get_object(User, id=user_id) asset_all = Asset.objects.all() # 获取所有资产 asset_group_all = AssetGroup.objects.all() # 获取所有资产组 asset_permed = user.asset.all() # 获取授权的资产对象列表 asset_group_permed = user.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user: assets = [asset for asset in asset_all if asset not in asset_permed] # 获取没有授权的资产对象列表 asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] # 同理 return my_render('jperm/perm_user_edit.html', locals(), request) elif request.method == 'POST' and user: asset_id_select = request.POST.getlist('asset_select', []) # 获取选择的资产id列表 asset_group_id_select = request.POST.getlist('asset_groups_select', []) # 获取选择的资产组id列表 asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 perm_info = { 'del': {'users': [user], 'assets': asset_del}, 'new': {'users': [user], 'assets': asset_new} } try: results = perm_user_api(perm_info) # 通过API授权或回收 except ServerError, e: return HttpResponse(e) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user.asset = asset_select user.asset_group = asset_group_select user.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') @require_role('admin') def perm_group_list(request): header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' keyword = request.GET.get('search', '') user_groups_list = UserGroup.objects.all() if keyword: request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) return my_render('jperm/perm_group_list.html', locals(), request) @require_role('admin') def perm_group_edit(request): header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' user_group_id = request.GET.get('id', '') user_group = get_object(UserGroup, id=user_group_id) asset_all = Asset.objects.all() asset_group_all = AssetGroup.objects.all() asset_permed = user_group.asset.all() # 获取授权的资产对象列表 asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user_group: assets = [asset for asset in asset_all if asset not in asset_permed] asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] return my_render('jperm/perm_group_edit.html', locals(), request) elif request.method == 'POST' and user_group: asset_id_select = request.POST.getlist('asset_select', []) asset_group_id_select = request.POST.getlist('asset_groups_select', []) asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 results = perm_user_api(asset_new, asset_del, asset_group_new, asset_group_del, user_group=user_group) # 通过API授权或回收 unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user_group.asset = asset_select user_group.asset_group = asset_group_select user_group.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') \ No newline at end of file diff --git a/playbook/user_perm.yaml b/playbook/user_perm.yaml index d57ff1964..6310249d4 100644 --- a/playbook/user_perm.yaml +++ b/playbook/user_perm.yaml @@ -2,18 +2,16 @@ tasks: - name: del user user: name={{ item }} state=absent remove=yes - with_items: [ the_items ] + with_items: [ the_del_users ] - hosts: the_new_group tasks: - name: add user user: name={{ item }} state=present - with_items: [ the_items ] + with_items: [ the_new_users ] - name: .ssh direcotory file: name=/home/{{ item }}/.ssh mode=700 owner={{ item }} group={{ item }} state=directory - with_items: [ the_items ] + with_items: [ the_new_users ] - name: set authorizied_file copy: src=the_pub_key dest=/home/{{ item }}/.ssh/authorizied_keys owner={{ item }} group={{ item }} mode=600 - with_items: [ the_items ] - - + with_items: [ the_new_users ] diff --git a/templates/jperm/perm_user_edit.html b/templates/jperm/perm_user_edit.html index e7db76b0d..5d06997f5 100644 --- a/templates/jperm/perm_user_edit.html +++ b/templates/jperm/perm_user_edit.html @@ -107,7 +107,7 @@
- +
From 50d2bfb27200f44c4eedb5a7a26f46829101a39c Mon Sep 17 00:00:00 2001 From: "ibuler@qq.com" Date: Mon, 12 Oct 2015 09:58:38 +0800 Subject: [PATCH 044/385] someday --- jperm/models.py | 72 +---- jperm/perm_api.py | 68 ++--- jperm/urls.py | 1 + jperm/views.py | 2 +- jumpserver/api.py | 2 +- jumpserver/settings.py | 6 +- jumpserver/templatetags/mytags.py | 358 +------------------------ templates/jperm/dept_perm_edit.html | 179 ------------- templates/jperm/dept_perm_list.html | 104 ------- templates/jperm/perm_apply.html | 187 ------------- templates/jperm/perm_apply_exec.html | 31 --- templates/jperm/perm_apply_info.html | 55 ---- templates/jperm/perm_apply_search.html | 40 --- templates/jperm/perm_asset_detail.html | 61 ----- templates/jperm/perm_detail.html | 118 -------- templates/jperm/perm_edit.html | 130 --------- templates/jperm/perm_edit_bak.html | 138 ---------- templates/jperm/perm_list_ajax.html | 132 --------- templates/jperm/perm_log.html | 108 +++----- templates/jperm/perm_log_offline.html | 127 --------- templates/jperm/perm_log_online.html | 128 --------- templates/jperm/perm_user_detail.html | 240 ----------------- templates/jperm/sudo_add.html | 226 ---------------- templates/jperm/sudo_cmd_add.html | 148 ---------- templates/jperm/sudo_cmd_detail.html | 48 ---- templates/jperm/sudo_cmd_list.html | 140 ---------- templates/jperm/sudo_detail.html | 170 ------------ templates/jperm/sudo_edit.html | 155 ----------- templates/jperm/sudo_list.html | 129 --------- templates/nav.html | 1 + 30 files changed, 88 insertions(+), 3216 deletions(-) delete mode 100644 templates/jperm/dept_perm_edit.html delete mode 100644 templates/jperm/dept_perm_list.html delete mode 100644 templates/jperm/perm_apply.html delete mode 100644 templates/jperm/perm_apply_exec.html delete mode 100644 templates/jperm/perm_apply_info.html delete mode 100644 templates/jperm/perm_apply_search.html delete mode 100644 templates/jperm/perm_asset_detail.html delete mode 100644 templates/jperm/perm_detail.html delete mode 100644 templates/jperm/perm_edit.html delete mode 100644 templates/jperm/perm_edit_bak.html delete mode 100644 templates/jperm/perm_list_ajax.html delete mode 100644 templates/jperm/perm_log_offline.html delete mode 100644 templates/jperm/perm_log_online.html delete mode 100644 templates/jperm/perm_user_detail.html delete mode 100644 templates/jperm/sudo_add.html delete mode 100644 templates/jperm/sudo_cmd_add.html delete mode 100644 templates/jperm/sudo_cmd_detail.html delete mode 100644 templates/jperm/sudo_cmd_list.html delete mode 100644 templates/jperm/sudo_detail.html delete mode 100644 templates/jperm/sudo_edit.html delete mode 100644 templates/jperm/sudo_list.html diff --git a/jperm/models.py b/jperm/models.py index 336016f8f..1a07e6572 100644 --- a/jperm/models.py +++ b/jperm/models.py @@ -5,71 +5,7 @@ from juser.models import User, UserGroup from jasset.models import Asset, AssetGroup -# class PermUserAsset(models.Model): -# user = models.ForeignKey(User) -# asset = models.ForeignKey(Asset) -# -# def __unicode__(self): -# return self.user.username -# -# -# class PermUserAssetGroup(models.Model): -# user = models.ForeignKey(User) -# asset_group = models.ForeignKey(AssetGroup) -# -# def __unicode__(self): -# return self.user.username -# -# -# class PermUserGroupAsset(models.Model): -# user_group = models.ForeignKey(UserGroup) -# asset = models.ForeignKey(Asset) -# -# def __unicode__(self): -# return self.user_group.name -# -# -# class PermUserGroupAssetGroup(models.Model): -# user_group = models.ForeignKey(UserGroup) -# asset_group = models.ForeignKey(AssetGroup) -# -# def __unicode__(self): -# return self.user_group.name - - - -# class CmdGroup(models.Model): -# name = models.CharField(max_length=50, unique=True) -# cmd = models.CharField(max_length=999) -# comment = models.CharField(blank=True, null=True, max_length=50) -# -# def __unicode__(self): -# return self.name -# -# -# class SudoPerm(models.Model): -# user_group = models.ForeignKey(UserGroup) -# user_runas = models.CharField(max_length=100) -# asset_group = models.ManyToManyField(AssetGroup) -# cmd_group = models.ManyToManyField(CmdGroup) -# comment = models.CharField(max_length=30, null=True, blank=True) -# -# def __unicode__(self): -# return self.user_group.name -# -# -# class Apply(models.Model): -# uuid = UUIDField(auto=True) -# applyer = models.CharField(max_length=20) -# admin = models.CharField(max_length=20) -# approver = models.CharField(max_length=20) -# bisgroup = models.CharField(max_length=500) -# asset = models.CharField(max_length=500) -# comment = models.TextField(blank=True, null=True) -# status = models.IntegerField(max_length=2) -# date_add = models.DateTimeField(null=True) -# date_end = models.DateTimeField(null=True) -# read = models.IntegerField(max_length=2) -# -# def __unicode__(self): -# return self.applyer +class PermLog(models.Model): + datetime = models.DateTimeField(auto_now_add=True) + result = models.CharField(max_length=1000, null=True, blank=True, default='') + is_finished = models.BooleanField(default=False) diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 21a69ac09..d777ca82d 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -6,6 +6,7 @@ import uuid import re from ansible.playbook import PlayBook from ansible import callbacks, utils +from jumpserver.tasks import playbook_run, add from jumpserver.models import Setting @@ -54,49 +55,6 @@ def get_playbook(template, var): return path -def playbook_run(inventory, playbook, settings): - stats = callbacks.AggregateStats() - playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) - runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY) - # run the playbook - if settings: - playbook = PlayBook(host_list=inventory, - playbook=playbook, - forks=5, - remote_user=settings.default_user, - remote_port=settings.default_port, - private_key_file=settings.default_pri_key_path, - callbacks=playbook_cb, - runner_callbacks=runner_cb, - stats=stats, - become=True, - become_user='root') - else: - playbook = PlayBook(host_list=inventory, - playbook=playbook, - forks=5, - callbacks=playbook_cb, - runner_callbacks=runner_cb, - stats=stats, - become=True, - become_user='root') - - results = playbook.run() - results_r = {'unreachable': [], 'failures': [], 'success': []} - for hostname, result in results.items(): - if result.get('unreachable', 2): - results_r['unreachable'].append(hostname) - print "%s >>> unreachable" % hostname - elif result.get('failures', 2): - results_r['failures'].append(hostname) - print "%s >>> Failed" % hostname - else: - results_r['success'].append(hostname) - print "%s >>> Success" % hostname - - return results_r - - def perm_user_api(perm_info): """ 用户授权api,通过调用ansible API完成用户新建等,传入参数必须如下,列表中可以是对象,也可以是用户名和ip @@ -111,6 +69,8 @@ def perm_user_api(perm_info): new_assets = perm_info['new']['assets'] del_users = perm_info['del']['users'] del_assets = perm_info['del']['assets'] + + print new_users, new_assets except IndexError: raise ServerError("Error: function perm_user_api传入参数错误") @@ -125,8 +85,6 @@ def perm_user_api(perm_info): except IndexError: raise ServerError("Error: function perm_user_api传入参数错误") - print new_assets, del_assets - print new_users, del_users try: if var_type == 'str': new_ip = new_assets @@ -141,9 +99,6 @@ def perm_user_api(perm_info): except IndexError: raise ServerError("Error: function perm_user_api传入参数类型错误") - print new_ip, del_ip - print new_username, del_username - host_group = {'new': new_ip, 'del': del_ip} inventory = get_inventory(host_group) @@ -155,9 +110,22 @@ def perm_user_api(perm_info): 'the_new_users': the_new_users, 'the_del_users': the_del_users, 'the_pub_key': '/tmp/id_rsa.pub'}) + print playbook, inventory + settings = get_object(Setting, name='default') - results_r = playbook_run(inventory, playbook, settings) - return results_r + results = playbook_run(inventory, playbook, settings) + return results + + +def get_user_assets(user): + if isinstance(user, int): + user = get_object(User, id=user) + elif isinstance(user, str): + user = get_object(User, username=user) + elif isinstance(user, User): + user = user + else: + user = None def refresh_group_api(user_group=None, asset_group=None): diff --git a/jperm/urls.py b/jperm/urls.py index 705ad9307..c95ab9684 100644 --- a/jperm/urls.py +++ b/jperm/urls.py @@ -7,6 +7,7 @@ urlpatterns = patterns('jperm.views', (r'^perm_user_edit/$', perm_user_edit), (r'^group/$', perm_group_list), (r'^perm_group_edit/$', perm_group_edit), + (r'log/$', log), # (r'^dept_perm_edit/$', 'dept_perm_edit'), # (r'^perm_list/$', view_splitter, {'su': perm_list, 'adm': perm_list_adm}), # (r'^dept_perm_list/$', 'dept_perm_list'), diff --git a/jperm/views.py b/jperm/views.py index 98c1b3bb7..8a5e67e3e 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -1 +1 @@ -# # coding: utf-8 # import sys # # reload(sys) # sys.setdefaultencoding('utf8') # # from django.shortcuts import render_to_response # from django.template import RequestContext # from jperm.models import Perm, SudoPerm, CmdGroup, Apply from django.db.models import Q from jperm.models import * from jumpserver.api import * from jperm.perm_api import * @require_role('admin') def perm_user_list(request): header_title, path1, path2 = '用户授权', '授权管理', '用户授权' keyword = request.GET.get('search', '') users_list = User.objects.all() # 获取所有用户 if keyword: users_list = users_list.filter(Q(name=keyword) | Q(username=keyword)) # 搜索 users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) # 分页 return my_render('jperm/perm_user_list.html', locals(), request) @require_role('admin') def perm_user_edit(request): header_title, path1, path2 = '用户授权', '授权管理', '授权更改' user_id = request.GET.get('id', '') user = get_object(User, id=user_id) asset_all = Asset.objects.all() # 获取所有资产 asset_group_all = AssetGroup.objects.all() # 获取所有资产组 asset_permed = user.asset.all() # 获取授权的资产对象列表 asset_group_permed = user.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user: assets = [asset for asset in asset_all if asset not in asset_permed] # 获取没有授权的资产对象列表 asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] # 同理 return my_render('jperm/perm_user_edit.html', locals(), request) elif request.method == 'POST' and user: asset_id_select = request.POST.getlist('asset_select', []) # 获取选择的资产id列表 asset_group_id_select = request.POST.getlist('asset_groups_select', []) # 获取选择的资产组id列表 asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 perm_info = { 'del': {'users': [user], 'assets': asset_del}, 'new': {'users': [user], 'assets': asset_new} } try: results = perm_user_api(perm_info) # 通过API授权或回收 except ServerError, e: return HttpResponse(e) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user.asset = asset_select user.asset_group = asset_group_select user.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') @require_role('admin') def perm_group_list(request): header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' keyword = request.GET.get('search', '') user_groups_list = UserGroup.objects.all() if keyword: request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) return my_render('jperm/perm_group_list.html', locals(), request) @require_role('admin') def perm_group_edit(request): header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' user_group_id = request.GET.get('id', '') user_group = get_object(UserGroup, id=user_group_id) asset_all = Asset.objects.all() asset_group_all = AssetGroup.objects.all() asset_permed = user_group.asset.all() # 获取授权的资产对象列表 asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user_group: assets = [asset for asset in asset_all if asset not in asset_permed] asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] return my_render('jperm/perm_group_edit.html', locals(), request) elif request.method == 'POST' and user_group: asset_id_select = request.POST.getlist('asset_select', []) asset_group_id_select = request.POST.getlist('asset_groups_select', []) asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 results = perm_user_api(asset_new, asset_del, asset_group_new, asset_group_del, user_group=user_group) # 通过API授权或回收 unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user_group.asset = asset_select user_group.asset_group = asset_group_select user_group.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') \ No newline at end of file +# # coding: utf-8 # import sys # # reload(sys) # sys.setdefaultencoding('utf8') # # from django.shortcuts import render_to_response # from django.template import RequestContext # from jperm.models import Perm, SudoPerm, CmdGroup, Apply from django.db.models import Q from jumpserver.api import * from jperm.perm_api import * from jperm.models import PermLog as Log @require_role('admin') def perm_user_list(request): header_title, path1, path2 = '用户授权', '授权管理', '用户授权' keyword = request.GET.get('search', '') users_list = User.objects.all() # 获取所有用户 if keyword: users_list = users_list.filter(Q(name=keyword) | Q(username=keyword)) # 搜索 users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) # 分页 return my_render('jperm/perm_user_list.html', locals(), request) @require_role('admin') def perm_user_edit(request): header_title, path1, path2 = '用户授权', '授权管理', '授权更改' user_id = request.GET.get('id', '') user = get_object(User, id=user_id) asset_all = Asset.objects.all() # 获取所有资产 asset_group_all = AssetGroup.objects.all() # 获取所有资产组 asset_permed = user.asset.all() # 获取授权的资产对象列表 asset_group_permed = user.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user: assets = [asset for asset in asset_all if asset not in asset_permed] # 获取没有授权的资产对象列表 asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] # 同理 return my_render('jperm/perm_user_edit.html', locals(), request) elif request.method == 'POST' and user: asset_id_select = request.POST.getlist('asset_select', []) # 获取选择的资产id列表 asset_group_id_select = request.POST.getlist('asset_groups_select', []) # 获取选择的资产组id列表 asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 for asset_group in asset_group_new: asset_new.extend(asset_group.asset_set.all()) for asset_group in asset_group_del: asset_del.extend(asset_group.asset_set.all()) perm_info = { 'del': {'users': [user], 'assets': asset_del}, 'new': {'users': [user], 'assets': asset_new} } print perm_info try: results = perm_user_api(perm_info) # 通过API授权或回收 except ServerError, e: return HttpResponse(e) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user.asset = asset_select user.asset_group = asset_group_select user.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') @require_role('admin') def perm_group_list(request): header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' keyword = request.GET.get('search', '') user_groups_list = UserGroup.objects.all() if keyword: request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) return my_render('jperm/perm_group_list.html', locals(), request) @require_role('admin') def perm_group_edit(request): header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' user_group_id = request.GET.get('id', '') user_group = get_object(UserGroup, id=user_group_id) asset_all = Asset.objects.all() asset_group_all = AssetGroup.objects.all() asset_permed = user_group.asset.all() # 获取授权的资产对象列表 asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user_group: assets = [asset for asset in asset_all if asset not in asset_permed] asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] return my_render('jperm/perm_group_edit.html', locals(), request) elif request.method == 'POST' and user_group: asset_id_select = request.POST.getlist('asset_select', []) asset_group_id_select = request.POST.getlist('asset_groups_select', []) asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 results = perm_user_api(asset_new, asset_del, asset_group_new, asset_group_del, user_group=user_group) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user_group.asset = asset_select user_group.asset_group = asset_group_select user_group.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') def log(request): header_title, path1, path2 = '授权记录', '授权管理', '授权记录' log_all = Log.objects.all().order_by('-datetime') log_all, p, logs, page_range, current_page, show_first, show_end = pages(log_all, request) return my_render('jperm/perm_log.html', locals(), request) \ No newline at end of file diff --git a/jumpserver/api.py b/jumpserver/api.py index fcd21c913..faabb068c 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -18,8 +18,8 @@ from django.http import HttpResponse, Http404 from django.template import RequestContext from juser.models import User, UserGroup from jasset.models import Asset, AssetGroup -from jlog.models import Log from jasset.models import AssetAlias +from jlog.models import Log from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.http import HttpResponseRedirect from django.shortcuts import render_to_response diff --git a/jumpserver/settings.py b/jumpserver/settings.py index aeeaec053..0a5a69bee 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -11,7 +11,9 @@ https://docs.djangoproject.com/en/1.7/ref/settings/ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os import ConfigParser +import djcelery +djcelery.setup_loader() config = ConfigParser.ConfigParser() BASE_DIR = os.path.dirname(os.path.dirname(__file__)) @@ -43,7 +45,7 @@ TEMPLATE_DEBUG = True ALLOWED_HOSTS = ['0.0.0.0/8'] - +BROKER_URL = 'django://' # Application definition INSTALLED_APPS = ( @@ -54,6 +56,8 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', + 'djcelery', + 'kombu.transport.django', 'jumpserver', 'juser', 'jasset', diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 160970ad2..2bd1df20b 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -12,14 +12,6 @@ from jasset.models import AssetAlias register = template.Library() -# @register.filter(name='stamp2str') -# def stamp2str(value): -# try: -# return time.strftime('%Y/%m/%d %H:%M:%S', time.localtime(value)) -# except AttributeError: -# return '0000/00/00 00:00:00' - - @register.filter(name='int2str') def int2str(value): """ @@ -42,18 +34,6 @@ def get_role(user_id): return u"普通用户" -# @register.filter(name='groups_str') -# def groups_str(user_id): -# groups = [] -# user = User.objects.get(id=user_id) -# for group in user.group.all(): -# groups.append(group.name) -# if len(groups) < 3: -# return ' '.join(groups) -# else: -# return "%s ..." % ' '.join(groups[0:2]) -# - @register.filter(name='groups2str') def groups2str(group_list): """ @@ -87,72 +67,6 @@ def user_asset_group_count(user): """ return len(user.asset_group.all()) -# -# @register.filter(name='user_group_asset_count') -# def user_group_asset_count(user_group): -# """ -# 返回用户组权限主机的数量 -# """ -# assets_id = user_group.assets.split(',') -# asset_groups = user_group.asset_groups.split(',') -# -# for asset_group_id in asset_groups: -# asset_group = get_object(AssetGroup, id=asset_group_id) -# if asset_group: -# assets_id.extend(asset.id for asset in asset_group.asset_set.all()) -# -# assets_id = set(map(str, assets_id)) -# return len(assets_id) -# -# -# @register.filter(name='user_group_asset_count') -# def user_group_asset_group_count(user_group): -# """ -# 返回用户组权限主机组的数量 -# """ -# return len(user_group.asset_groups.split(',')) -# -# @register.filter(name='group_str2_all') -# def group_str2_all(group_list): -# group_lis = [] -# for i in group_list: -# if str(i) != 'ALL': -# group_lis.append(i) -# if len(group_lis) < 3: -# return ' '.join([group.name for group in group_lis]) -# else: -# return '%s ...' % ' '.join([group.name for group in group_lis[0:2]]) -# -# -# @register.filter(name='group_dept_all') -# def group_dept_all(group_list): -# group_lis = [] -# for i in group_list: -# if str(i) != 'ALL': -# group_lis.append(i) -# return ' '.join([group.name for group in group_lis]) -# -# -# @register.filter(name='group_manage_str') -# def group_manage_str(username): -# user = User.objects.get(username=username) -# group = user.user_group.filter(type='M') -# if group: -# return group[0].name -# else: -# return '' -# -# -# @register.filter(name='get_item') -# def get_item(dictionary, key): -# return dictionary.get(key) -# -# -# @register.filter(name='get_login_type') -# def get_login_type(login): -# login_types = {'L': 'LDAP', 'M': 'MAP'} -# return login_types[login] - @register.filter(name='bool2str') def bool2str(value): @@ -162,16 +76,6 @@ def bool2str(value): return u'否' -# # @register.filter(name='user_readonly') -# # def user_readonly(user_id): -# # user = User.objects.filter(id=user_id) -# # if user: -# # user = user[0] -# # if user.role == 'CU': -# # return False -# # return True - - @register.filter(name='members_count') def members_count(group_id): """统计用户组下成员数量""" @@ -181,148 +85,6 @@ def members_count(group_id): else: return 0 -# -# @register.filter(name='group_user_count') -# def group_user_count(group_id): -# group = UserGroup.objects.get(id=group_id) -# return group.user_set.count() -# -# -# @register.filter(name='dept_user_num') -# def dept_user_num(dept_id): -# dept = DEPT.objects.filter(id=dept_id) -# if dept: -# dept = dept[0] -# return dept.user_set.count() -# else: -# return 0 -# -# -# @register.filter(name='dept_group_num') -# def dept_group_num(dept_id): -# dept = DEPT.objects.filter(id=dept_id) -# if dept: -# dept = dept[0] -# return dept.usergroup_set.all().count() -# else: -# return 0 -# -# -# @register.filter(name='perm_count') -# def perm_count(group_id): -# group = UserGroup.objects.get(id=group_id) -# return group.perm_set.count() -# -# -# @register.filter(name='dept_asset_num') -# def dept_asset_num(dept_id): -# dept = DEPT.objects.filter(id=dept_id) -# if dept: -# dept = dept[0] -# return dept.asset_set.all().count() -# return 0 -# -# -# @register.filter(name='ugrp_perm_agrp_count') -# def ugrp_perm_agrp_count(user_group_id): -# user_group = UserGroup.objects.filter(id=user_group_id) -# if user_group: -# user_group = user_group[0] -# return user_group.perm_set.all().count() -# return 0 -# -# -# @register.filter(name='ugrp_sudo_agrp_count') -# def ugrp_sudo_agrp_count(user_group_id): -# user_group = UserGroup.objects.filter(id=user_group_id) -# asset_groups = [] -# if user_group: -# user_group = user_group[0] -# for perm in user_group.sudoperm_set.all(): -# asset_groups.extend(perm.asset_group.all()) -# return len(set(asset_groups)) -# return 0 -# -# -# @register.filter(name='ugrp_perm_asset_count') -# def ugrp_perm_asset_count(user_group_id): -# user_group = UserGroup.objects.filter(id=user_group_id) -# assets = [] -# if user_group: -# user_group = user_group[0] -# asset_groups = [perm.asset_group for perm in user_group.perm_set.all()] -# for asset_group in asset_groups: -# assets.extend(asset_group.asset_set.all()) -# return len(set(assets)) -# -# -# @register.filter(name='ugrp_sudo_asset_count') -# def ugrp_sudo_asset_count(user_group_id): -# user_group = UserGroup.objects.filter(id=user_group_id) -# asset_groups = [] -# assets = [] -# if user_group: -# user_group = user_group[0] -# for perm in user_group.sudoperm_set.all(): -# asset_groups.extend(perm.asset_group.all()) -# -# for asset_group in asset_groups: -# assets.extend(asset_group.asset_set.all()) -# return len(set(assets)) -# -# -# @register.filter(name='get_user_alias') -# def get_user_alias(post, user_id): -# user = User.objects.get(id=user_id) -# host = Asset.objects.get(id=post.id) -# alias = AssetAlias.objects.filter(user=user, host=host) -# if alias: -# return alias[0].alias -# else: -# return '' -# -# -# @register.filter(name='group_type_to_str') -# def group_type_to_str(type_name): -# group_types = { -# 'P': '用户', -# 'M': '部门', -# 'A': '用户组', -# } -# return group_types.get(type_name) -# -# -# @register.filter(name='ast_to_list') -# def ast_to_list(lis): -# ast_lis = ast.literal_eval(lis) -# if len(ast_lis) <= 2: -# return ','.join([i for i in ast_lis]) -# else: -# restr = ','.join([i for i in ast_lis[0:2]]) + '...' -# return restr -# -# -# @register.filter(name='get_group_count') -# def get_group_count(post, dept): -# count = post.asset_set.filter(dept=dept).count() -# return count -# -# -# @register.filter(name='get_idc_count') -# def get_idc_count(post, dept): -# count = post.asset_set.filter(dept=dept).count() -# return count -# -# -# @register.filter(name='ast_to_list_1') -# def ast_to_list_1(lis): -# return ast.literal_eval(lis) -# -# -# @register.filter(name='string_length') -# def string_length(string, length): -# return '%s ...' % string[0:length] - @register.filter(name='to_name') def to_name(user_id): @@ -336,17 +98,6 @@ def to_name(user_id): return '非法用户' -# @register.filter(name='to_dept_name') -# def to_dept_name(user_id): -# try: -# user = User.objects.filter(id=int(user_id)) -# if user: -# user = user[0] -# return user.dept.name -# except: -# return '非法部门' - - @register.filter(name='to_role_name') def to_role_name(role_id): """role_id 转变为角色名称""" @@ -359,99 +110,16 @@ def to_avatar(role_id='0'): """不同角色不同头像""" role_dict = {'0': 'user', '1': 'admin', '2': 'root'} return role_dict.get(str(role_id), 'user') -# -# -# @register.filter(name='get_user_asset_group') -# def get_user_asset_group(user): -# return user.get_asset_group() -# -# -# @register.filter(name='group_asset_list') -# def group_asset_list(group): -# return group.asset_set.all() -# -# -# @register.filter(name='group_asset_list_count') -# def group_asset_list_count(group): -# return group.asset_set.all().count() -# -# -# @register.filter(name='time_delta') -# def time_delta(time_before): -# delta = datetime.datetime.now() - time_before -# days = delta.days -# if days: -# return "%s 天前" % days -# else: -# hours = delta.seconds/3600 -# if hours: -# return "%s 小时前" % hours -# else: -# mins = delta.seconds/60 -# if mins: -# return '%s 分钟前' % mins -# else: -# return '%s 秒前' % delta.seconds -# -# -# @register.filter(name='sudo_cmd_list') -# def sudo_cmd_list(cmd_group_id): -# cmd_group = CmdGroup.objects.filter(id=cmd_group_id) -# if cmd_group: -# cmd_group = cmd_group[0] -# return cmd_group.cmd.split(',') -# -# -# @register.filter(name='sudo_cmd_count') -# def sudo_cmd_count(user_group_id): -# user_group = UserGroup.objects.filter(id=user_group_id) -# cmds = [] -# if user_group: -# user_group = user_group[0] -# cmd_groups = [] -# -# for perm in user_group.sudoperm_set.all(): -# cmd_groups.extend(perm.cmd_group.all()) -# -# for cmd_group in cmd_groups: -# cmds.extend(cmd_group.cmd.split(',')) -# return len(set(cmds)) -# -# else: -# return 0 -# -# -# @register.filter(name='sudo_cmd_count') -# def sudo_cmd_count(user_group_id): -# user_group = UserGroup.objects.filter(id=user_group_id) -# cmds = [] -# if user_group: -# user_group = user_group[0] -# cmd_groups = [] -# for perm in user_group.sudoperm_set.all(): -# cmd_groups.extend(perm.cmd_group.all()) -# -# for cmd_group in cmd_groups: -# cmds.extend(cmd_group.cmd.split(',')) -# return len(set(cmds)) -# else: -# return 0 -# -# -# @register.filter(name='sudo_cmd_ids') -# def sudo_cmd_ids(user_group_id): -# user_group = UserGroup.objects.filter(id=user_group_id) -# if user_group: -# user_group = user_group[0] -# cmd_groups = [] -# for perm in user_group.sudoperm_set.all(): -# cmd_groups.extend(perm.cmd_group.all()) -# cmd_ids = [str(cmd_group.id) for cmd_group in cmd_groups] -# return ','.join(cmd_ids) -# else: -# return '0' -# -# -# @register.filter(name='cmd_group_split') -# def cmd_group_split(cmd_group): -# return cmd_group.cmd.split(',') + + +@register.filter(name='result2bool') +def result2bool(result=''): + """将结果定向为结果""" + result = eval(result) + unreachable = result.get('unreachable', []) + failures = result.get('failures', []) + + if unreachable or failures: + return '失败' + else: + return '成功' diff --git a/templates/jperm/dept_perm_edit.html b/templates/jperm/dept_perm_edit.html deleted file mode 100644 index 7d9c8247f..000000000 --- a/templates/jperm/dept_perm_edit.html +++ /dev/null @@ -1,179 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - - -
-
-
-
-
-
部门授权编辑
- -
- - - - - -
-
- {% if error %} -
{{ error }}
- {% endif %} - {% if msg %} -
{{ msg }}
- {% endif %} -
-
- -
- - -
-
- -
- -
- -
- -
-
-
-
- -
-
- - -
- -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
-
-
- -
-
-
- - -
-
-
-
- -
-
-
-
-
- - - - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/dept_perm_list.html b/templates/jperm/dept_perm_list.html deleted file mode 100644 index 92d467d89..000000000 --- a/templates/jperm/dept_perm_list.html +++ /dev/null @@ -1,104 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
查看部门
- -
- -
-
- 添加部门 - -
- - - - - - - - - - - - - - {% for dept in contacts.object_list %} - - - - - - - - {% endfor %} - -
部门名称部门成员数目授权主机数目备注操作
{{ dept.name }} {{ dept.id | dept_user_num }} {{ dept.id | dept_asset_num }} {{ dept.comment }} -{# 主机#} - 授权编辑 -
-
-
-
- Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries -
-
- {% include 'paginator.html' %} -
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/perm_apply.html b/templates/jperm/perm_apply.html deleted file mode 100644 index 167a9574e..000000000 --- a/templates/jperm/perm_apply.html +++ /dev/null @@ -1,187 +0,0 @@ -{% extends 'base.html' %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
填写要申请主机的基本信息
-
- - - - - - - - - - -
-
- - - - - -
- {% if emg %} -
{{ emg }}
- {% endif %} - {% if smg %} -
{{ smg }}
- {% endif %} -
- {% csrf_token %} -
-
-
- -
-
-
-
- -
-
-
- - {% for da in dept_da %} - - {% endfor %} -
-
- -
-
- -
- -
-
- -
-
- -
- -
-
-
-
- -
-
- - -
- -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
-
- - -
-
-
-
- -
-
-
- -
-
-
-
-
-
-
-
- - -{% endblock content %} \ No newline at end of file diff --git a/templates/jperm/perm_apply_exec.html b/templates/jperm/perm_apply_exec.html deleted file mode 100644 index 6e9580210..000000000 --- a/templates/jperm/perm_apply_exec.html +++ /dev/null @@ -1,31 +0,0 @@ -{% extends 'base.html' %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-

-
-
-
-
-
- - - -{% endblock content %} \ No newline at end of file diff --git a/templates/jperm/perm_apply_info.html b/templates/jperm/perm_apply_info.html deleted file mode 100644 index 716b76f15..000000000 --- a/templates/jperm/perm_apply_info.html +++ /dev/null @@ -1,55 +0,0 @@ -{% load mytags %} - - - - - - - - - - - - - - -
-

{{ post.applyer }}权限申请详情

-
-
-
-
申请人
-
所属部门
-
申请主机组
-
申请主机
- {% for i in post.asset|ast_to_list_1 %} -
Null
- {% endfor %} -
批准人
-
申请时间
-
批准时间
-
备注
-
-
-
-
-
-
{{ post.applyer }}
-
{{ post.dept }}
-
{% for i in post.bisgroup|ast_to_list_1 %} {{ i }} {% endfor %}
- {% for i in post.asset|ast_to_list_1 %} -
{{ i }}
- {% endfor %} -
Null
-
{{ post.approver }}1
-
{{ post.date_add|date:"Y-m-d H:i:s"}}
-
{{ post.date_end|date:"Y-m-d H:i:s" }}1
-
{{ post.comment }}
-
-
-
- - \ No newline at end of file diff --git a/templates/jperm/perm_apply_search.html b/templates/jperm/perm_apply_search.html deleted file mode 100644 index 7f62da3aa..000000000 --- a/templates/jperm/perm_apply_search.html +++ /dev/null @@ -1,40 +0,0 @@ -{% load mytags %} -
- - - - - - - - - - - - - - - - {% for post in contacts.object_list %} - - - - - - - - - - - {% endfor %} - -
申请人 所属部门 申请主机组 申请主机 批准人 申请时间 备注 详情
{{ post.applyer }} {{ post.dept }} {{ post.bisgroup|ast_to_list }} {{ post.asset|ast_to_list }} {{ post.approver }} {{ post.date_add|date:"Y-m-d H:i:s"}} {{ post.comment }} - 详情 -
-
-
-
- {% include 'paginator.html' %} -
-
-
\ No newline at end of file diff --git a/templates/jperm/perm_asset_detail.html b/templates/jperm/perm_asset_detail.html deleted file mode 100644 index 8be1ece6b..000000000 --- a/templates/jperm/perm_asset_detail.html +++ /dev/null @@ -1,61 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} - -{% block content %} - {% include 'nav_cat_bar.html' %} -
-
- -
-
-
-
授权主机详情
- -
-
- - - - - - - - - - {% for asset in assets_list %} - - - - - - {% endfor %} - -
IPIDC主机组
{{ asset.ip }}{{ asset.idc.name }} - {% for group in asset.bis_group.all|filter_private %} - {{ group }} - {% endfor %} -
-
-
-
- -
-
- -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/perm_detail.html b/templates/jperm/perm_detail.html deleted file mode 100644 index b772e4613..000000000 --- a/templates/jperm/perm_detail.html +++ /dev/null @@ -1,118 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} - -{% block content %} - {% include 'nav_cat_bar.html' %} -
-
-
-
-
-
授权主机/组
- -
-
-

用户

- 组下用户. -
-
-
-
-
- - {{ user_group.name }} -
- 共: {{ group_user_num }} 用户 -
-
-

{{ user_group.comment }}

-

- {% for user in users %} - {{ user.name }}
- {% endfor %} -

-

-
-
-
- {% if not user|get_user_asset_group %} - (无) - {% endif %} -
-
-
- -
-
-
-
授权主机/组
- -
-
-

授权主机/组

- 这里包含了用户所有的主机组和组下的主机. -
-
- {% for group in asset_groups %} -
-
-
- - {{ group.name }} -
- 共: {{ group | group_asset_list_count }}台 -
-
-

{{ group.comment }}

-

- {% for asset in group|group_asset_list %} - {{ asset.ip }}
- {% endfor %} -

-

-
-
-
- {% endfor %} - {% if not user|get_user_asset_group %} - (暂无) - {% endif %} -
-
-
-
-
- -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/perm_edit.html b/templates/jperm/perm_edit.html deleted file mode 100644 index 33cd1e4eb..000000000 --- a/templates/jperm/perm_edit.html +++ /dev/null @@ -1,130 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
主机授权修改
- -
- -
-
- {% if error %} -
{{ error }}
- {% endif %} - {% if msg %} -
{{ msg }}
- {% endif %} -
-
- -
- - -
-
- -
- -
- -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
-
-
- -
-
-
- - -
-
-
-
- -
-
-
-
-
- - - - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/perm_edit_bak.html b/templates/jperm/perm_edit_bak.html deleted file mode 100644 index cba2d4155..000000000 --- a/templates/jperm/perm_edit_bak.html +++ /dev/null @@ -1,138 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} - -{% block content %} - - - {% include 'nav_cat_bar.html' %} -
-
-
-
- -
-
授权编辑表单 Edit perm of Group
- -
- -
-
-
-
- - -
-
-
-
- -
-
-
-
- - -
-
- -
-
- -
-

未授权主机组

-
- - -
-
- -
-
- - -
-
- -

授权主机

-
- -
-
-
-
-
-
- - -
-
-
-
- -
-
-
-
-
- -{# #} - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/perm_list_ajax.html b/templates/jperm/perm_list_ajax.html deleted file mode 100644 index 64a15a0f2..000000000 --- a/templates/jperm/perm_list_ajax.html +++ /dev/null @@ -1,132 +0,0 @@ -{% load mytags %} -{% ifequal tab 'tab1' %} - - - - - - - - - - - - {% for group in contacts.object_list %} - - - - - - - - {% endfor %} - -
组名 - 类型 - 成员数量授权数量操作
{{ group.name }} {{ group.type|group_type_to_str }} {{ group.id|member_count }} {{ group.id|perm_count }} - 详情 - 编辑 - 删除 -
-
-
-
- Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries -
-
-
-
-
    - {% if contacts.has_previous %} - - {% else %} - - {% endif %} - {% for page in p.page_range %} - {% ifequal offset1 page %} -
  • {{ page }}
  • - {% else %} -
  • {{ page }}
  • - {% endifequal %} - {% endfor %} - {% if contacts.has_next %} - - {% else %} - - {% endif %} -
-
-
-
-{% else %} - - - - - - - - - - - - {% for user in contacts2.object_list %} - - - - - - - - {% endfor %} - -
用户角色属组主机数量操作
{{ user.name }} {{ user.id | get_role }} {{ user.username | groups_str }} {{ user.id | perm_asset_count }} - 详情 -
-
-
-
- Showing {{ contacts2.start_index }} to {{ contacts2.end_index }} of {{ p2.count }} entries -
-
-
-
-
    - {% if contacts2.has_previous %} - - {% else %} - - {% endif %} - {% for page in p2.page_range %} - {% ifequal offset1 page %} -
  • {{ page }}
  • - {% else %} -
  • {{ page }}
  • - {% endifequal %} - {% endfor %} - {% if contacts2.has_next %} - - {% else %} - - {% endif %} -
-
-
-
-{% endifequal %} \ No newline at end of file diff --git a/templates/jperm/perm_log.html b/templates/jperm/perm_log.html index 3b71002e6..b24a868a1 100644 --- a/templates/jperm/perm_log.html +++ b/templates/jperm/perm_log.html @@ -1,12 +1,14 @@ {% extends 'base.html' %} +{% load mytags %} {% block content %} {% include 'nav_cat_bar.html' %} +
-
+
-
-
用户权限申请详细信息列表
+
+
查看小组
@@ -14,12 +16,6 @@ - @@ -27,63 +23,47 @@
-
- -
-
-
- - - - - - - - - - - - - - - {% for post in contacts.object_list %} - - - - - - - - - - - - - {% endfor %} - -
申请人 所属部门 申请主机组 申请主机 申请时间 批准时间 备注
{{ post.applyer }} {{ post.dept }} {{ post.bisgroup }} {{ post.asset }} {{ post.date_add|date:"Y-m-d H:i:s"}} {{ post.date_end|date:"Y-m-d H:i:s" }} {{ post.comment }}
-
-
+
+
+ +
+ + + + + + + + + + + {% for log in logs %} + + + + + + {% endfor %} + +
日期结果完成
{{ log.datetime | date:"Y-n-d G:i:s" }} + {{ log.result | result2bool | safe }} + {{ log.is_finished | yesno:"是,否,为止" }}
+
+
+
+ Showing {{ users.start_index }} to {{ users.end_index }} of {{ p.count }} entries +
+
+ {% include 'paginator.html' %} +
diff --git a/templates/jperm/perm_log_offline.html b/templates/jperm/perm_log_offline.html deleted file mode 100644 index 8b1250277..000000000 --- a/templates/jperm/perm_log_offline.html +++ /dev/null @@ -1,127 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} -
-
-
-
-
-
用户权限申请详细信息列表
- -
- -
-
- -
-
-
- - - - - - - - - - - - - - - - {% for post in contacts.object_list %} - - - - - - - - - - - {% endfor %} - -
申请人 所属部门 申请主机组 申请主机 批准人 申请时间 备注 详情
{{ post.applyer }} {{ post.dept }} {{ post.bisgroup|ast_to_list }} {{ post.asset|ast_to_list }} {{ post.approver }} {{ post.date_add|date:"Y-m-d H:i:s"}} {{ post.comment }} - 详情 -
-
-
-
- {% include 'paginator.html' %} -
-
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/perm_log_online.html b/templates/jperm/perm_log_online.html deleted file mode 100644 index cda6b07c3..000000000 --- a/templates/jperm/perm_log_online.html +++ /dev/null @@ -1,128 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} -
-
-
-
-
-
用户权限申请详细信息列表
- -
- -
-
- -
-
-
- - - - - - - - - - - - - - - {% for post in contacts.object_list %} - - - - - - - - - - {% endfor %} - -
申请人 所属部门 申请主机组 申请主机 申请时间 备注 操作
{{ post.applyer }} {{ post.dept }} {{ post.bisgroup|ast_to_list }} {{ post.asset|ast_to_list }} {{ post.date_add|date:"Y-m-d H:i:s"}} {{ post.comment }} - 详情 - {% ifnotequal session_role_id 0 %} - 确认 - 删除 - {% endifnotequal %} -
-
-
-
- {% include 'paginator.html' %} -
-
-
-
-
-
-
- - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/perm_user_detail.html b/templates/jperm/perm_user_detail.html deleted file mode 100644 index 3a17821a4..000000000 --- a/templates/jperm/perm_user_detail.html +++ /dev/null @@ -1,240 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
用户授权详情
- -
- -
-
-
-
- -
-
- -
- -
-
- - - - - - - - - - - - {% for perm in contacts.object_list %} - - - - - - - - {% endfor %} - -
名称用户组主机组备注操作
{{ perm.name }} - {% for user_group in perm.user_group.all %} - {{ user_group.name }} - {% endfor %} - - {% for asset_group in perm.asset_group.all %} - {{ asset_group.name }} - {% endfor %} - {{ perm.comment }} - 详情 - 编辑 - 删除 -
-
-
-
- Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries -
-
-
-
-
    - {% if contacts.has_previous %} - - {% else %} - - {% endif %} - {% for page in page_range %} - {% ifequal current_page page %} -
  • {{ page }}
  • - {% else %} -
  • {{ page }}
  • - {% endifequal %} - {% endfor %} - {% if contacts.has_next %} - - {% else %} - - {% endif %} -
-
-
-
-
- -
-{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# {% for user in contacts2.object_list %}#} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# {% endfor %}#} -{# #} -{#
用户角色属组主机数量操作
{{ user.name }} {{ user.id | get_role }} {{ user.username | groups_str }} {{ user.id | perm_asset_count }} #} -{# 详情#} -{#
#} -{#
#} -{#
#} -{#
#} -{# Showing {{ contacts2.start_index }} to {{ contacts2.end_index }} of {{ p2.count }} entries#} -{#
#} -{#
#} -{#
#} -{#
#} -{#
    #} -{# {% if contacts2.has_previous %}#} -{# #} -{# {% else %}#} -{# #} -{# {% endif %}#} -{# {% for page in page_range2 %}#} -{# {% ifequal current_page page %}#} -{#
  • {{ page }}
  • #} -{# {% else %}#} -{#
  • {{ page }}
  • #} -{# {% endifequal %}#} -{# {% endfor %}#} -{# {% if contacts2.has_next %}#} -{# #} -{# {% else %}#} -{# #} -{# {% endif %}#} -{#
#} -{#
#} -{#
#} -{#
#} - -
-
- -
- -
- - - -
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/sudo_add.html b/templates/jperm/sudo_add.html deleted file mode 100644 index 0087da321..000000000 --- a/templates/jperm/sudo_add.html +++ /dev/null @@ -1,226 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
Sudo授权添加
- -
- -
- - -
- -
-
-
- {% if error %} -
{{ error }}
- {% endif %} - {% if msg %} -
{{ msg }}
- {% endif %} -
-
- -
- - 取个名字方便辨识,只支持英文 -
-
-
- -
- -
- - - 允许以哪个用户允许sudo,逗号分隔,默认root - -
-
-
- -
- -
-
- -
-
- - -
-
- - -
-
- -
-
- -
-
-
- -
- -
- -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
-
- -
- -
- -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
-
- - -
-
- -
- -
-
-
-
- -
-
-
- - -
-
-
-
-
-
-
- -
-
-
-
-
- - - - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/sudo_cmd_add.html b/templates/jperm/sudo_cmd_add.html deleted file mode 100644 index ff46b049c..000000000 --- a/templates/jperm/sudo_cmd_add.html +++ /dev/null @@ -1,148 +0,0 @@ -{% extends 'base.html' %} - -{% block content %} - {% include 'nav_cat_bar.html' %} -
-
-
-
-
-
填写基本信息
- -
-
-
- - -
- -
-
-
- {% if error %} -
{{ error }}
- {% endif %} - {% if msg %} -
{{ msg }}
- {% endif %} -
- -
- - -
-
- {% ifequal session_role_id 2 %} -
-
- -
- -
-
- {% endifequal %} -
-
- -
- - - 输入命令一行一个,请写绝对路径如: /bin/su,所有是ALL,排除su是 !/bin/su - -
-
-
-
- -
- -
-
- -
-
-
- - -
-
- -
-
-
-
-
-
-
-
-
-
- - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/sudo_cmd_detail.html b/templates/jperm/sudo_cmd_detail.html deleted file mode 100644 index 26a7b8e72..000000000 --- a/templates/jperm/sudo_cmd_detail.html +++ /dev/null @@ -1,48 +0,0 @@ -{% load mytags %} - - - {% include 'link_css.html' %} - - - - - -
-
- {% if cmd_group_name %} -

{{ cmd_group.name }} 命令详情

- {% endif %} -
- - - {% if cmd_group_name %} - - - - - - - - - - - - - - {% endif %} - - - - -
ID名称部门
{{ cmd_group.id }}{{ cmd_group.name }}{{ cmd_group.dept.name }}
命令: - {{ cmds_str }} -
-
-
- - \ No newline at end of file diff --git a/templates/jperm/sudo_cmd_list.html b/templates/jperm/sudo_cmd_list.html deleted file mode 100644 index f7c2d9652..000000000 --- a/templates/jperm/sudo_cmd_list.html +++ /dev/null @@ -1,140 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
查看命令分组
- -
- -
-
- - -
-
-
- - - - - - - - - - - - - {% for group in contacts.object_list %} - - - - - - - - {% endfor %} - -
组名命令部门备注操作
{{ group.name }} {{ group.cmd | string_length:50 }} {{ group.dept.name }} {{ group.comment }} - 详情 - 编辑 - 删除 -
-
-
-
-
-
-
-
- Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries -
-
-
-
-
    - {% if contacts.has_previous %} - - {% else %} - - {% endif %} - {% for page in p.page_range %} - {% ifequal offset1 page %} -
  • {{ page }}
  • - {% else %} -
  • {{ page }}
  • - {% endifequal %} - {% endfor %} - {% if contacts.has_next %} - - {% else %} - - {% endif %} -
-
-
-
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/sudo_detail.html b/templates/jperm/sudo_detail.html deleted file mode 100644 index c732a4b37..000000000 --- a/templates/jperm/sudo_detail.html +++ /dev/null @@ -1,170 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} - -{% block content %} - {% include 'nav_cat_bar.html' %} -
-
-
-
-
-
授权主机/组
- -
-
-

用户

- 组下用户. -
-
-
-
-
- - {{ user_group.name }} -
- 共: {{ group_user_num }} 用户 -
-
-

{{ user_group.comment }}

-

- {% for user in users %} - {{ user.name }}
- {% endfor %} -

-

-
-
-
- {% if not users %} - (暂无) - {% endif %} -
-
-
- -
-
-
-
授权主机/组
- -
-
-

授权主机/组

- 这里包含了sudo授权所有的主机组和组下的主机. -
-
- {% for group in asset_groups %} -
-
-
- - {{ group.name }} -
- 共: {{ group | group_asset_list_count }}台 -
-
-

{{ group.comment }}

-

- {% for asset in group|group_asset_list %} - {{ asset.ip }}
- {% endfor %} -

-

-
-
-
- {% endfor %} - {% if not asset_groups %} - (暂无) - {% endif %} -
-
-
- -
-
-
-
授权命令/组
- -
-
- {% for cmd_group in cmd_groups %} -
-
-
- - {{ cmd_group.name }} -
- 共: {{ cmd_group.id|sudo_cmd_count }} 个 -
-
-

{{ group.comment }}

-

- {% for cmd in cmd_group|cmd_group_split %} - {{ cmd }}
- {% endfor %} -

-

-
-
-
- {% endfor %} - {% if not cmd_groups %} - (暂无) - {% endif %} -
-
-
- -
-
- -{% endblock %} \ No newline at end of file diff --git a/templates/jperm/sudo_edit.html b/templates/jperm/sudo_edit.html deleted file mode 100644 index e1c7977ce..000000000 --- a/templates/jperm/sudo_edit.html +++ /dev/null @@ -1,155 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
Sudo授权编辑
- -
- -
-
- - -
-
- {% if error %} -
{{ error }}
- {% endif %} - {% if msg %} -
{{ msg }}
- {% endif %} -
-
- -
- - - - 允许以哪个用户进行sudo,逗号分隔,如: root或者 ALL 等 - -
-
- -
- -
- -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
-
- -
- -
- -
-
- -
-
- -
-
- - -
-
- -
-
- -
-
-
- - -
-
- -
- -
-
-
-
- -
-
-
- - -
-
-
-
-
-
-
-
-
-
-
-{% endblock %} - diff --git a/templates/jperm/sudo_list.html b/templates/jperm/sudo_list.html deleted file mode 100644 index ab9116e7a..000000000 --- a/templates/jperm/sudo_list.html +++ /dev/null @@ -1,129 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} -{% block content %} -{% include 'nav_cat_bar.html' %} - -
-
-
-
-
-
Sudo授权列表
- -
- -
- -
-
- -
- -
-
-
- - - - - - - - - - - - - - - {% for group in contacts.object_list %} - - - - - - - - - - - {% endfor %} - -
组名所属部门成员数目授权主机组数目授权主机数目sudo命令备注操作
{{ group.name }} {{ group.dept.name }} {{ group.id | member_count }} {{ group.id | ugrp_sudo_agrp_count }} {{ group.id | ugrp_sudo_asset_count }} {{ group.id | sudo_cmd_count }} {{ group.comment }} - 详情 - sudo授权 -
-
-
-
- Showing {{ contacts.start_index }} to {{ contacts.end_index }} of {{ p.count }} entries -
-
- {% include 'paginator.html' %} -
-
-
-
-
-
-
-
-
-
- - - -{% endblock %} \ No newline at end of file diff --git a/templates/nav.html b/templates/nav.html index d0a5e37b9..f0b6bd2db 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -39,6 +39,7 @@ 命令授权
  • 权限审批
  • +
  • 授权记录
  • From fe1f825fdf91d46e83a246801321c5be4b5dc0c4 Mon Sep 17 00:00:00 2001 From: "ibuler@qq.com" Date: Mon, 19 Oct 2015 23:40:16 +0800 Subject: [PATCH 045/385] perm edit --- jasset/views.py | 303 ---------------------------------------------- jperm/perm_api.py | 165 ++++++++++++++++++++++--- 2 files changed, 149 insertions(+), 319 deletions(-) diff --git a/jasset/views.py b/jasset/views.py index 4a33f647c..8e639c3b3 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -118,119 +118,6 @@ def asset_add(request): msg = u'主机 %s 添加成功' % ip return my_render('jasset/asset_add.html', locals(), request) -# -# -# @require_admin -# def host_add_batch(request): -# """ 批量添加主机 """ -# header_title, path1, path2 = u'批量添加主机', u'资产管理', u'批量添加主机' -# login_types = {'LDAP': 'L', 'MAP': 'M'} -# active_types = {'激活': 1, '禁用': 0} -# dept_id = get_user_dept(request) -# if request.method == 'POST': -# multi_hosts = request.POST.get('j_multi').split('\n') -# for host in multi_hosts: -# if host == '': -# break -# j_ip, j_port, j_type, j_idc, j_groups, j_depts, j_active, j_comment = host.split() -# j_active = active_types[str(j_active)] -# j_group = ast.literal_eval(j_groups) -# j_dept = ast.literal_eval(j_depts) -# -# if j_type not in ['LDAP', 'MAP']: -# return httperror(request, u'没有%s这种登录方式!' %j_type) -# -# j_type = login_types[j_type] -# idc = IDC.objects.filter(name=j_idc) -# if idc: -# j_idc = idc[0].id -# else: -# return httperror(request, '添加失败, 没有%s这个IDC' % j_idc) -# -# group_ids, dept_ids = [], [] -# for group_name in j_group: -# group = BisGroup.objects.filter(name=group_name) -# if group: -# group_id = group[0].id -# else: -# return httperror(request, '添加失败, 没有%s这个主机组' % group_name) -# group_ids.append(group_id) -# -# for dept_name in j_dept: -# dept = DEPT.objects.filter(name=dept_name) -# if dept: -# dept_id = dept[0].id -# else: -# return httperror(request, '添加失败, 没有%s这个部门' % dept_name) -# dept_ids.append(dept_id) -# -# if is_group_admin(request) and not validate(request, asset_group=group_ids, edept=dept_ids): -# return httperror(request, '添加失败, 没有%s这个主机组' % group_name) -# -# if Asset.objects.filter(ip=str(j_ip)): -# return httperror(request, '添加失败, 改IP%s已存在' % j_ip) -# -# host_info = [j_ip, j_port, j_idc, j_type, group_ids, dept_ids, j_active, j_comment] -# db_host_insert(host_info) -# -# smg = u'批量添加添加成功' -# return my_render('jasset/host_add_multi.html', locals(), request) -# -# return my_render('jasset/host_add_multi.html', locals(), request) -# -# -# @require_admin -# def host_edit_batch(request): -# """ 批量修改主机 """ -# if request.method == 'POST': -# len_table = request.POST.get('len_table') -# for i in range(int(len_table)): -# j_id = "editable[" + str(i) + "][j_id]" -# j_ip = "editable[" + str(i) + "][j_ip]" -# j_port = "editable[" + str(i) + "][j_port]" -# j_dept = "editable[" + str(i) + "][j_dept]" -# j_idc = "editable[" + str(i) + "][j_idc]" -# j_type = "editable[" + str(i) + "][j_type]" -# j_group = "editable[" + str(i) + "][j_group]" -# j_active = "editable[" + str(i) + "][j_active]" -# j_comment = "editable[" + str(i) + "][j_comment]" -# -# j_id = request.POST.get(j_id).strip() -# j_ip = request.POST.get(j_ip).strip() -# j_port = request.POST.get(j_port).strip() -# j_dept = request.POST.getlist(j_dept) -# j_idc = request.POST.get(j_idc).strip() -# j_type = request.POST.get(j_type).strip() -# j_group = request.POST.getlist(j_group) -# j_active = request.POST.get(j_active).strip() -# j_comment = request.POST.get(j_comment).strip() -# -# host_info = [j_id, j_ip, j_idc, j_port, j_type, j_group, j_dept, j_active, j_comment] -# batch_host_edit(host_info) -# -# return HttpResponseRedirect('/jasset/host_list/') -# -# -# @require_role(role='user') -# def host_edit_common_batch(request): -# """ 普通用户批量修改主机别名 """ -# u = get_session_user_info(request)[2] -# if request.method == 'POST': -# len_table = request.POST.get('len_table') -# for i in range(int(len_table)): -# j_id = "editable[" + str(i) + "][j_id]" -# j_alias = "editable[" + str(i) + "][j_alias]" -# j_id = request.POST.get(j_id, '').strip() -# j_alias = request.POST.get(j_alias, '').strip() -# a = Asset.objects.get(id=j_id) -# asset_alias = AssetAlias.objects.filter(user=u, host=a) -# if asset_alias: -# asset_alias = asset_alias[0] -# asset_alias.alias = j_alias -# asset_alias.save() -# else: -# AssetAlias.objects.create(user=u, host=a, alias=j_alias) -# return my_render('jasset/host_list_common.html', locals(), request) @require_role(role='user') @@ -313,53 +200,6 @@ def asset_edit(request): return my_render('jasset/asset_edit.html', locals(), request) -# @require_role(role='admin') -# def host_edit_adm(request): -# """ 部门管理员修改主机 """ -# header_title, path1, path2 = u'修改主机', u'资产管理', u'修改主机' -# actives = {1: u'激活', 0: u'禁用'} -# login_types = {'L': 'LDAP', 'M': 'MAP'} -# eidc = IDC.objects.all() -# dept = get_session_user_info(request)[5] -# egroup = BisGroup.objects.exclude(name='ALL').filter(dept=dept) -# host_id = request.GET.get('id', '') -# post = Asset.objects.filter(id=int(host_id)) -# if post: -# post = post[0] -# else: -# return httperror(request, '没有此主机!') -# -# e_group = post.bis_group.all() -# -# if request.method == 'POST': -# j_ip = request.POST.get('j_ip') -# j_idc = request.POST.get('j_idc') -# j_port = request.POST.get('j_port') -# j_type = request.POST.get('j_type') -# j_dept = request.POST.getlist('j_dept') -# j_group = request.POST.getlist('j_group') -# j_active = request.POST.get('j_active') -# j_comment = request.POST.get('j_comment') -# -# host_info = [j_ip, j_port, j_idc, j_type, j_group, j_dept, j_active, j_comment] -# -# if not validate(request, asset_group=j_group, edept=j_dept): -# emg = u'修改失败,您无权操作!' -# return my_render('jasset/asset_edit.html', locals(), request) -# -# if j_type == 'M': -# j_user = request.POST.get('j_user') -# j_password = request.POST.get('j_password') -# db_host_update(host_info, j_user, j_password, post) -# else: -# db_host_update(host_info, post) -# -# smg = u'主机 %s 修改成功' % j_ip -# return HttpResponseRedirect('/jasset/host_detail/?id=%s' % host_id) -# -# return my_render('jasset/asset_edit.html', locals(), request) - - @require_role('admin') def asset_detail(request): """ 主机详情 """ @@ -370,146 +210,3 @@ def asset_detail(request): return my_render('jasset/asset_detail.html', locals(), request) - - -# -# -# @require_admin -# def group_edit(request): -# """ 修改主机组 """ -# header_title, path1, path2 = u'编辑主机组', u'资产管理', u'编辑主机组' -# group_id = request.GET.get('id', '') -# group = BisGroup.objects.filter(id=group_id) -# if group: -# group = group[0] -# else: -# httperror(request, u'没有这个主机组!') -# -# host_all = Asset.objects.all() -# dept_id = get_session_user_info(request)[3] -# eposts = Asset.objects.filter(bis_group=group) -# -# if is_group_admin(request) and not validate(request, asset_group=[group_id]): -# return httperror(request, '编辑失败, 您无权操作!') -# dept = DEPT.objects.filter(id=group.dept.id) -# if dept: -# dept = dept[0] -# else: -# return httperror(request, u'没有这个部门!') -# -# all_dept = dept.asset_set.all() -# posts = [g for g in all_dept if g not in eposts] -# -# if request.method == 'POST': -# j_group = request.POST.get('j_group', '') -# j_hosts = request.POST.getlist('j_hosts', '') -# j_dept = request.POST.get('j_dept', '') -# j_comment = request.POST.get('j_comment', '') -# -# j_dept = DEPT.objects.filter(id=int(j_dept)) -# j_dept = j_dept[0] -# -# group.asset_set.clear() -# for host in j_hosts: -# g = Asset.objects.get(id=host) -# group.asset_set.add(g) -# BisGroup.objects.filter(id=group_id).update(name=j_group, dept=j_dept, comment=j_comment) -# smg = u'主机组%s修改成功' % j_group -# return HttpResponseRedirect('/jasset/group_list') -# -# return my_render('jasset/group_edit.html', locals(), request) -# -# -# @require_admin -# def group_detail(request): -# """ 主机组详情 """ -# header_title, path1, path2 = u'主机组详情', u'资产管理', u'主机组详情' -# login_types = {'L': 'LDAP', 'M': 'MAP'} -# dept = get_session_user_info(request)[5] -# group_id = request.GET.get('id', '') -# group = BisGroup.objects.get(id=group_id) -# if is_super_user(request): -# posts = Asset.objects.filter(bis_group=group).order_by('ip') -# -# elif is_group_admin(request): -# if not validate(request, asset_group=[group_id]): -# return httperror(request, u'您无权查看!') -# posts = Asset.objects.filter(bis_group=group).filter(dept=dept).order_by('ip') -# -# contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) -# return my_render('jasset/group_detail.html', locals(), request) -# -# -# @require_admin -# def group_del_host(request): -# """ 主机组中剔除主机, 并不删除真实主机 """ -# if request.method == 'POST': -# group_id = request.POST.get('group_id') -# offset = request.GET.get('id', '') -# group = BisGroup.objects.get(id=group_id) -# if offset == 'group': -# len_list = request.POST.get("len_list") -# for i in range(int(len_list)): -# key = "id_list[" + str(i) + "]" -# jid = request.POST.get(key) -# g = Asset.objects.get(id=jid) -# group.asset_set.remove(g) -# -# else: -# offset = request.GET.get('id', '') -# group_id = request.GET.get('gid', '') -# group = BisGroup.objects.get(id=group_id) -# g = Asset.objects.get(id=offset) -# group.asset_set.remove(g) -# -# return HttpResponseRedirect('/jasset/group_detail/?id=%s' % group.id) -# - -# @require_admin -# def dept_host_ajax(request): -# """ 添加主机组时, 部门联动主机异步 """ -# dept_id = request.GET.get('id', '') -# if dept_id not in ['1', '2']: -# dept = DEPT.objects.filter(id=dept_id) -# if dept: -# dept = dept[0] -# hosts = dept.asset_set.all() -# else: -# hosts = Asset.objects.all() -# -# return my_render('jasset/dept_host_ajax.html', locals(), request) -# -# -# def show_all_ajax(request): -# """ 批量修改主机时, 部门和组全部显示 """ -# env = request.GET.get('env', '') -# get_id = request.GET.get('id', '') -# host = Asset.objects.filter(id=get_id) -# if host: -# host = host[0] -# return my_render('jasset/show_all_ajax.html', locals(), request) -# -# -# @require_login -# def host_search(request): -# """ 搜索主机 """ -# keyword = request.GET.get('keyword') -# login_types = {'L': 'LDAP', 'M': 'MAP'} -# dept = get_session_user_info(request)[5] -# post_all = Asset.objects.filter(Q(ip__contains=keyword) | -# Q(idc__name__contains=keyword) | -# Q(bis_group__name__contains=keyword) | -# Q(comment__contains=keyword)).distinct().order_by('ip') -# if is_super_user(request): -# posts = post_all -# -# elif is_group_admin(request): -# posts = post_all.filter(dept=dept) -# -# elif is_common_user(request): -# user_id, username = get_session_user_info(request)[0:2] -# post_perm = user_perm_asset_api(username) -# posts = list(set(post_all) & set(post_perm)) -# contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) -# -# return my_render('jasset/host_search.html', locals(), request) \ No newline at end of file diff --git a/jperm/perm_api.py b/jperm/perm_api.py index d777ca82d..8f95a7e1b 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -65,10 +65,10 @@ def perm_user_api(perm_info): 'assets': []}} """ try: - new_users = perm_info['new']['users'] - new_assets = perm_info['new']['assets'] - del_users = perm_info['del']['users'] - del_assets = perm_info['del']['assets'] + new_users = perm_info.get('new', {}).get('users', []) + new_assets = perm_info.get('new', {}).get('assets',[]) + del_users = perm_info.get('del', {}).get('users', []) + del_assets = perm_info.get('del', {}).get('assets', []) print new_users, new_assets except IndexError: @@ -117,18 +117,151 @@ def perm_user_api(perm_info): return results -def get_user_assets(user): - if isinstance(user, int): - user = get_object(User, id=user) - elif isinstance(user, str): - user = get_object(User, username=user) - elif isinstance(user, User): - user = user - else: - user = None +def user_group_permed(user_group): + assets = user_group.asset_set.all() + asset_groups = user_group.asset_group.all() + + for asset_group in asset_groups: + assets.extend(asset_group.asset.all()) + + return {'assets': assets, 'asset_groups': asset_groups} + + +def user_permed(user): + asset_groups = [] + assets = [] + user_groups = user.user_group.all() + asset_groups.extend(user.asset_group.all()) + assets.extend(user.asset.all()) + + for user_group in user_groups: + asset_groups.extend(user_group_permed(user_group).get('assets', [])) + assets.extend((user_group_permed(user_group).get('asset_groups', []))) + + return {'assets': assets, 'asset_groups': asset_groups} + + +def _public_perm_api(info): + """ + 公用的用户,用户组,主机,主机组编辑修改新建调用的api,用来完成授权 + info like that: + { + 'type': 'new_user', + 'user': 'a', + 'group': ['A', 'B'] + } + + { + 'type': 'edit_user', + 'user': 'a', + 'group': {'new': ['A'], 'del': []} + } + + { + 'type': 'del_user', + 'user': ['a', 'b'] + } + + { + 'type': 'edit_user_group', + 'group': 'A', + 'user': {'del': ['a', 'b'], 'new': ['c', 'd']} + } + + { + 'type': 'del_user_group', + 'group': ['A'] + } + + { + 'type': 'new_asset', + 'asset': 'a', + 'group': ['A', 'B'] + } + + { + 'type': 'edit_asset', + 'asset': 'a', + 'group': { + 'del': ['A', ['B'], + 'new': ['C', ['D']] + } + } + + { + 'type': 'del_asset', + 'asset': ['a', 'b'] + } + + { + 'type': 'edit_asset_group', + 'group': 'A', + 'asset': {'new': ['a', 'b'], 'del': ['c', 'd']} + } + + { + 'type': 'del_asset_group', + 'group': ['A', 'B'] + } + """ + + if info.get('type') == 'new_user': + new_assets = [] + user = info.get('user') + user_groups = info.get('group') + for user_group in user_groups: + new_assets.extend(user_group_permed(user_group).get('assets', [])) + + perm_info = { + 'new': {'users': [user], 'assets': new_assets} + } + elif info.get('type') == 'edit_user': + new_assets = [] + del_assets = [] + user = info.get('user') + new_group = info.get('group').get('new') + del_group = info.get('group').get('del') + + for user_group in new_group: + new_assets.extend(user_group_permed(user_group).get('assets', [])) + + for user_group in del_group: + del_assets.extend((user_group_permed(user_group).get('assets', []))) + + perm_info = { + 'del': {'users': [user], 'assets': del_assets}, + 'new': {'users': [user], 'assets': new_assets} + } + + elif info.get('type') == 'del_user': + user = info.get('user') + del_assets = user_permed(user).get('assets', []) + perm_info = { + 'del': {'users': [user], 'assets': del_assets}, + } + + elif info.get('type') == 'edit_user_group': + user_group = info.get('group') + new_users = info.get('user').get('new') + del_users = info.get('user').get('del') + assets = user_group_permed(user_group).get('assets', []) + + perm_info = { + 'new': {'users': new_users, 'assets': assets}, + 'del': {'users': del_users, 'assets': assets} + } + + elif info.get('type') == 'del_user_group': + assets = [] + user_groups = info.get('group', []) + del_users = [user_group.user_set.all() for user_group in user_groups] + for user_group in user_groups: + assets.extend(user_group_permed(user_group).get('assets', [])) + + perm_info = {} + + + -def refresh_group_api(user_group=None, asset_group=None): - """用户组添加删除用户,主机组添加删除主机触发""" - pass From 40d00f7cbd97442ba9c37574dcd0c419b11f5942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B9=BF=E5=AE=8F=E4=BC=9F?= Date: Wed, 21 Oct 2015 20:44:28 +0800 Subject: [PATCH 046/385] lost playboo_run --- jperm/models.py | 6 +- jperm/perm_api.py | 76 ++++++++------- jperm/views.py | 2 +- juser/views.py | 244 ++++------------------------------------------ 4 files changed, 65 insertions(+), 263 deletions(-) diff --git a/jperm/models.py b/jperm/models.py index 1a07e6572..9584e8946 100644 --- a/jperm/models.py +++ b/jperm/models.py @@ -7,5 +7,7 @@ from jasset.models import Asset, AssetGroup class PermLog(models.Model): datetime = models.DateTimeField(auto_now_add=True) - result = models.CharField(max_length=1000, null=True, blank=True, default='') - is_finished = models.BooleanField(default=False) + action = models.CharField(max_length=100, null=True, blank=True, default='') + results = models.CharField(max_length=1000, null=True, blank=True, default='') + is_success = models.BooleanField(default=False) + is_finish = models.BooleanField(default=False) diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 8f95a7e1b..2ded2087c 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -6,9 +6,10 @@ import uuid import re from ansible.playbook import PlayBook from ansible import callbacks, utils -from jumpserver.tasks import playbook_run, add +from jumpserver.tasks import playbook_run from jumpserver.models import Setting +from jperm.models import PermLog def get_object_list(model, id_list): @@ -64,38 +65,21 @@ def perm_user_api(perm_info): 'new': {'users': [], 'assets': []}} """ + log = PermLog(action=perm_info.get('action', '')) try: new_users = perm_info.get('new', {}).get('users', []) - new_assets = perm_info.get('new', {}).get('assets',[]) + new_assets = perm_info.get('new', {}).get('assets', []) del_users = perm_info.get('del', {}).get('users', []) del_assets = perm_info.get('del', {}).get('assets', []) - print new_users, new_assets except IndexError: raise ServerError("Error: function perm_user_api传入参数错误") - # 检查传入的是字符串还是对象 - check_users = new_users + del_users try: - if isinstance(check_users[0], str): - var_type = 'str' - else: - var_type = 'obj' - - except IndexError: - raise ServerError("Error: function perm_user_api传入参数错误") - - try: - if var_type == 'str': - new_ip = new_assets - del_ip = del_assets - new_username = new_users - del_username = del_users - else: - new_ip = [asset.ip for asset in new_assets if isinstance(asset, Asset)] - del_ip = [asset.ip for asset in del_assets if isinstance(asset, Asset)] - new_username = [user.username for user in new_users if isinstance(user, User)] - del_username = [user.username for user in del_users if isinstance(user, User)] + new_ip = [asset.ip for asset in new_assets if isinstance(asset, Asset)] + del_ip = [asset.ip for asset in del_assets if isinstance(asset, Asset)] + new_username = [user.username for user in new_users if isinstance(user, User)] + del_username = [user.username for user in del_users if isinstance(user, User)] except IndexError: raise ServerError("Error: function perm_user_api传入参数类型错误") @@ -114,11 +98,20 @@ def perm_user_api(perm_info): settings = get_object(Setting, name='default') results = playbook_run(inventory, playbook, settings) + if not results.get('failed', 1) and not results.get('unreachable', ''): + is_success = True + else: + is_success = False + + log.results = results + log.is_finish = True + log.is_success = is_success + log.save() return results def user_group_permed(user_group): - assets = user_group.asset_set.all() + assets = user_group.asset.all() asset_groups = user_group.asset_group.all() for asset_group in asset_groups: @@ -130,7 +123,7 @@ def user_group_permed(user_group): def user_permed(user): asset_groups = [] assets = [] - user_groups = user.user_group.all() + user_groups = user.group.all() asset_groups.extend(user.asset_group.all()) assets.extend(user.asset.all()) @@ -213,7 +206,7 @@ def _public_perm_api(info): new_assets.extend(user_group_permed(user_group).get('assets', [])) perm_info = { - 'new': {'users': [user], 'assets': new_assets} + 'new': {'action': 'new user: ' + user.name, 'users': [user], 'assets': new_assets} } elif info.get('type') == 'edit_user': new_assets = [] @@ -229,6 +222,7 @@ def _public_perm_api(info): del_assets.extend((user_group_permed(user_group).get('assets', []))) perm_info = { + 'action': 'edit user: ' + user.name, 'del': {'users': [user], 'assets': del_assets}, 'new': {'users': [user], 'assets': new_assets} } @@ -237,7 +231,7 @@ def _public_perm_api(info): user = info.get('user') del_assets = user_permed(user).get('assets', []) perm_info = { - 'del': {'users': [user], 'assets': del_assets}, + 'action': 'del user: ' + user.name, 'del': {'users': [user], 'assets': del_assets}, } elif info.get('type') == 'edit_user_group': @@ -247,18 +241,32 @@ def _public_perm_api(info): assets = user_group_permed(user_group).get('assets', []) perm_info = { + 'action': 'edit user group: ' + user_group.name, 'new': {'users': new_users, 'assets': assets}, 'del': {'users': del_users, 'assets': assets} } elif info.get('type') == 'del_user_group': - assets = [] - user_groups = info.get('group', []) - del_users = [user_group.user_set.all() for user_group in user_groups] - for user_group in user_groups: - assets.extend(user_group_permed(user_group).get('assets', [])) + user_group = info.get('group', []) + del_users = user_group.user_set.all() + assets = user_group_permed(user_group).get('assets', []) + + perm_info = { + 'action': "del user group: " + user_group.name, 'del': {'users': del_users, 'assets': assets} + } + else: + return + + try: + results = perm_user_api(perm_info) # 通过API授权或回收 + except ServerError, e: + return e + else: + return results + + + - perm_info = {} diff --git a/jperm/views.py b/jperm/views.py index 8a5e67e3e..0fd1f1f99 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -1 +1 @@ -# # coding: utf-8 # import sys # # reload(sys) # sys.setdefaultencoding('utf8') # # from django.shortcuts import render_to_response # from django.template import RequestContext # from jperm.models import Perm, SudoPerm, CmdGroup, Apply from django.db.models import Q from jumpserver.api import * from jperm.perm_api import * from jperm.models import PermLog as Log @require_role('admin') def perm_user_list(request): header_title, path1, path2 = '用户授权', '授权管理', '用户授权' keyword = request.GET.get('search', '') users_list = User.objects.all() # 获取所有用户 if keyword: users_list = users_list.filter(Q(name=keyword) | Q(username=keyword)) # 搜索 users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) # 分页 return my_render('jperm/perm_user_list.html', locals(), request) @require_role('admin') def perm_user_edit(request): header_title, path1, path2 = '用户授权', '授权管理', '授权更改' user_id = request.GET.get('id', '') user = get_object(User, id=user_id) asset_all = Asset.objects.all() # 获取所有资产 asset_group_all = AssetGroup.objects.all() # 获取所有资产组 asset_permed = user.asset.all() # 获取授权的资产对象列表 asset_group_permed = user.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user: assets = [asset for asset in asset_all if asset not in asset_permed] # 获取没有授权的资产对象列表 asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] # 同理 return my_render('jperm/perm_user_edit.html', locals(), request) elif request.method == 'POST' and user: asset_id_select = request.POST.getlist('asset_select', []) # 获取选择的资产id列表 asset_group_id_select = request.POST.getlist('asset_groups_select', []) # 获取选择的资产组id列表 asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 for asset_group in asset_group_new: asset_new.extend(asset_group.asset_set.all()) for asset_group in asset_group_del: asset_del.extend(asset_group.asset_set.all()) perm_info = { 'del': {'users': [user], 'assets': asset_del}, 'new': {'users': [user], 'assets': asset_new} } print perm_info try: results = perm_user_api(perm_info) # 通过API授权或回收 except ServerError, e: return HttpResponse(e) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user.asset = asset_select user.asset_group = asset_group_select user.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') @require_role('admin') def perm_group_list(request): header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' keyword = request.GET.get('search', '') user_groups_list = UserGroup.objects.all() if keyword: request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) return my_render('jperm/perm_group_list.html', locals(), request) @require_role('admin') def perm_group_edit(request): header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' user_group_id = request.GET.get('id', '') user_group = get_object(UserGroup, id=user_group_id) asset_all = Asset.objects.all() asset_group_all = AssetGroup.objects.all() asset_permed = user_group.asset.all() # 获取授权的资产对象列表 asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user_group: assets = [asset for asset in asset_all if asset not in asset_permed] asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] return my_render('jperm/perm_group_edit.html', locals(), request) elif request.method == 'POST' and user_group: asset_id_select = request.POST.getlist('asset_select', []) asset_group_id_select = request.POST.getlist('asset_groups_select', []) asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 results = perm_user_api(asset_new, asset_del, asset_group_new, asset_group_del, user_group=user_group) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user_group.asset = asset_select user_group.asset_group = asset_group_select user_group.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') def log(request): header_title, path1, path2 = '授权记录', '授权管理', '授权记录' log_all = Log.objects.all().order_by('-datetime') log_all, p, logs, page_range, current_page, show_first, show_end = pages(log_all, request) return my_render('jperm/perm_log.html', locals(), request) \ No newline at end of file +# # coding: utf-8 # import sys # # reload(sys) # sys.setdefaultencoding('utf8') # # from django.shortcuts import render_to_response # from django.template import RequestContext # from jperm.models import Perm, SudoPerm, CmdGroup, Apply from django.db.models import Q from jumpserver.api import * from jperm.perm_api import * from jperm.models import PermLog as Log @require_role('admin') def perm_user_list(request): header_title, path1, path2 = '用户授权', '授权管理', '用户授权' keyword = request.GET.get('search', '') users_list = User.objects.all() # 获取所有用户 if keyword: users_list = users_list.filter(Q(name=keyword) | Q(username=keyword)) # 搜索 users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) # 分页 return my_render('jperm/perm_user_list.html', locals(), request) @require_role('admin') def perm_user_edit(request): header_title, path1, path2 = '用户授权', '授权管理', '授权更改' user_id = request.GET.get('id', '') user = get_object(User, id=user_id) asset_all = Asset.objects.all() # 获取所有资产 asset_group_all = AssetGroup.objects.all() # 获取所有资产组 asset_permed = user.asset.all() # 获取授权的资产对象列表 asset_group_permed = user.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user: assets = [asset for asset in asset_all if asset not in asset_permed] # 获取没有授权的资产对象列表 asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] # 同理 return my_render('jperm/perm_user_edit.html', locals(), request) elif request.method == 'POST' and user: asset_id_select = request.POST.getlist('asset_select', []) # 获取选择的资产id列表 asset_group_id_select = request.POST.getlist('asset_groups_select', []) # 获取选择的资产组id列表 asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 for asset_group in asset_group_new: asset_new.extend(asset_group.asset_set.all()) for asset_group in asset_group_del: asset_del.extend(asset_group.asset_set.all()) perm_info = { 'action': 'perm user edit: ' + user.name, 'del': {'users': [user], 'assets': asset_del}, 'new': {'users': [user], 'assets': asset_new} } print perm_info try: results = perm_user_api(perm_info) # 通过API授权或回收 except ServerError, e: return HttpResponse(e) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user.asset = asset_select user.asset_group = asset_group_select user.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') @require_role('admin') def perm_group_list(request): header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' keyword = request.GET.get('search', '') user_groups_list = UserGroup.objects.all() if keyword: request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) return my_render('jperm/perm_group_list.html', locals(), request) @require_role('admin') def perm_group_edit(request): header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' user_group_id = request.GET.get('id', '') user_group = get_object(UserGroup, id=user_group_id) asset_all = Asset.objects.all() asset_group_all = AssetGroup.objects.all() asset_permed = user_group.asset.all() # 获取授权的资产对象列表 asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user_group: assets = [asset for asset in asset_all if asset not in asset_permed] asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] return my_render('jperm/perm_group_edit.html', locals(), request) elif request.method == 'POST' and user_group: asset_id_select = request.POST.getlist('asset_select', []) asset_group_id_select = request.POST.getlist('asset_groups_select', []) asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 users = user_group.user_set.all() perm_info = { 'action': 'perm group edit: ' + user_group.name, 'del': {'users': users, 'assets': asset_del}, 'new': {'users': users, 'assets': asset_new} } results = perm_user_api(perm_info) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user_group.asset = asset_select user_group.asset_group = asset_group_select user_group.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') def log(request): header_title, path1, path2 = '授权记录', '授权管理', '授权记录' log_all = Log.objects.all().order_by('-datetime') log_all, p, logs, page_range, current_page, show_first, show_end = pages(log_all, request) return my_render('jperm/perm_log.html', locals(), request) \ No newline at end of file diff --git a/juser/views.py b/juser/views.py index 6cfd91ef1..c6ca3a649 100644 --- a/juser/views.py +++ b/juser/views.py @@ -11,6 +11,7 @@ from django.template import RequestContext from django.db.models import ObjectDoesNotExist from juser.user_api import * +from jperm.perm_api import _public_perm_api, perm_user_api, user_permed def chg_role(request): @@ -89,31 +90,6 @@ def group_del(request): return HttpResponse('删除成功') -# @require_role(role='admin') -# def group_list_adm(request): -# header_title, path1, path2 = '查看部门小组', '用户管理', '查看小组' -# keyword = request.GET.get('search', '') -# did = request.GET.get('did', '') -# user, dept = get_session_user_dept(request) -# contact_list = dept.usergroup_set.all().order_by('name') -# -# if keyword: -# contact_list = contact_list.filter(Q(name__icontains=keyword) | Q(comment__icontains=keyword)) -# -# contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(contact_list, request) -# return render_to_response('juser/group_list.html', locals(), context_instance=RequestContext(request)) - -# -# @require_role(role='admin') -# def group_detail(request): -# group_id = request.GET.get('id', None) -# if not group_id: -# return HttpResponseRedirect('/') -# group = UserGroup.objects.get(id=group_id) -# users = group.user_set.all() -# return render_to_response('juser/group_detail.html', locals(), context_instance=RequestContext(request)) - - @require_role(role='super') def group_edit(request): error = '' @@ -165,54 +141,6 @@ def group_edit(request): return my_render('juser/group_edit.html', locals(), request) -# @require_role(role='admin') -# def group_edit_adm(request): -# error = '' -# msg = '' -# header_title, path1, path2 = '修改小组信息', '用户管理', '编辑小组' -# user, dept = get_session_user_dept(request) -# if request.method == 'GET': -# group_id = request.GET.get('id', '') -# if not validate(request, user_group=[group_id]): -# return HttpResponseRedirect('/juser/group_list/') -# group = UserGroup.objects.filter(id=group_id) -# if group: -# group = group[0] -# users_all = dept.user_set.all() -# users_selected = group.user_set.all() -# users = [user for user in users_all if user not in users_selected] -# -# return render_to_response('juser/group_edit.html', locals(), context_instance=RequestContext(request)) -# else: -# group_id = request.POST.get('group_id', '') -# group_name = request.POST.get('group_name', '') -# comment = request.POST.get('comment', '') -# users_selected = request.POST.getlist('users_selected') -# -# users = [] -# try: -# if not validate(request, user=users_selected): -# raise ServerError(u'右侧非部门用户') -# -# if not validate(request, user_group=[group_id]): -# raise ServerError(u'没有权限修改本组') -# -# for user_id in users_selected: -# users.extend(User.objects.filter(id=user_id)) -# -# user_group = UserGroup.objects.filter(id=group_id) -# if user_group: -# user_group.update(name=group_name, comment=comment, dept=dept) -# user_group = user_group[0] -# user_group.user_set.clear() -# user_group.user_set = users -# -# except ServerError, e: -# error = e -# -# return HttpResponseRedirect('/juser/group_list/') - - @require_role(role='super') def user_add(request): error = '' @@ -257,11 +185,20 @@ def user_add(request): is_active=is_active, date_joined=datetime.datetime.now()) server_add_user(username, password, ssh_key_pwd, ssh_key_login_need) - except Exception, e: + user = get_object(User, username=username) + if groups: + user_groups = [] + for user_group_id in groups: + user_groups.extend(UserGroup.objects.filter(id=user_group_id)) + print user_groups + results = _public_perm_api({'type': 'new_user', 'user': user, 'group': user_groups}) + print results + except IndexError, e: error = u'添加用户 %s 失败 %s ' % (username, e) try: db_del_user(username) server_del_user(username) + _public_perm_api({'type': 'del_user', 'user': user, 'group': user_groups}) except Exception: pass else: @@ -271,78 +208,6 @@ def user_add(request): return my_render('juser/user_add.html', locals(), request) -# @require_role(role='admin') -# def user_add_adm(request): -# error = '' -# msg = '' -# header_title, path1, path2 = '添加用户', '用户管理', '添加用户' -# user, dept = get_session_user_dept(request) -# group_all = dept.usergroup_set.all() -# -# if request.method == 'POST': -# username = request.POST.get('username', '') -# password = PyCrypt.gen_rand_pwd(16) -# name = request.POST.get('name', '') -# email = request.POST.get('email', '') -# groups = request.POST.getlist('groups', []) -# ssh_key_pwd = PyCrypt.gen_rand_pwd(16) -# is_active = True if request.POST.get('is_active', '1') == '1' else False -# ldap_pwd = PyCrypt.gen_rand_pwd(16) -# -# try: -# if '' in [username, password, ssh_key_pwd, name, groups, is_active]: -# error = u'带*内容不能为空' -# raise ServerError -# user = User.objects.filter(username=username) -# if user: -# error = u'用户 %s 已存在' % username -# raise ServerError -# -# except ServerError: -# pass -# else: -# try: -# user = db_add_user(username=username, -# password=CRYPTOR.md5_crypt(password), -# name=name, email=email, dept=dept, -# groups=groups, role='CU', -# ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd), -# ldap_pwd=CRYPTOR.encrypt(ldap_pwd), -# is_active=is_active, -# date_joined=datetime.datetime.now()) -# -# server_add_user(username, password, ssh_key_pwd) -# if LDAP_ENABLE: -# ldap_add_user(username, ldap_pwd) -# -# except Exception, e: -# error = u'添加用户 %s 失败 %s ' % (username, e) -# try: -# db_del_user(username) -# server_del_user(username) -# if LDAP_ENABLE: -# ldap_del_user(username) -# except Exception: -# pass -# else: -# mail_title = u'恭喜你的跳板机用户添加成功 Jumpserver' -# mail_msg = """ -# Hi, %s -# 您的用户名: %s -# 您的部门: %s -# 您的角色: %s -# 您的web登录密码: %s -# 您的ssh密钥文件密码: %s -# 密钥下载地址: http://%s:%s/juser/down_key/?id=%s -# 说明: 请登陆后再下载密钥! -# """ % (name, username, dept.name, '普通用户', -# password, ssh_key_pwd, SEND_IP, SEND_PORT, user.id) -# send_mail(mail_title, mail_msg, MAIL_FROM, [email], fail_silently=False) -# msg = u'添加用户 %s 成功! 用户密码已发送到 %s 邮箱!' % (username, email) -# -# return render_to_response('juser/user_add.html', locals(), context_instance=RequestContext(request)) - - @require_role(role='super') def user_list(request): user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} @@ -365,31 +230,6 @@ def user_list(request): return my_render('juser/user_list.html', locals(), request) -# @require_role(role='admin') -# def user_list_adm(request): -# user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} -# header_title, path1, path2 = '查看用户', '用户管理', '用户列表' -# keyword = request.GET.get('keyword', '') -# user, dept = get_session_user_dept(request) -# gid = request.GET.get('gid', '') -# contact_list = dept.user_set.all().order_by('name') -# -# if gid: -# if not validate(request, user_group=[gid]): -# return HttpResponseRedirect('/juser/user_list/') -# user_group = UserGroup.objects.filter(id=gid) -# if user_group: -# user_group = user_group[0] -# contact_list = user_group.user_set.all() -# -# if keyword: -# contact_list = contact_list.filter(Q(username__icontains=keyword) | Q(name__icontains=keyword)).order_by('name') -# -# contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(contact_list, request) -# -# return render_to_response('juser/user_list.html', locals(), context_instance=RequestContext(request)) - - @require_role(role='user') def user_detail(request): header_title, path1, path2 = '用户详情', '用户管理', '用户详情' @@ -420,8 +260,12 @@ def user_del(request): user_ids = request.GET.get('id', '') user_id_list = user_ids.split(',') for user_id in user_id_list: - User.objects.filter(id=user_id).delete() - + user = get_object(User, id=user_id) + if user: + assets = user_permed(user) + result = _public_perm_api({'type': 'del_user', 'user': user, 'asset': assets}) + print result + user.delete() return HttpResponse('删除成功') @@ -540,6 +384,7 @@ def user_edit(request): admin_groups=admin_groups, role=role_post, is_active=is_active) + _public_perm_api({'type': 'del_user', 'user': user, 'asset': user_permed(user)}) if email_need: msg = u""" @@ -561,59 +406,6 @@ def user_edit(request): # @require_role(role='admin') def user_edit_adm(request): pass -# header_title, path1, path2 = '编辑用户', '用户管理', '用户编辑' -# user, dept = get_session_user_dept(request) -# if request.method == 'GET': -# user_id = request.GET.get('id', '') -# if not user_id: -# return HttpResponseRedirect('/juser/user_list/') -# -# if not validate(request, user=[user_id]): -# return HttpResponseRedirect('/juser/user_list/') -# -# user = User.objects.filter(id=user_id) -# dept_all = DEPT.objects.all() -# group_all = dept.usergroup_set.all() -# if user: -# user = user[0] -# groups_str = ' '.join([str(group.id) for group in user.group.all()]) -# -# else: -# user_id = request.POST.get('user_id', '') -# password = request.POST.get('password', '') -# name = request.POST.get('name', '') -# email = request.POST.get('email', '') -# groups = request.POST.getlist('groups', []) -# ssh_key_pwd = request.POST.get('ssh_key_pwd', '') -# is_active = True if request.POST.get('is_active', '1') == '1' else False -# -# if not validate(request, user=[user_id], user_group=groups): -# return HttpResponseRedirect('/juser/user_edit/') -# if user_id: -# user = User.objects.filter(id=user_id) -# if user: -# user = user[0] -# else: -# return HttpResponseRedirect('/juser/user_list/') -# -# if password != user.password: -# password = CRYPTOR.md5_crypt(password) -# -# if ssh_key_pwd != user.ssh_key_pwd: -# ssh_key_pwd = CRYPTOR.encrypt(ssh_key_pwd) -# -# db_update_user(user_id=user_id, -# password=password, -# name=name, -# email=email, -# groups=groups, -# is_active=is_active, -# ssh_key_pwd=ssh_key_pwd) -# -# return HttpResponseRedirect('/juser/user_list/') -# -# return render_to_response('juser/user_edit.html', locals(), context_instance=RequestContext(request)) -# def profile(request): From 77131a39d48f87860a42095453a9248f13da8fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B9=BF=E5=AE=8F=E4=BC=9F?= Date: Wed, 21 Oct 2015 20:53:07 +0800 Subject: [PATCH 047/385] recovery playboo_run --- jperm/perm_api.py | 2 -- jumpserver/tasks.py | 46 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 jumpserver/tasks.py diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 2ded2087c..18892ce94 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -4,8 +4,6 @@ from jasset.models import * from jumpserver.api import * import uuid import re -from ansible.playbook import PlayBook -from ansible import callbacks, utils from jumpserver.tasks import playbook_run from jumpserver.models import Setting diff --git a/jumpserver/tasks.py b/jumpserver/tasks.py new file mode 100644 index 000000000..54726eb10 --- /dev/null +++ b/jumpserver/tasks.py @@ -0,0 +1,46 @@ +# coding: utf-8 + +from ansible.playbook import PlayBook +from ansible import callbacks, utils + + +def playbook_run(inventory, playbook, default_user=None, default_port=None, default_pri_key_path=None): + stats = callbacks.AggregateStats() + playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) + runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY) + # run the playbook + print default_user, default_port, default_pri_key_path, inventory, playbook + if default_user and default_port and default_pri_key_path: + playbook = PlayBook(host_list=inventory, + playbook=playbook, + forks=5, + remote_user=default_user, + remote_port=default_port, + private_key_file=default_pri_key_path, + callbacks=playbook_cb, + runner_callbacks=runner_cb, + stats=stats, + become=True, + become_user='root') + else: + playbook = PlayBook(host_list=inventory, + playbook=playbook, + forks=5, + callbacks=playbook_cb, + runner_callbacks=runner_cb, + stats=stats, + become=True, + become_user='root') + results = playbook.run() + results_r = {'unreachable': [], 'failures': [], 'success': []} + for hostname, result in results.items(): + if result.get('unreachable', 2): + results_r['unreachable'].append(hostname) + print "%s >>> unreachable" % hostname + elif result.get('failures', 2): + results_r['failures'].append(hostname) + print "%s >>> Failed" % hostname + else: + results_r['success'].append(hostname) + print "%s >>> Success" % hostname + return results_r \ No newline at end of file From 4baffed48172c548b783e8d68cde02cddd513002 Mon Sep 17 00:00:00 2001 From: "ibuler@qq.com" Date: Wed, 21 Oct 2015 21:19:18 +0800 Subject: [PATCH 048/385] base commit --- jperm/perm_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 18892ce94..0202ef19b 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -96,7 +96,7 @@ def perm_user_api(perm_info): settings = get_object(Setting, name='default') results = playbook_run(inventory, playbook, settings) - if not results.get('failed', 1) and not results.get('unreachable', ''): + if not results.get('failures', 1) and not results.get('unreachable', ''): is_success = True else: is_success = False From 79e81340dc837df84799cd3cad95d44bfde87383 Mon Sep 17 00:00:00 2001 From: "ibuler@qq.com" Date: Wed, 21 Oct 2015 22:39:53 +0800 Subject: [PATCH 049/385] modify log --- jperm/perm_api.py | 3 ++- templates/jperm/perm_log.html | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 0202ef19b..585fa3cbf 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -204,7 +204,8 @@ def _public_perm_api(info): new_assets.extend(user_group_permed(user_group).get('assets', [])) perm_info = { - 'new': {'action': 'new user: ' + user.name, 'users': [user], 'assets': new_assets} + 'action': 'new user: ' + user.name, + 'new': {'users': [user], 'assets': new_assets} } elif info.get('type') == 'edit_user': new_assets = [] diff --git a/templates/jperm/perm_log.html b/templates/jperm/perm_log.html index b24a868a1..ead5f399d 100644 --- a/templates/jperm/perm_log.html +++ b/templates/jperm/perm_log.html @@ -40,7 +40,8 @@ 日期 - 结果 + 动作 + 成功 完成 @@ -48,10 +49,13 @@ {% for log in logs %} {{ log.datetime | date:"Y-n-d G:i:s" }} + {{ log.action }} - {{ log.result | result2bool | safe }} + + {{ log.is_success | yesno:"是,否,为止" }} + - {{ log.is_finished | yesno:"是,否,为止" }} + {{ log.is_finish | yesno:"是,否,为止" }} {% endfor %} @@ -70,4 +74,15 @@
  • +{% endblock %} + +{% block self_footer_js %} + + {% endblock %} \ No newline at end of file From 72a18d6abf4b2a279e94d5a9e5c4a28e09dcbb98 Mon Sep 17 00:00:00 2001 From: "ibuler@qq.com" Date: Sat, 24 Oct 2015 23:52:06 +0800 Subject: [PATCH 050/385] use map --- jperm/models.py | 8 ++ jperm/perm_api.py | 22 ++++- jperm/urls.py | 41 +++------ jperm/views.py | 2 +- jumpserver/tasks.py | 1 + juser/user_api.py | 36 ++++---- playbook/user_perm.yaml | 2 +- templates/jperm/sys_user_add.html | 130 +++++++++++++++++++++++++++++ templates/jperm/sys_user_list.html | 102 ++++++++++++++++++++++ templates/nav.html | 2 +- 10 files changed, 290 insertions(+), 56 deletions(-) create mode 100644 templates/jperm/sys_user_add.html create mode 100644 templates/jperm/sys_user_list.html diff --git a/jperm/models.py b/jperm/models.py index 9584e8946..b86ddd433 100644 --- a/jperm/models.py +++ b/jperm/models.py @@ -11,3 +11,11 @@ class PermLog(models.Model): results = models.CharField(max_length=1000, null=True, blank=True, default='') is_success = models.BooleanField(default=False) is_finish = models.BooleanField(default=False) + + +class SysUser(models.Model): + username = models.CharField(max_length=100) + password = models.CharField(max_length=100) + comment = models.CharField(max_length=100, null=True, blank=True, default='') + + diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 585fa3cbf..ccfcbb6b4 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -76,8 +76,8 @@ def perm_user_api(perm_info): try: new_ip = [asset.ip for asset in new_assets if isinstance(asset, Asset)] del_ip = [asset.ip for asset in del_assets if isinstance(asset, Asset)] - new_username = [user.username for user in new_users if isinstance(user, User)] - del_username = [user.username for user in del_users if isinstance(user, User)] + new_username = [user.username for user in new_users] + del_username = [user.username for user in del_users] except IndexError: raise ServerError("Error: function perm_user_api传入参数类型错误") @@ -90,7 +90,7 @@ def perm_user_api(perm_info): playbook = get_playbook(os.path.join(BASE_DIR, 'playbook', 'user_perm.yaml'), {'the_new_group': 'new', 'the_del_group': 'del', 'the_new_users': the_new_users, 'the_del_users': the_del_users, - 'the_pub_key': '/tmp/id_rsa.pub'}) + 'KEY_DIR': os.path.join(SSH_KEY_DIR, 'sysuser')}) print playbook, inventory @@ -264,6 +264,22 @@ def _public_perm_api(info): return results +def push_user(user, asset_groups_id): + assets = [] + if not user: + return {'error': '没有该用户'} + for group_id in asset_groups_id: + asset_group = get_object(AssetGroup, id=group_id) + if asset_group: + assets.extend(asset_group.asset_set.all()) + perm_info = { + 'action': 'Push user:' + user.username, + 'new': {'users': [user], 'assets': assets} + } + + results = perm_user_api(perm_info) + return results + diff --git a/jperm/urls.py b/jperm/urls.py index c95ab9684..3892407c1 100644 --- a/jperm/urls.py +++ b/jperm/urls.py @@ -2,34 +2,13 @@ from django.conf.urls import patterns, include, url from jperm.views import * urlpatterns = patterns('jperm.views', - # Examples: - (r'^user/$', perm_user_list), - (r'^perm_user_edit/$', perm_user_edit), - (r'^group/$', perm_group_list), - (r'^perm_group_edit/$', perm_group_edit), - (r'log/$', log), - # (r'^dept_perm_edit/$', 'dept_perm_edit'), - # (r'^perm_list/$', view_splitter, {'su': perm_list, 'adm': perm_list_adm}), - # (r'^dept_perm_list/$', 'dept_perm_list'), - # (r'^perm_user_detail/$', 'perm_user_detail'), - # (r'^perm_detail/$', 'perm_detail'), - # (r'^perm_del/$', 'perm_del'), - # (r'^perm_asset_detail/$', 'perm_asset_detail'), - # (r'^sudo_list/$', view_splitter, {'su': sudo_list, 'adm': sudo_list_adm}), - # (r'^sudo_del/$', 'sudo_del'), - # (r'^sudo_edit/$', view_splitter, {'su': sudo_edit, 'adm': sudo_edit_adm}), - # (r'^sudo_refresh/$', 'sudo_refresh'), - # (r'^sudo_detail/$', 'sudo_detail'), - # (r'^cmd_add/$', view_splitter, {'su': cmd_add, 'adm': cmd_add_adm}), - # (r'^cmd_list/$', 'cmd_list'), - # (r'^cmd_del/$', 'cmd_del'), - # (r'^cmd_edit/$', 'cmd_edit'), - # (r'^cmd_detail/$', 'cmd_detail'), - # (r'^apply/$', 'perm_apply'), - # (r'^apply_show/(\w+)/$', 'perm_apply_log'), - # (r'^apply_exec/$', 'perm_apply_exec'), - # (r'^apply_info/$', 'perm_apply_info'), - # (r'^apply_del/$', 'perm_apply_del'), - # (r'^apply_search/$', 'perm_apply_search'), - -) + (r'^user/$', perm_user_list), + (r'^perm_user_edit/$', perm_user_edit), + (r'^group/$', perm_group_list), + (r'^perm_group_edit/$', perm_group_edit), + (r'^log/$', log), + (r'^sys_user_add/$', sys_user_add), + (r'^sys_user_list/$', sys_user_list), + (r'^sys_user_del/$', sys_user_del), + (r'^sys_user_edit/$', sys_user_edit), + ) diff --git a/jperm/views.py b/jperm/views.py index 0fd1f1f99..2e910e025 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -1 +1 @@ -# # coding: utf-8 # import sys # # reload(sys) # sys.setdefaultencoding('utf8') # # from django.shortcuts import render_to_response # from django.template import RequestContext # from jperm.models import Perm, SudoPerm, CmdGroup, Apply from django.db.models import Q from jumpserver.api import * from jperm.perm_api import * from jperm.models import PermLog as Log @require_role('admin') def perm_user_list(request): header_title, path1, path2 = '用户授权', '授权管理', '用户授权' keyword = request.GET.get('search', '') users_list = User.objects.all() # 获取所有用户 if keyword: users_list = users_list.filter(Q(name=keyword) | Q(username=keyword)) # 搜索 users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) # 分页 return my_render('jperm/perm_user_list.html', locals(), request) @require_role('admin') def perm_user_edit(request): header_title, path1, path2 = '用户授权', '授权管理', '授权更改' user_id = request.GET.get('id', '') user = get_object(User, id=user_id) asset_all = Asset.objects.all() # 获取所有资产 asset_group_all = AssetGroup.objects.all() # 获取所有资产组 asset_permed = user.asset.all() # 获取授权的资产对象列表 asset_group_permed = user.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user: assets = [asset for asset in asset_all if asset not in asset_permed] # 获取没有授权的资产对象列表 asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] # 同理 return my_render('jperm/perm_user_edit.html', locals(), request) elif request.method == 'POST' and user: asset_id_select = request.POST.getlist('asset_select', []) # 获取选择的资产id列表 asset_group_id_select = request.POST.getlist('asset_groups_select', []) # 获取选择的资产组id列表 asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 for asset_group in asset_group_new: asset_new.extend(asset_group.asset_set.all()) for asset_group in asset_group_del: asset_del.extend(asset_group.asset_set.all()) perm_info = { 'action': 'perm user edit: ' + user.name, 'del': {'users': [user], 'assets': asset_del}, 'new': {'users': [user], 'assets': asset_new} } print perm_info try: results = perm_user_api(perm_info) # 通过API授权或回收 except ServerError, e: return HttpResponse(e) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user.asset = asset_select user.asset_group = asset_group_select user.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') @require_role('admin') def perm_group_list(request): header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' keyword = request.GET.get('search', '') user_groups_list = UserGroup.objects.all() if keyword: request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) return my_render('jperm/perm_group_list.html', locals(), request) @require_role('admin') def perm_group_edit(request): header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' user_group_id = request.GET.get('id', '') user_group = get_object(UserGroup, id=user_group_id) asset_all = Asset.objects.all() asset_group_all = AssetGroup.objects.all() asset_permed = user_group.asset.all() # 获取授权的资产对象列表 asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user_group: assets = [asset for asset in asset_all if asset not in asset_permed] asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] return my_render('jperm/perm_group_edit.html', locals(), request) elif request.method == 'POST' and user_group: asset_id_select = request.POST.getlist('asset_select', []) asset_group_id_select = request.POST.getlist('asset_groups_select', []) asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 users = user_group.user_set.all() perm_info = { 'action': 'perm group edit: ' + user_group.name, 'del': {'users': users, 'assets': asset_del}, 'new': {'users': users, 'assets': asset_new} } results = perm_user_api(perm_info) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user_group.asset = asset_select user_group.asset_group = asset_group_select user_group.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') def log(request): header_title, path1, path2 = '授权记录', '授权管理', '授权记录' log_all = Log.objects.all().order_by('-datetime') log_all, p, logs, page_range, current_page, show_first, show_end = pages(log_all, request) return my_render('jperm/perm_log.html', locals(), request) \ No newline at end of file +# # coding: utf-8 # import sysuser # # reload(sysuser) # sysuser.setdefaultencoding('utf8') # # from django.shortcuts import render_to_response # from django.template import RequestContext # from jperm.models import Perm, SudoPerm, CmdGroup, Apply from django.db.models import Q from jumpserver.api import * from jperm.perm_api import * from jperm.models import PermLog as Log from jperm.models import SysUser from juser.user_api import gen_ssh_key @require_role('admin') def perm_user_list(request): header_title, path1, path2 = '用户授权', '授权管理', '用户授权' keyword = request.GET.get('search', '') users_list = User.objects.all() # 获取所有用户 if keyword: users_list = users_list.filter(Q(name=keyword) | Q(username=keyword)) # 搜索 users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) # 分页 return my_render('jperm/perm_user_list.html', locals(), request) @require_role('admin') def perm_user_edit(request): header_title, path1, path2 = '用户授权', '授权管理', '授权更改' user_id = request.GET.get('id', '') user = get_object(User, id=user_id) asset_all = Asset.objects.all() # 获取所有资产 asset_group_all = AssetGroup.objects.all() # 获取所有资产组 asset_permed = user.asset.all() # 获取授权的资产对象列表 asset_group_permed = user.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user: assets = [asset for asset in asset_all if asset not in asset_permed] # 获取没有授权的资产对象列表 asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] # 同理 return my_render('jperm/perm_user_edit.html', locals(), request) elif request.method == 'POST' and user: asset_id_select = request.POST.getlist('asset_select', []) # 获取选择的资产id列表 asset_group_id_select = request.POST.getlist('asset_groups_select', []) # 获取选择的资产组id列表 asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 for asset_group in asset_group_new: asset_new.extend(asset_group.asset_set.all()) for asset_group in asset_group_del: asset_del.extend(asset_group.asset_set.all()) perm_info = { 'action': 'perm user edit: ' + user.name, 'del': {'users': [user], 'assets': asset_del}, 'new': {'users': [user], 'assets': asset_new} } print perm_info try: results = perm_user_api(perm_info) # 通过API授权或回收 except ServerError, e: return HttpResponse(e) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user.asset = asset_select user.asset_group = asset_group_select user.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') @require_role('admin') def perm_group_list(request): header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' keyword = request.GET.get('search', '') user_groups_list = UserGroup.objects.all() if keyword: request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) return my_render('jperm/perm_group_list.html', locals(), request) @require_role('admin') def perm_group_edit(request): header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' user_group_id = request.GET.get('id', '') user_group = get_object(UserGroup, id=user_group_id) asset_all = Asset.objects.all() asset_group_all = AssetGroup.objects.all() asset_permed = user_group.asset.all() # 获取授权的资产对象列表 asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 if request.method == 'GET' and user_group: assets = [asset for asset in asset_all if asset not in asset_permed] asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] return my_render('jperm/perm_group_edit.html', locals(), request) elif request.method == 'POST' and user_group: asset_id_select = request.POST.getlist('asset_select', []) asset_group_id_select = request.POST.getlist('asset_groups_select', []) asset_select = get_object_list(Asset, asset_id_select) asset_group_select = get_object_list(AssetGroup, asset_group_id_select) asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 users = user_group.user_set.all() perm_info = { 'action': 'perm group edit: ' + user_group.name, 'del': {'users': users, 'assets': asset_del}, 'new': {'users': users, 'assets': asset_new} } results = perm_user_api(perm_info) unreachable_asset = [] failures_asset = [] for ip in results.get('unreachable'): unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) for ip in results.get('failures'): failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) failures_asset.extend(unreachable_asset) # 失败的授权要统计 for asset in failures_asset: if asset in asset_select: asset_select.remove(asset) else: asset_select.append(asset) user_group.asset = asset_select user_group.asset_group = asset_group_select user_group.save() # 保存到数据库 return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") else: return HttpResponse('输入错误') def log(request): header_title, path1, path2 = '授权记录', '授权管理', '授权记录' log_all = Log.objects.all().order_by('-datetime') log_all, p, logs, page_range, current_page, show_first, show_end = pages(log_all, request) return my_render('jperm/perm_log.html', locals(), request) def sys_user_add(request): asset_group_all = AssetGroup.objects.all() if request.method == 'POST': username = request.POST.get('username', '') password = request.POST.get('password', '') asset_groups_id = request.POST.getlist('asset_groups_select', []) comment = request.POST.get('comment') sys_user = SysUser(username=username, password=password, comment=comment) sys_user.save() gen_ssh_key(username, key_dir=os.path.join(SSH_KEY_DIR, 'sysuser'), authorized_keys=False) results = push_user(sys_user, asset_groups_id) return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") return my_render('jperm/sys_user_add.html', locals(), request) def sys_user_list(request): users_list = SysUser.objects.all() users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) return my_render('jperm/sys_user_list.html', locals(), request) def sys_user_edit(request): pass def sys_user_del(request): pass \ No newline at end of file diff --git a/jumpserver/tasks.py b/jumpserver/tasks.py index 54726eb10..22fd514ef 100644 --- a/jumpserver/tasks.py +++ b/jumpserver/tasks.py @@ -32,6 +32,7 @@ def playbook_run(inventory, playbook, default_user=None, default_port=None, defa become=True, become_user='root') results = playbook.run() + print results results_r = {'unreachable': [], 'failures': [], 'success': []} for hostname, result in results.items(): if result.get('unreachable', 2): diff --git a/juser/user_api.py b/juser/user_api.py index 04a638a0f..2354b663f 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -1,6 +1,7 @@ # coding: utf-8 from Crypto.PublicKey import RSA +from subprocess import call from juser.models import AdminGroup from jumpserver.api import * @@ -115,30 +116,27 @@ def db_del_user(username): user.delete() -def gen_ssh_key(username, password=None, length=2048): +def gen_ssh_key(username, password='', + key_dir=os.path.join(BASE_DIR, 'keys/user/'), + authorized_keys=True, home="/home", length=2048): """ generate a user ssh key in a property dir 生成一个用户ssh密钥对 """ - print "gen_ssh_key" + str(time.time()) - private_key_dir = os.path.join(BASE_DIR, 'keys/jumpserver/') - private_key_file = os.path.join(private_key_dir, username+".pem") - public_key_dir = '/home/%s/.ssh/' % username - public_key_file = os.path.join(public_key_dir, 'authorized_keys') - is_dir(private_key_dir) - is_dir(public_key_dir, username, mode=0700) + private_key_file = os.path.join(key_dir, username) + if os.path.isfile(private_key_file): + os.unlink(private_key_file) + ret = bash('ssh-keygen -t rsa -f %s -b %s -P "%s"' % (private_key_file, length, password)) - key = RSA.generate(length) - with open(private_key_file, 'w') as pri_f: - pri_f.write(key.exportKey('PEM', password)) - os.chmod(private_key_file, 0600) - print "gen_ssh_pub_key" + str(time.time()) - pub_key = key.publickey() - with open(public_key_file, 'w') as pub_f: - pub_f.write(pub_key.exportKey('OpenSSH')) - os.chmod(public_key_file, 0600) - bash('chown %s:%s %s' % (username, username, public_key_file)) - print "gen_ssh_key_end" + str(time.time()) + if authorized_keys: + auth_key_dir = os.path.join(home, username, '.ssh') + is_dir(auth_key_dir, username, 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: + auth_f.write(pub_f.read()) + os.chmod(authorized_key_file, 0600) + bash('chown %s:%s %s' % (username, username, authorized_key_file)) def server_add_user(username, password, ssh_key_pwd, ssh_key_login_need): diff --git a/playbook/user_perm.yaml b/playbook/user_perm.yaml index 6310249d4..4bcfd72e6 100644 --- a/playbook/user_perm.yaml +++ b/playbook/user_perm.yaml @@ -13,5 +13,5 @@ file: name=/home/{{ item }}/.ssh mode=700 owner={{ item }} group={{ item }} state=directory with_items: [ the_new_users ] - name: set authorizied_file - copy: src=the_pub_key dest=/home/{{ item }}/.ssh/authorizied_keys owner={{ item }} group={{ item }} mode=600 + 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/jperm/sys_user_add.html b/templates/jperm/sys_user_add.html new file mode 100644 index 000000000..ac4fd2db1 --- /dev/null +++ b/templates/jperm/sys_user_add.html @@ -0,0 +1,130 @@ +{% extends 'base.html' %} +{% load mytags %} +{% block content %} + {% include 'nav_cat_bar.html' %} +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + {% if error %} +
    {{ error }}
    + {% endif %} + {% if msg %} +
    {{ msg }}
    + {% endif %} +
    + +
    + +
    +
    +
    +
    + +
    + + 通常在其它硬件上使用,服务器会使用自动生成的key +
    + +
    +
    +
    + +
    +
    + + 将在以上资产组服务器新建系统用户 +
    +
    + +
    +
    + + +
    +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block self_footer_js %} + + +{% endblock %} \ No newline at end of file diff --git a/templates/jperm/sys_user_list.html b/templates/jperm/sys_user_list.html new file mode 100644 index 000000000..20236a0ea --- /dev/null +++ b/templates/jperm/sys_user_list.html @@ -0,0 +1,102 @@ +{% extends 'base.html' %} +{% load mytags %} +{% block content %} +{% include 'nav_cat_bar.html' %} + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    + + + + + + + + + + {% for user in users.object_list %} + + + + + + {% endfor %} + +
    + + 用户名操作
    + + {{ user.username }} + 详情 + 编辑 + 删除 +
    +
    +
    +
    + Showing {{ users.start_index }} to {{ users.end_index }} of {{ p.count }} entries +
    +
    + {% include 'paginator.html' %} +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +{% endblock %} +{% block self_head_css_js %} + +{% endblock %} \ No newline at end of file diff --git a/templates/nav.html b/templates/nav.html index f0b6bd2db..08a68b620 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -36,7 +36,7 @@
  • - 命令授权 + 系统用户
  • 权限审批
  • 授权记录
  • From 9366003f7bc574037d2df9a02be799fb3b380d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B9=BF=E5=AE=8F=E4=BC=9F?= Date: Mon, 26 Oct 2015 22:17:16 +0800 Subject: [PATCH 051/385] jlog --- connect.py | 71 ------------ jlog/log_api.py | 68 +++++++++++ jlog/models.py | 2 - jlog/urls.py | 6 +- jlog/views.py | 184 ++++++++++++++--------------- jumpserver/api.py | 198 +++++++++++++------------------- jumpserver/settings.py | 2 +- jumpserver/urls.py | 2 +- templates/jlog/base.jinja2 | 111 ++++++++++++++++++ templates/jlog/dynamic.jinja2 | 11 ++ templates/jlog/log_offline.html | 10 +- templates/jlog/log_online.html | 8 -- templates/jlog/static.jinja2 | 18 +++ 13 files changed, 379 insertions(+), 312 deletions(-) create mode 100644 jlog/log_api.py create mode 100644 templates/jlog/base.jinja2 create mode 100644 templates/jlog/dynamic.jinja2 create mode 100644 templates/jlog/static.jinja2 diff --git a/connect.py b/connect.py index 7994205ec..66268d50c 100644 --- a/connect.py +++ b/connect.py @@ -19,7 +19,6 @@ if django.get_version() != '1.6': django.setup() from jumpserver.api import ServerError, User, Asset, Jtty, get_object from jumpserver.api import logger -from jumpserver.api import BisGroup as AssetGroup login_user = get_object(User, username=getpass.getuser()) @@ -98,76 +97,6 @@ def print_prompt(): print textwrap.dedent(msg) -# def remote_exec_cmd(ip, port, username, password, cmd): -# try: -# time.sleep(5) -# ssh = paramiko.SSHClient() -# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) -# ssh.connect(ip, port, username, password, timeout=5) -# stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd) -# out = stdout.readlines() -# err = stderr.readlines() -# color_print('%s:' % ip, 'blue') -# for i in out: -# color_print(" " * 4 + i.strip(), 'green') -# for j in err: -# color_print(" " * 4 + j.strip(), 'red') -# ssh.close() -# except Exception as e: -# color_print(ip + ':', 'blue') -# color_print(str(e), 'red') - - -# def multi_remote_exec_cmd(hosts, username, cmd): -# pool = Pool(processes=5) -# for host in hosts: -# username, password, ip, port = get_connect_item(username, host) -# pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd)) -# pool.close() -# pool.join() - - -# def exec_cmd_servers(username): -# color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green') -# user.get_asset_info(printable=True) -# while True: -# hosts = [] -# inputs = raw_input('\033[1;32mip(s)>: \033[0m') -# if inputs in ['q', 'Q']: -# break -# get_hosts = login_user.get_asset_info().keys() -# -# if ',' in inputs: -# ips_input = inputs.split(',') -# for host in ips_input: -# if host in get_hosts: -# hosts.append(host) -# else: -# for host in get_hosts: -# if fnmatch.fnmatch(host, inputs): -# hosts.append(host.strip()) -# -# if len(hosts) == 0: -# color_print("Check again, Not matched any ip!", 'red') -# continue -# else: -# print "You matched ip: %s" % hosts -# color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green') -# while True: -# cmd = raw_input('\033[1;32mCmd(s): \033[0m') -# if cmd in ['q', 'Q']: -# break -# exec_log_dir = os.path.join(log_dir, 'exec_cmds') -# if not os.path.isdir(exec_log_dir): -# os.mkdir(exec_log_dir) -# os.chmod(exec_log_dir, 0777) -# filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d')) -# f = open(filename, 'a') -# f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" % -# (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd)) -# multi_remote_exec_cmd(hosts, username, cmd) - - def main(): """ he he diff --git a/jlog/log_api.py b/jlog/log_api.py new file mode 100644 index 000000000..c9d54149a --- /dev/null +++ b/jlog/log_api.py @@ -0,0 +1,68 @@ +# coding: utf-8 + + +from argparse import ArgumentParser, FileType +from contextlib import closing +from codecs import open as copen +from json import dumps +from math import ceil +from os.path import basename, dirname, exists, join +from struct import unpack +from subprocess import Popen +from sys import platform, prefix, stderr +from tempfile import NamedTemporaryFile + +from jinja2 import FileSystemLoader, Template +from jinja2.environment import Environment + +from jumpserver.api import BASE_DIR + + +DEFAULT_TEMPLATE = join(BASE_DIR, 'templates', 'jlog', 'static.jinja2') + + +def escapeString(string): + string = string.encode('unicode_escape').decode('utf-8') + string = string.replace("'", "\\'") + string = '\'' + string + '\'' + return string + + +def getTiming(timef): + timing = None + with closing(timef): + timing = [l.strip().split(' ') for l in timef] + timing = [(int(ceil(float(r[0]) * 1000)), int(r[1])) for r in timing] + return timing + + +def scriptToJSON(scriptf, timing=None): + ret = [] + + with closing(scriptf): + scriptf.readline() # ignore first header line from script file + offset = 0 + for t in timing: + data = escapeString(scriptf.read(t[1])) + offset += t[0] + ret.append((data, offset)) + return dumps(ret) + + +def renderTemplate(script_path, time_file_path, dimensions=(24, 60), templatename=DEFAULT_TEMPLATE): + with copen(script_path, encoding='utf-8', errors='replace') as scriptf: + with open(time_file_path) as timef: + timing = getTiming(timef) + json = scriptToJSON(scriptf, timing) + + fsl = FileSystemLoader(dirname(templatename), 'utf-8') + e = Environment() + e.loader = fsl + + templatename = basename(templatename) + rendered = e.get_template(templatename).render(json=json, + dimensions=dimensions) + + return rendered + + diff --git a/jlog/models.py b/jlog/models.py index baaffb5a7..9398ef45e 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -5,12 +5,10 @@ class Log(models.Model): user = models.CharField(max_length=20, null=True) host = models.CharField(max_length=20, null=True) remote_ip = models.CharField(max_length=100) - dept_name = models.CharField(max_length=20) log_path = models.CharField(max_length=100) start_time = models.DateTimeField(null=True) pid = models.IntegerField(max_length=10) is_finished = models.BooleanField(default=False) - handle_finished = models.BooleanField(default=False) end_time = models.DateTimeField(null=True) def __unicode__(self): diff --git a/jlog/urls.py b/jlog/urls.py index 0b6810d3c..24d821f12 100644 --- a/jlog/urls.py +++ b/jlog/urls.py @@ -5,7 +5,7 @@ from jlog.views import * urlpatterns = patterns('', url(r'^$', log_list), url(r'^log_list/(\w+)/$', log_list), - url(r'^log_kill/', log_kill), - url(r'^history/$', log_history), - url(r'^search/$', log_search), + # url(r'^log_kill/', log_kill), + # url(r'^history/$', log_history), + # url(r'^search/$', log_search), ) \ No newline at end of file diff --git a/jlog/views.py b/jlog/views.py index 0eb74f815..187802a7b 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -4,117 +4,103 @@ from django.template import RequestContext from django.shortcuts import render_to_response from jumpserver.api import * -from jasset.views import httperror from django.http import HttpResponseNotFound CONF = ConfigParser() CONF.read('%s/jumpserver.conf' % BASE_DIR) +from jlog.models import Log + +# def get_user_info(request, offset): +# """ 获取用户信息及环境 """ +# env_dic = {'online': 0, 'offline': 1} +# env = env_dic[offset] +# keyword = request.GET.get('keyword', '') +# user_info = get_session_user_info(request) +# user_id, username = user_info[0:2] +# dept_id, dept_name = user_info[3:5] +# ret = [request, keyword, env, username, dept_name] +# +# return ret +# +# +# def get_user_log(ret_list): +# """ 获取不同类型用户日志记录 """ +# request, keyword, env, username, dept_name = ret_list +# post_all = Log.objects.filter(is_finished=env).order_by('-start_time') +# post_keyword_all = Log.objects.filter(Q(user__contains=keyword) | +# Q(host__contains=keyword)) \ +# .filter(is_finished=env).order_by('-start_time') +# +# if keyword: +# posts = post_keyword_all +# else: +# posts = post_all +# +# return posts -def get_user_info(request, offset): - """ 获取用户信息及环境 """ - env_dic = {'online': 0, 'offline': 1} - env = env_dic[offset] - keyword = request.GET.get('keyword', '') - user_info = get_session_user_info(request) - user_id, username = user_info[0:2] - dept_id, dept_name = user_info[3:5] - ret = [request, keyword, env, username, dept_name] - - return ret - - -def get_user_log(ret_list): - """ 获取不同类型用户日志记录 """ - request, keyword, env, username, dept_name = ret_list - post_all = Log.objects.filter(is_finished=env).order_by('-start_time') - post_keyword_all = Log.objects.filter(Q(user__contains=keyword) | - Q(host__contains=keyword)) \ - .filter(is_finished=env).order_by('-start_time') - - if is_super_user(request): - if keyword: - posts = post_keyword_all - else: - posts = post_all - - elif is_group_admin(request): - if keyword: - posts = post_keyword_all.filter(dept_name=dept_name) - else: - posts = post_all.filter(dept_name=dept_name) - - elif is_common_user(request): - if keyword: - posts = post_keyword_all.filter(user=username) - else: - posts = post_all.filter(user=username) - - return posts - - -@require_login def log_list(request, offset): """ 显示日志 """ header_title, path1, path2 = u'查看日志', u'查看日志', u'在线用户' keyword = request.GET.get('keyword', '') web_socket_host = CONF.get('websocket', 'web_socket_host') - posts = get_user_log(get_user_info(request, offset)) + # posts = get_user_log(get_user_info(request, offset)) + if offset == 'online': + posts = Log.objects.filter(is_finished=False).order_by('-start_time') + else: + posts = Log.objects.filter(is_finished=True).order_by('-start_time') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request)) - -@require_admin -def log_kill(request): - """ 杀掉connect进程 """ - pid = request.GET.get('id', '') - log = Log.objects.filter(pid=pid) - if log: - log = log[0] - dept_name = log.dept_name - deptname = get_session_user_info(request)[4] - if is_group_admin(request) and dept_name != deptname: - return httperror(request, u'Kill失败, 您无权操作!') - try: - os.kill(int(pid), 9) - except OSError: - pass - Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.datetime.now()) - return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request)) - else: - return HttpResponseNotFound(u'没有此进程!') - - -@require_login -def log_history(request): - """ 命令历史记录 """ - log_id = request.GET.get('id', 0) - log = Log.objects.filter(id=int(log_id)) - if log: - log = log[0] - dept_name = log.dept_name - deptname = get_session_user_info(request)[4] - if is_group_admin(request) and dept_name != deptname: - return httperror(request, '查看失败, 您无权查看!') - - elif is_common_user(request): - return httperror(request, '查看失败, 您无权查看!') - - log_his = "%s.his" % log.log_path - if os.path.isfile(log_his): - f = open(log_his) - content = f.read() - return HttpResponse(content) - else: - return httperror(request, '无日志记录, 请查看日志处理脚本是否开启!') - - -@require_login -def log_search(request): - """ 日志搜索 """ - offset = request.GET.get('env', '') - keyword = request.GET.get('keyword', '') - posts = get_user_log(get_user_info(request, offset)) - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - return render_to_response('jlog/log_search.html', locals(), context_instance=RequestContext(request)) +# +# def log_kill(request): +# """ 杀掉connect进程 """ +# pid = request.GET.get('id', '') +# log = Log.objects.filter(pid=pid) +# if log: +# log = log[0] +# dept_name = log.dept_name +# deptname = get_session_user_info(request)[4] +# if is_group_admin(request) and dept_name != deptname: +# return httperror(request, u'Kill失败, 您无权操作!') +# try: +# os.kill(int(pid), 9) +# except OSError: +# pass +# Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.datetime.now()) +# return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request)) +# else: +# return HttpResponseNotFound(u'没有此进程!') +# +# +# def log_history(request): +# """ 命令历史记录 """ +# log_id = request.GET.get('id', 0) +# log = Log.objects.filter(id=int(log_id)) +# if log: +# log = log[0] +# dept_name = log.dept_name +# deptname = get_session_user_info(request)[4] +# if is_group_admin(request) and dept_name != deptname: +# return httperror(request, '查看失败, 您无权查看!') +# +# elif is_common_user(request): +# return httperror(request, '查看失败, 您无权查看!') +# +# log_his = "%s.his" % log.log_path +# if os.path.isfile(log_his): +# f = open(log_his) +# content = f.read() +# return HttpResponse(content) +# else: +# return httperror(request, '无日志记录, 请查看日志处理脚本是否开启!') +# +# +# def log_search(request): +# """ 日志搜索 """ +# offset = request.GET.get('env', '') +# keyword = request.GET.get('keyword', '') +# posts = get_user_log(get_user_info(request, offset)) +# contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) +# return render_to_response('jlog/log_search.html', locals(), context_instance=RequestContext(request)) diff --git a/jumpserver/api.py b/jumpserver/api.py index faabb068c..b4ee56595 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -12,6 +12,7 @@ import random import subprocess import paramiko import struct, fcntl, signal,socket, select, fnmatch +import re from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.http import HttpResponse, Http404 @@ -69,75 +70,6 @@ def set_log(level): return logger_f -# class LDAPMgmt(): -# """ -# LDAP class for add, select, del, update -# LDAP 管理类,增删改查 -# """ -# def __init__(self, -# host_url, -# base_dn, -# root_cn, -# root_pw): -# self.ldap_host = host_url -# self.ldap_base_dn = base_dn -# self.conn = ldap.initialize(host_url) -# self.conn.set_option(ldap.OPT_REFERRALS, 0) -# self.conn.protocol_version = ldap.VERSION3 -# self.conn.simple_bind_s(root_cn, root_pw) -# -# def list(self, filter, scope=ldap.SCOPE_SUBTREE, attr=None): -# """ -# query -# 查询 -# """ -# result = {} -# try: -# ldap_result = self.conn.search_s(self.ldap_base_dn, scope, filter, attr) -# for entry in ldap_result: -# name, data = entry -# for k, v in data.items(): -# print '%s: %s' % (k, v) -# result[k] = v -# return result -# except ldap.LDAPError, e: -# print e -# -# def add(self, dn, attrs): -# """ -# add -# 添加 -# """ -# try: -# ldif = modlist.addModlist(attrs) -# self.conn.add_s(dn, ldif) -# except ldap.LDAPError, e: -# print e -# -# def modify(self, dn, attrs): -# """ -# modify -# 更改 -# """ -# try: -# attr_s = [] -# for k, v in attrs.items(): -# attr_s.append((2, k, v)) -# self.conn.modify_s(dn, attr_s) -# except ldap.LDAPError, e: -# print e -# -# def delete(self, dn): -# """ -# delete -# 删除 -# """ -# try: -# self.conn.delete_s(dn) -# except ldap.LDAPError, e: -# print e - - def page_list_return(total, current=1): """ page @@ -181,17 +113,46 @@ def pages(post_objects, request): return post_objects, paginator, page_objects, page_range, current_page, show_first, show_end +def remove_control_char(str_r): + """ + 处理日志特殊字符 + """ + control_char = re.compile(r""" + \x1b[ #%()*+\-.\/]. | + \r | #匹配 回车符(CR) + (?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd + (?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL) + (?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST) + \x1b. #匹配 转义过后的字符 + [\x80-\x9f] #匹配 所有控制字符 + """, re.X) + backspace = re.compile(r"[^\b][\b]") + line_filtered = control_char.sub('', str_r.rstrip()) + while backspace.search(line_filtered): + line_filtered = backspace.sub('', line_filtered) + + return line_filtered + + +def newline_code_in(strings): + for i in ['\r', '\r\n', '\n']: + if i in strings: + #print "new line" + return True + return False + + class Jtty(object): """ A virtual tty class 一个虚拟终端类,实现连接ssh和记录日志 """ - def __init__(self, user, asset): + def __init__(self, username, ip): self.chan = None - self.username = user.username - self.ip = asset.ip - self.user = user - self.asset = asset + self.username = username + self.ip = ip + # self.user = user + # self.asset = asset @staticmethod def get_win_size(): @@ -227,11 +188,8 @@ class Jtty(object): timestamp_start = int(time.time()) date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) - log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start) today_connect_log_dir = os.path.join(tty_log_dir, date_start) - log_file_path = os.path.join(today_connect_log_dir, log_filename) - dept_name = self.user.dept.name - + log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.ip, time_start)) pid = os.getpid() pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') @@ -242,23 +200,29 @@ class Jtty(object): raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) try: - log_file = open(log_file_path, 'a') + log_file_f = open(log_file_path + '.log', 'a') + log_time_f = open(log_file_path + '.time', 'a') + log_res_f = open(log_file_path + '.res', 'a') except IOError: raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name, + log = Log(user=self.username, host=self.ip, remote_ip=ip_list, log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) - log_file.write('Start time is %s\n' % datetime.datetime.now()) + log_file_f.write('Start time is %s\n' % datetime.datetime.now()) log.save() - return log_file, log + return log_file_f, log_time_f, log_res_f, log def posix_shell(self): """ Use paramiko channel connect server interactive. 使用paramiko模块的channel,连接后端,进入交互式 """ - log_file, log = self.log_record() + log_file_f, log_time_f, log_res_f, log = self.log_record() old_tty = termios.tcgetattr(sys.stdin) + pre_timestamp = time.time() + input_r = '' + input_mode = False + try: tty.setraw(sys.stdin.fileno()) tty.setcbreak(sys.stdin.fileno()) @@ -277,23 +241,40 @@ class Jtty(object): break sys.stdout.write(x) sys.stdout.flush() - log_file.write(x) - log_file.flush() + log_file_f.write(x) + now_timestamp = time.time() + log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x))) + pre_timestamp = now_timestamp + log_file_f.flush() + log_time_f.flush() + + if input_mode and not newline_code_in(x): + input_r += x + except socket.timeout: pass if sys.stdin in r: x = os.read(sys.stdin.fileno(), 1) + if not input_mode: + input_mode = True + + if str(x) in ['\r', '\n', '\r\n']: + input_r = remove_control_char(input_r) + log_res_f.write('%s: %s\n' % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), input_r)) + log_res_f.flush() + input_r = '' + input_mode = False + if len(x) == 0: break self.chan.send(x) finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - log_file.write('End time is %s' % datetime.datetime.now()) - log_file.close() + log_file_f.write('End time is %s' % datetime.datetime.now()) + log_file_f.close() log.is_finished = True - log.handle_finished = False log.end_time = datetime.datetime.now() log.save() @@ -302,27 +283,15 @@ class Jtty(object): get args for connect: ip, port, username, passwd 获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码 """ - if not self.asset.is_active: - raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) + # if not self.asset.is_active: + # raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) + # + # if not self.user.is_active: + # raise ServerError('该用户被禁用 User %s is not active.' % self.username) - if not self.user.is_active: - raise ServerError('该用户被禁用 User %s is not active.' % self.username) - - login_type_dict = { - 'L': self.user.ldap_pwd, - } - - if self.asset.login_type in login_type_dict: - password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type]) - return self.username, password, self.ip, int(self.asset.port) - - elif self.asset.login_type == 'M': - username = self.asset.username - password = CRYPTOR.decrypt(self.asset.password) - return username, password, self.ip, int(self.asset.port) - - else: - raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]') + # password = CRYPTOR.decrypt(self.]) + # return self.username, password, self.ip, int(self.asset.port) + return 'root', 'redhat', '127.0.0.1', 22 def get_connection(self): """ @@ -337,7 +306,7 @@ class Jtty(object): ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: - ssh.connect(ip, port=port, username=username, password=password, compress=True) + ssh.connect(ip, port=port, username=username, password=password) except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: raise ServerError('认证错误 Authentication Error.') except socket.error: @@ -351,7 +320,7 @@ class Jtty(object): 连接服务器 """ ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.asset.ip + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip # 发起ssh连接请求 Make a ssh connection ssh = self.get_connection() @@ -706,14 +675,5 @@ def my_render(template, data, request): CRYPTOR = PyCrypt(KEY) -# if LDAP_ENABLE: -# LDAP_HOST_URL = CONF.get('ldap', 'host_url') -# LDAP_BASE_DN = CONF.get('ldap', 'base_dn') -# LDAP_ROOT_DN = CONF.get('ldap', 'root_dn') -# LDAP_ROOT_PW = CONF.get('ldap', 'root_pw') -# ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) -# else: -# ldap_conn = None - log_level = CONF.get('base', 'log') logger = set_log(log_level) \ No newline at end of file diff --git a/jumpserver/settings.py b/jumpserver/settings.py index 0a5a69bee..279320c92 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -62,7 +62,7 @@ INSTALLED_APPS = ( 'juser', 'jasset', 'jperm', - # 'jlog', + 'jlog', ) MIDDLEWARE_CLASSES = ( diff --git a/jumpserver/urls.py b/jumpserver/urls.py index 6c483ea5b..c6d6b4421 100644 --- a/jumpserver/urls.py +++ b/jumpserver/urls.py @@ -16,7 +16,7 @@ urlpatterns = patterns('', (r'^error/$', 'jumpserver.views.httperror'), (r'^juser/', include('juser.urls')), (r'^jasset/', include('jasset.urls')), - # (r'^jlog/', include('jlog.urls')), + (r'^jlog/', include('jlog.urls')), (r'^jperm/', include('jperm.urls')), (r'^node_auth/', 'jumpserver.views.node_auth'), diff --git a/templates/jlog/base.jinja2 b/templates/jlog/base.jinja2 new file mode 100644 index 000000000..926163d1d --- /dev/null +++ b/templates/jlog/base.jinja2 @@ -0,0 +1,111 @@ + + {% block head %}{% endblock %} + + + + + + + + + -5x +5x + + + diff --git a/templates/jlog/dynamic.jinja2 b/templates/jlog/dynamic.jinja2 new file mode 100644 index 000000000..8ddf2a8ac --- /dev/null +++ b/templates/jlog/dynamic.jinja2 @@ -0,0 +1,11 @@ +{% extends "base.jinja2" %} +{% block head %} + + + +{% endblock %} diff --git a/templates/jlog/log_offline.html b/templates/jlog/log_offline.html index 12bfb5af5..b949a0e18 100644 --- a/templates/jlog/log_offline.html +++ b/templates/jlog/log_offline.html @@ -39,12 +39,6 @@ - @@ -77,12 +71,12 @@ 用户名 - 所属部门 登录主机 来源IP {% ifnotequal session_role_id 0 %} 命令统计 {% endifnotequal %} + 回放录像 登录时间 结束时间 @@ -92,12 +86,12 @@ {% for post in contacts.object_list %} {{ post.user }} - {{ post.dept_name }} {{ post.host }} {{ post.remote_ip }} {% ifnotequal session_role_id 0 %} 命令统计 {% endifnotequal %} + 回放 {{ post.start_time|date:"Y-m-d H:i:s"}} {{ post.end_time|date:"Y-m-d H:i:s" }} diff --git a/templates/jlog/log_online.html b/templates/jlog/log_online.html index 7e2858e57..4731c5ff1 100644 --- a/templates/jlog/log_online.html +++ b/templates/jlog/log_online.html @@ -39,12 +39,6 @@ - @@ -77,7 +71,6 @@ 用户名 - 所属部门 登录主机 来源IP {% ifnotequal session_role_id 0 %} @@ -92,7 +85,6 @@ {% for post in contacts.object_list %} {{ post.user }} - {{ post.dept_name }} {{ post.host }} {{ post.remote_ip }} {% ifnotequal session_role_id 0 %} diff --git a/templates/jlog/static.jinja2 b/templates/jlog/static.jinja2 new file mode 100644 index 000000000..6e41b7ee5 --- /dev/null +++ b/templates/jlog/static.jinja2 @@ -0,0 +1,18 @@ +{% extends "base.jinja2" %} +{% block head %} + + + +{% endblock %} From 8e9f22537f98ae5dd4afbd054a1d94f408ff05a7 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Tue, 27 Oct 2015 17:40:53 +0800 Subject: [PATCH 052/385] reuse asset_list for host_list --- templates/jasset/asset_list.html | 2 ++ templates/nav.html | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 6b1812b41..14607f3a4 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -24,7 +24,9 @@
    + {% if session_role_id > 0 %} 添加 + {% endif %}
    - - {% for host_five in new_posts %} - - {% for host in host_five %} - + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    +
    +
    + + + +
    + + +
    +
    +
    + +
    +
    {{ host.ip }}
    + + + + + + + + {# #} + + + + + + + {% for asset in assets %} + + + + + + + {# #} + + + + {% endfor %} - - {% endfor %} -
    + + 主机名 IP地址 IDC 所属主机组 配置信息 操作系统 使用默认管理 操作
    + + {{ asset.hostname|default_if_none:"" }} {{ asset.ip|default_if_none:"" }} {{ asset.idc.name|default_if_none:"" }} {{ asset.group.all|group_str2 }}{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }} + {{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }} {{ asset.use_default_auth|bool2str }} + 详情 + {% ifnotequal session_role_id 0 %} + {% if user.role == 'admin' %} + + 编辑 + 更新 + 删除 + {% endif %} + {% endifnotequal %} +
    - {% ifequal host_count 0 %} - (空) - {% endifequal %} + + +
    +
    + {% if user.role == 'admin' %} + + 修改 + {% endif %} +
    + {% include 'paginator.html' %} +
    +
    From a143797ac910b5056f398ed2678d5054e15a47c8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 22 Nov 2015 17:56:38 +0800 Subject: [PATCH 206/385] =?UTF-8?q?=E4=BF=AE=E6=94=B9gen=5Fresource=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/perm_api.py | 22 +++++++++++++++++--- jumpserver/api.py | 9 ++++++--- templates/jasset/asset_add.html | 1 + templates/jperm/perm_rule_add.html | 32 +++++++++++++++--------------- 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 856c13005..f79040c60 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -132,13 +132,29 @@ def get_group_asset_perm(ob): return perm -def gen_resource(ob, perm=None): +def gen_resource(ob, ex='', perm=None): """ - ob为用户或资产列表或资产queryset + ob为用户或资产列表或资产queryset, 如果同时输入用户和资产,则获取用户在这些资产上的信息 生成MyInventory需要的 resource文件 """ res = [] - if isinstance(ob, User): + if isinstance(ob, User) and isinstance(ex, (list, QuerySet)): + if not perm: + perm = get_group_user_perm(ob) + for asset, asset_info in perm.get('asset').items(): + if asset not in ex: + continue + asset_info = get_asset_info(asset) + info = {'hostname': asset.hostname, 'ip': asset.ip, 'port': asset_info.get('port', 22)} + try: + role = sorted(list(perm.get('asset').get(asset).get('role')))[0] + except IndexError: + continue + info['username'] = role.name + info['password'] = role.password + info['ssh_key'] = get_role_key(ob, role) + res.append(info) + elif isinstance(ob, User): if not perm: perm = get_group_user_perm(ob) diff --git a/jumpserver/api.py b/jumpserver/api.py index 3ffa7566b..cbf7eebc8 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -96,9 +96,12 @@ def get_role_key(user, role): def chown(path, user, group=''): if not group: group = user - uid = pwd.getpwnam(user).pw_uid - gid = pwd.getpwnam(group).pwd_gid - os.chown(path, uid, gid) + try: + uid = pwd.getpwnam(user).pw_uid + gid = pwd.getpwnam(group).pw_gid + os.chown(path, uid, gid) + except KeyError: + pass def page_list_return(total, current=1): diff --git a/templates/jasset/asset_add.html b/templates/jasset/asset_add.html index 7ef74920a..35903a469 100644 --- a/templates/jasset/asset_add.html +++ b/templates/jasset/asset_add.html @@ -48,6 +48,7 @@ {{ af.ip|bootstrap_horizontal }}

    Tips: 如果IP地址不填写, IP默认会设置与主机名一致

    +
    diff --git a/templates/jperm/perm_rule_add.html b/templates/jperm/perm_rule_add.html index 3eef4eb00..a6d0ce530 100644 --- a/templates/jperm/perm_rule_add.html +++ b/templates/jperm/perm_rule_add.html @@ -26,7 +26,7 @@
    -
    + {% if error %}
    {{ error }}
    {% endif %} @@ -34,7 +34,7 @@
    {{ msg }}
    {% endif %}
    - +
    @@ -48,11 +48,11 @@ {% endfor %} + 用户和用户组必选一个
    -
    - +
    + 资产和资产组必选一个
    -
    - +
    + {% for asset in assets %} + + {% endfor %} + +
    +
    +
    + +
    + +
    + +
    + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    + +{% endblock %} +{% block self_head_css_js %} + {% load staticfiles %} + + + + +{% endblock %} +{% block self_footer_js %} + +{% endblock %} \ No newline at end of file diff --git a/templates/upload.html b/templates/upload.html index 88e859a88..fb50a7cfa 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -73,6 +73,11 @@
    + {# @@ -241,6 +242,23 @@ }); }); +{# function update_tips(){#} +{# layer.tips('我是另外一个tips,只不过我长得跟之前那位稍有些不一样。', '吸附元素选择器', {#} +{# tips: [1, '#3595CC'],#} +{# time: 4000#} +{# });#} +{# }#} + + $('#asset_update_all').click(function () { + layer.msg('玩命更新中...', {time: 200000}); + $.ajax({ + type: "post", + url: "/jasset/asset_update_batch/?arg=all", + success: function () { + parent.location.reload(); + } + }); + }); function change_info(){ var args = $("#asset_form").serialize(); From 30fe9f52363257bf4c5fcd64a370f671903d1eb8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 24 Nov 2015 11:58:42 +0800 Subject: [PATCH 226/385] =?UTF-8?q?fix=20websocket=E6=8E=88=E6=9D=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/perm_api.py | 2 +- run_websocket.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 2bd6de632..53bb06591 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -138,7 +138,7 @@ def user_have_perm(user, asset): if asset in user_assets: return user_perm_all.get('asset').get(asset).get('role') else: - return False + return [] def gen_resource(ob, ex='', perm=None): diff --git a/run_websocket.py b/run_websocket.py index 02b132f2d..d4ce8bb74 100644 --- a/run_websocket.py +++ b/run_websocket.py @@ -38,17 +38,17 @@ define("host", default='0.0.0.0', help="run port on", type=str) def require_auth(role='user'): def _deco(func): - def _deco(request, *args, **kwargs): + def _deco2(request, *args, **kwargs): if request.get_cookie('sessionid'): session_key = request.get_cookie('sessionid') else: session_key = request.get_secure_cookie('sessionid') - logger.debug('Websocket: session_key: ' + session_key) - + logger.debug('Websocket: session_key: %s' % session_key) if session_key: session = get_object(Session, session_key=session_key) - if session and datetime.datetime.now() > session.expire_date: + logger.debug('Websocket: session: %s' % session) + if session and datetime.datetime.now() < session.expire_date: user_id = session.get_decoded().get('_auth_user_id') user = get_object(User, id=user_id) if user: @@ -60,6 +60,8 @@ def require_auth(role='user'): logger.debug('Websocket: user [ %s ] is not admin.' % user.username) else: return func(request, *args, **kwargs) + else: + logger.debug('Websocket: session expired: %s' % session_key) request.close() logger.warning('Websocket: Request auth failed.') # asset_id = int(request.get_argument('id', 9999)) @@ -78,7 +80,7 @@ def require_auth(role='user'): # else: # print("No session user.") # request.close() - return _deco + return _deco2 return _deco @@ -244,13 +246,15 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler): def check_origin(self, origin): return True - @require_auth + @require_auth('user') def open(self): + logger.debug('Websocket: Open request') role_name = self.get_argument('role', 'sb') asset_id = self.get_argument('id', 9999) asset = get_object(Asset, id=asset_id) if asset: roles = user_have_perm(self.user, asset) + logger.debug(roles) login_role = '' for role in roles: if role.name == role_name: @@ -267,7 +271,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler): return logger.debug('Websocket: request web terminal Host: %s User: %s Role: %s' % (asset.hostname, self.user.username, login_role.name)) - self.term = WebTty(self.user, self.asset, login_role) + self.term = WebTty(self.user, asset, login_role) self.term.get_connection() self.term.channel = self.term.ssh.invoke_shell(term='xterm') WebTerminalHandler.tasks.append(MyThread(target=self.forward_outbound)) @@ -302,7 +306,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler): self.term.channel.send(data['data']) def on_close(self): - print 'On_close' + logger.debug('Websocket: Close request') if self in WebTerminalHandler.clients: WebTerminalHandler.clients.remove(self) try: From 7cbb7718eb1113c2aea84ccea1506da461252055 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 24 Nov 2015 12:03:38 +0800 Subject: [PATCH 227/385] fix bug --- jperm/utils.py | 2 +- templates/jperm/perm_role_add.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jperm/utils.py b/jperm/utils.py index a6b3608ee..8561eb8f7 100644 --- a/jperm/utils.py +++ b/jperm/utils.py @@ -46,7 +46,7 @@ def gen_keys(): """ key_basename = "key-" + uuid4().hex key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename) - mkdir(key_path_dir, 0755) + mkdir(key_path_dir, mode=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') diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index 243452db0..ef0753982 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -34,7 +34,7 @@
    {{ msg }}
    {% endif %}
    - +
    From 168157bfe12fbd21ecea5e535f7edd5188a062c1 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 24 Nov 2015 13:31:48 +0800 Subject: [PATCH 228/385] fix --- connect.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/connect.py b/connect.py index f4e21fe7b..c486c0199 100644 --- a/connect.py +++ b/connect.py @@ -21,7 +21,7 @@ if django.get_version() != '1.6': django.setup() from django.contrib.sessions.models import Session from jumpserver.api import ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info, get_role -from jumpserver.api import logger, Log, TtyLog, get_role_key +from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm from jumpserver.settings import LOG_DIR from jperm.ansible_api import Command @@ -211,9 +211,16 @@ class Tty(object): 获取需要登陆的主机的信息和映射用户的账号密码 """ asset_info = get_asset_info(self.asset) + role_pass = CRYPTOR.decrypt(self.role.password) + role_key = os.path.join(self.role.key_path, 'id_rsa') self.connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'), 'port': int(asset_info.get('port')), 'role_name': self.role.name, - 'role_pass': self.role.password, 'role_key': self.role.key_path} + 'role_pass': role_pass, 'role_key': role_key} + logger.debug("Connect: Host: %s Port: %s User: %s Pass: %s Key: %s" % (asset_info.get('ip'), + asset_info.get('port'), + self.role.name, + role_pass, + role_key)) return self.connect_info def get_connection(self): @@ -237,7 +244,7 @@ class Tty(object): look_for_keys=False) self.ssh = ssh return ssh - except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: + except (paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException): pass ssh.connect(connect_info.get('ip'), From ec9b00e2554d9f606fde443fbe814bc46ddfa48f Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 24 Nov 2015 13:37:36 +0800 Subject: [PATCH 229/385] fix bug --- connect.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/connect.py b/connect.py index c486c0199..de2e18bdb 100644 --- a/connect.py +++ b/connect.py @@ -211,8 +211,8 @@ class Tty(object): 获取需要登陆的主机的信息和映射用户的账号密码 """ asset_info = get_asset_info(self.asset) + role_key = get_role_key(self.user, self.role) role_pass = CRYPTOR.decrypt(self.role.password) - role_key = os.path.join(self.role.key_path, 'id_rsa') self.connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'), 'port': int(asset_info.get('port')), 'role_name': self.role.name, 'role_pass': role_pass, 'role_key': role_key} @@ -234,7 +234,7 @@ class Tty(object): ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: - role_key = get_role_key(self.user, self.role) + role_key = connect_info.get('role_key') if role_key and os.path.isfile(role_key): try: ssh.connect(connect_info.get('ip'), @@ -245,6 +245,7 @@ class Tty(object): self.ssh = ssh return ssh except (paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException): + logger.warning('Use ssh key %s Failed.' % role_key) pass ssh.connect(connect_info.get('ip'), From 1a63d32fe35cfee34ec354bb50d61b63dd3786d0 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 24 Nov 2015 16:31:06 +0800 Subject: [PATCH 230/385] fix bug --- jlog/views.py | 1 + jumpserver/api.py | 5 ++++- run_websocket.py | 19 +++++++------------ templates/jlog/log_online.html | 18 +++--------------- 4 files changed, 15 insertions(+), 28 deletions(-) diff --git a/jlog/views.py b/jlog/views.py index 472f2c3b7..6e319c808 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -51,6 +51,7 @@ def log_list(request, offset): web_monitor_uri = 'ws://%s/monitor' % WEB_SOCKET_HOST web_kill_uri = 'http://%s/kill' % WEB_SOCKET_HOST + session_id = request.session.session_key return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request)) diff --git a/jumpserver/api.py b/jumpserver/api.py index 7e2a0d0ef..b44d5d4e5 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -54,7 +54,10 @@ def get_asset_info(asset): if default: info['port'] = default.default_port info['username'] = default.default_user - info['password'] = CRYPTOR.decrypt(default.default_password) + try: + info['password'] = CRYPTOR.decrypt(default.default_password) + except ServerError: + pass info['ssh_key'] = default.default_pri_key_path else: info['port'] = asset.port diff --git a/run_websocket.py b/run_websocket.py index d4ce8bb74..168d50ba9 100644 --- a/run_websocket.py +++ b/run_websocket.py @@ -42,7 +42,7 @@ def require_auth(role='user'): if request.get_cookie('sessionid'): session_key = request.get_cookie('sessionid') else: - session_key = request.get_secure_cookie('sessionid') + session_key = request.get_argument('sessionid', '') logger.debug('Websocket: session_key: %s' % session_key) if session_key: @@ -62,7 +62,10 @@ def require_auth(role='user'): return func(request, *args, **kwargs) else: logger.debug('Websocket: session expired: %s' % session_key) - request.close() + try: + request.close() + except AttributeError: + pass logger.warning('Websocket: Request auth failed.') # asset_id = int(request.get_argument('id', 9999)) # print asset_id @@ -99,14 +102,7 @@ class EventHandler(ProcessEvent): def __init__(self, client=None): self.client = client - def process_IN_CREATE(self, event): - print "Create file:%s." % os.path.join(event.path, event.name) - - def process_IN_DELETE(self, event): - print "Delete file:%s." % os.path.join(event.path, event.name) - def process_IN_MODIFY(self, event): - print "Modify file:%s." % os.path.join(event.path, event.name) self.client.write_message(f.read()) @@ -222,12 +218,11 @@ class WebTerminalKillHandler(tornado.web.RequestHandler): ws_id = self.get_argument('id') Log.objects.filter(id=ws_id).update(is_finished=True) for ws in WebTerminalHandler.clients: - print ws.id if ws.id == int(ws_id): - print "killed" + logger.debug("Kill log id %s" % ws_id) ws.log.save() ws.close() - print len(WebTerminalHandler.clients) + logger.debug('Websocket: web terminal client num: %s' % len(WebTerminalHandler.clients)) class WebTerminalHandler(tornado.websocket.WebSocketHandler): diff --git a/templates/jlog/log_online.html b/templates/jlog/log_online.html index 42ea11a38..562c530a4 100644 --- a/templates/jlog/log_online.html +++ b/templates/jlog/log_online.html @@ -50,7 +50,7 @@
    -
    用户日志详细信息列表
    +
    用户日志详细信息列表
    @@ -186,29 +186,17 @@ }); }); -{# function log_search(){#} -{# $.ajax({#} -{# type: "GET",#} -{# url: "/jlog/search/?env=online",#} -{# data: $("#search_form").serialize(),#} -{# success: function (data) {#} -{# $(".tab-content").html(data);#} -{# }#} -{# });#} -{# }#} - - function cut(num, host){ console.log(host); if (host=='Web'){ var g_url = '{{ web_kill_uri }}' + '?id=' + num; } else { - g_url = "/jlog/log_kill/?id=" + num; + var g_url = "/jlog/log_kill/?id=" + num; } $.ajax({ type: "GET", - url: g_url, + url: g_url+"&sessionid={{ session_id }}", success: window.open("/jlog/log_list/online/", "_self") }); From 32d036c2b03d79febe665df3e1b6f0aa82d5171e Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 24 Nov 2015 17:38:27 +0800 Subject: [PATCH 231/385] =?UTF-8?q?=E4=BF=AE=E6=94=B9setting=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/views.py | 6 +++--- jumpserver/api.py | 10 +++++----- jumpserver/models.py | 12 ++++++++---- jumpserver/views.py | 16 ++++++++-------- templates/setting.html | 10 ++++++---- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/jperm/views.py b/jperm/views.py index 1c9eaa15c..0128bfca2 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -412,9 +412,9 @@ def perm_role_push(request): push_resource = [] for asset in calc_assets: if asset.use_default_auth: - username = Setting.default_user - password = Setting.default_password - port = Setting.default_port + username = Setting.field1 + port = Setting.field2 + password = Setting.field3 else: username = asset.username password = asset.password diff --git a/jumpserver/api.py b/jumpserver/api.py index b44d5d4e5..fa53587f2 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -52,15 +52,15 @@ def get_asset_info(asset): info = {'hostname': asset.hostname, 'ip': asset.ip} if asset.use_default_auth: if default: - info['port'] = default.default_port - info['username'] = default.default_user + info['port'] = int(default.field2) + info['username'] = default.field1 try: - info['password'] = CRYPTOR.decrypt(default.default_password) + info['password'] = CRYPTOR.decrypt(default.field3) except ServerError: pass - info['ssh_key'] = default.default_pri_key_path + info['ssh_key'] = default.field4 else: - info['port'] = asset.port + info['port'] = int(asset.port) info['username'] = asset.username info['password'] = CRYPTOR.decrypt(asset.password) diff --git a/jumpserver/models.py b/jumpserver/models.py index e8cb7ea2b..e75921aa4 100644 --- a/jumpserver/models.py +++ b/jumpserver/models.py @@ -5,10 +5,14 @@ from django.db import models class Setting(models.Model): name = models.CharField(max_length=100) - default_user = models.CharField(max_length=100, null=True, blank=True) - default_port = models.IntegerField(null=True, blank=True) - default_password = models.CharField(max_length=100, null=True, blank=True) - default_pri_key_path = models.CharField(max_length=100, null=True, blank=True) + field1 = models.CharField(max_length=100, null=True, blank=True) + field2 = models.CharField(max_length=100, null=True, blank=True) + field3 = models.CharField(max_length=100, null=True, blank=True) + field4 = models.CharField(max_length=100, null=True, blank=True) + field5 = models.CharField(max_length=100, null=True, blank=True) class Meta: db_table = u'setting' + + def __unicode__(self): + return self.name diff --git a/jumpserver/views.py b/jumpserver/views.py index 57ae3f5dd..87cdf0126 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -265,7 +265,7 @@ def setting(request): password = request.POST.get('password', '') private_key = request.POST.get('key', '') - if '' in [username, port] and ('' in password or '' in private_key): + if '' in [username, port]: return HttpResponse('所填内容不能为空, 且密码和私钥填一个') else: private_key_path = os.path.join(BASE_DIR, 'keys/role_keys', 'default', 'default_private_key.pem') @@ -275,19 +275,19 @@ def setting(request): os.chmod(private_key_path, 0600) if setting_default: - if password != setting_default.default_password: + if password: password_encode = CRYPTOR.encrypt(password) else: password_encode = password - Setting.objects.filter(name='default').update(default_user=username, default_port=port, - default_password=password_encode, - default_pri_key_path=private_key_path) + Setting.objects.filter(name='default').update(field1=username, field2=port, + field3=password_encode, + field4=private_key_path) else: password_encode = CRYPTOR.encrypt(password) - setting_r = Setting(name='default', default_user=username, default_port=port, - default_password=password_encode, - default_pri_key_path=private_key_path).save() + setting_r = Setting(name='default', field1=username, field2=port, + field3=password_encode, + field4=private_key_path).save() msg = "设置成功" return my_render('setting.html', locals(), request) diff --git a/templates/setting.html b/templates/setting.html index 7fa097150..de6264d46 100644 --- a/templates/setting.html +++ b/templates/setting.html @@ -48,28 +48,30 @@
    - +
    - +
    - + + 如果不修改密码,请留空
    - + + 如果不修改密钥,请留空
    From 0262d94dd343736a3723e0a9f7d408425d37facb Mon Sep 17 00:00:00 2001 From: wangyong <864072399@qq.com> Date: Tue, 24 Nov 2015 19:04:16 +0800 Subject: [PATCH 232/385] fix disk detail bug --- jumpserver.conf | 2 +- jumpserver/templatetags/mytags.py | 6 +++++- templates/jasset/asset_edit.html | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/jumpserver.conf b/jumpserver.conf index 6297ab00a..41894e0d9 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -13,7 +13,7 @@ password = mysql234 database = jumpserver [websocket] -web_socket_host = j:3000 +web_socket_host = js:3000 [mail] mail_enable = 1 diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index c8f5debde..2759838f2 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -209,7 +209,11 @@ def str_to_dic(info): """ str to list """ - return ast.literal_eval(info).iteritems() + if '{' in info: + info_dic = ast.literal_eval(info).iteritems() + else: + info_dic = {} + return info_dic @register.filter(name='str_to_code') diff --git a/templates/jasset/asset_edit.html b/templates/jasset/asset_edit.html index a3f9bd516..c22bab6ca 100644 --- a/templates/jasset/asset_edit.html +++ b/templates/jasset/asset_edit.html @@ -96,15 +96,15 @@
    {{ af.memory|bootstrap_horizontal }} +
    + {{ af.disk|bootstrap_horizontal }} +
    {{ af.system_type|bootstrap_horizontal }}
    {{ af.system_version|bootstrap_horizontal }} -
    - {{ af.disk|bootstrap_horizontal }} -
    {{ af.number|bootstrap_horizontal }} From 1a0c9cd4e6a9d68be1cded0a64b05a02513b88e8 Mon Sep 17 00:00:00 2001 From: yumaojun <719118794@qq.com> Date: Tue, 24 Nov 2015 22:03:58 +0800 Subject: [PATCH 233/385] =?UTF-8?q?1.=20=E6=96=B0=E5=A2=9E=20PermSudo?= =?UTF-8?q?=E8=A1=A8,=20=E7=94=A8=E4=BA=8E=E8=AE=B0=E5=BD=95=20sudo?= =?UTF-8?q?=E5=88=AB=E5=90=8D=202.=20=E5=AE=9E=E7=8E=B0Sudo=E8=A1=A8?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=9A=84=20=E6=B7=BB=E5=8A=A0=EF=BC=8C?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=EF=BC=8C=E6=9B=B4=E6=96=B0=EF=BC=8C=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E9=A1=B5=E9=9D=A2=203.=20=E6=B7=BB=E5=8A=A0=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E6=97=B6=EF=BC=8C=E9=9C=80=E8=A6=81=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=9A=84=20sudo=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/ansible_api.py | 67 +++++++++----- jperm/models.py | 17 +++- jperm/urls.py | 5 +- jperm/utils.py | 25 +++++ jperm/views.py | 136 +++++++++++++++++++++++++++- jumpserver/templatetags/mytags.py | 9 ++ templates/jperm/perm_role_add.html | 11 +++ templates/jperm/perm_role_edit.html | 11 +++ templates/jperm/perm_role_list.html | 3 + templates/jperm/perm_sudo_add.html | 120 ++++++++++++++++++++++++ templates/jperm/perm_sudo_edit.html | 120 ++++++++++++++++++++++++ templates/jperm/perm_sudo_list.html | 112 +++++++++++++++++++++++ templates/jperm/role_sudo.j2 | 126 ++++++++++++++++++++++++++ templates/nav.html | 4 +- 14 files changed, 737 insertions(+), 29 deletions(-) create mode 100644 templates/jperm/perm_sudo_add.html create mode 100644 templates/jperm/perm_sudo_edit.html create mode 100644 templates/jperm/perm_sudo_list.html create mode 100644 templates/jperm/role_sudo.j2 diff --git a/jperm/ansible_api.py b/jperm/ansible_api.py index 3a8b50c61..ee7c2ddee 100644 --- a/jperm/ansible_api.py +++ b/jperm/ansible_api.py @@ -20,7 +20,6 @@ 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), @@ -44,7 +43,7 @@ class CommandValueError(AnsibleError): super(CommandValueError, self).__init__('value:invalid', field, message) -class MyInventory(object): +class MyInventory(Inventory): """ this is my ansible inventory object. """ @@ -65,7 +64,7 @@ class MyInventory(object): self.inventory = Inventory(host_list=[]) self.gen_inventory() - def add_group(self, hosts, groupname, groupvars=None): + def my_add_group(self, hosts, groupname, groupvars=None): """ add hosts to a group """ @@ -83,11 +82,13 @@ class MyInventory(object): hostport = host.get("port") username = host.get("username") password = host.get("password") + sudo_password = host.get("sudo_password") my_host = Host(name=hostname, port=hostport) my_host.set_variable('ansible_ssh_host', hostname) my_host.set_variable('ansible_ssh_port', hostport) my_host.set_variable('ansible_ssh_user', username) my_host.set_variable('ansible_ssh_pass', password) + # set other variables for key, value in host.iteritems(): if key not in ["hostname", "port", "username", "password"]: @@ -102,10 +103,10 @@ class MyInventory(object): add hosts to inventory. """ if isinstance(self.resource, list): - self.add_group(self.resource, 'default_group') + self.my_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")) + self.my_add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars")) class Command(MyInventory): @@ -125,7 +126,7 @@ class Command(MyInventory): if module_name not in ["raw", "command", "shell"]: raise CommandValueError("module_name", - "module_name must be of the 'raw, command, shell'") + "module_name must be of the 'raw, command, shell'") hoc = Runner(module_name=module_name, module_args=command, timeout=timeout, @@ -136,15 +137,17 @@ class Command(MyInventory): ) self.results = hoc.run() + ret = {} if self.stdout: - return {"ok": self.stdout} + ret["ok"] = self.stdout else: msg = [] if self.stderr: msg.append(self.stderr) if self.dark: msg.append(self.dark) - return {"failed": msg} + ret["failed"] = msg + return ret @property def raw_results(self): @@ -206,7 +209,14 @@ 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='default_group', pattern='*'): + def __run(self, + module_args, + module_name="command", + timeout=5, + forks=10, + group='default_group', + pattern='*', + ): """ run command from andible ad-hoc. command : 必须是一个需要执行的命令字符串, 比如 @@ -219,6 +229,7 @@ class Tasks(Command): subset=group, pattern=pattern, forks=forks, + become=False, ) self.results = hoc.run() @@ -272,7 +283,7 @@ class Tasks(Command): module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path) self.__run(module_args, "authorized_key") - return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"} + return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok"} def add_user(self, username, password): """ @@ -310,7 +321,8 @@ class Tasks(Command): delete a host user. """ module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % (username) - self.__run(module_args, "user") + self.__run(module_args, + "user",) return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"} @@ -386,9 +398,15 @@ class Tasks(Command): "product_sn": setup.get("ansible_product_serial") } - return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok", "result": result} - + return {"failed": self.msg, "ok": result} + def push_sudo(self, role_custo, role_name, role_chosen): + """ + use template to render pushed sudoers file + :return: + """ + module_args = 'src=%s dest=%s owner=root group=root mode=0440' % (username, encrypt_pass) + self.__run(module_args, "template") class CustomAggregateStats(callbacks.AggregateStats): @@ -440,12 +458,12 @@ class MyPlaybook(MyInventory): playbook_path = os.path.join(ANSIBLE_DIR, playbook_relational_path) pb = PlayBook( - playbook = playbook_path, - stats = stats, - callbacks = playbook_cb, - runner_callbacks = runner_cb, - inventory = self.inventory, - extra_vars = extra_vars, + playbook=playbook_path, + stats=stats, + callbacks=playbook_cb, + runner_callbacks=runner_cb, + inventory=self.inventory, + extra_vars=extra_vars, check=False) self.results = pb.run() @@ -475,9 +493,14 @@ if __name__ == "__main__": # }, # } - resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902"}] - command = Command(resource) - print command.run("who") + resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902", + # "ansible_become": "yes", + # "ansible_become_method": "sudo", + # # "ansible_become_user": "root", + # "ansible_become_pass": "yusky0902", + }] + cmd = Command(resource) + print cmd.run('ls') # 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 dc8643b67..019411f9b 100644 --- a/jperm/models.py +++ b/jperm/models.py @@ -19,12 +19,23 @@ class SysUser(models.Model): comment = models.CharField(max_length=100, null=True, blank=True, default='') +class PermSudo(models.Model): + name = models.CharField(max_length=100, unique=True) + date_added = models.DateTimeField(auto_now=True) + commands = models.TextField() + comment = models.CharField(max_length=100, null=True, blank=True, default='') + + def __unicode__(self): + return self.name + + class PermRole(models.Model): name = models.CharField(max_length=100, unique=True) comment = models.CharField(max_length=100, null=True, blank=True, default='') password = models.CharField(max_length=100) key_path = models.CharField(max_length=100) date_added = models.DateTimeField(auto_now=True) + sudo = models.ManyToManyField(PermSudo, related_name='perm_role') def __unicode__(self): return self.name @@ -41,4 +52,8 @@ class PermRule(models.Model): role = models.ManyToManyField(PermRole, related_name='perm_rule') def __unicode__(self): - return self.name \ No newline at end of file + return self.name + + + + diff --git a/jperm/urls.py b/jperm/urls.py index aa80f7f75..e382a2a2c 100644 --- a/jperm/urls.py +++ b/jperm/urls.py @@ -13,7 +13,10 @@ urlpatterns = patterns('jperm.views', (r'^role/perm_role_detail/$', perm_role_detail), (r'^role/perm_role_edit/$', perm_role_edit), (r'^role/perm_role_push/$', perm_role_push), - + (r'^sudo/$', perm_sudo_list), + (r'^sudo/perm_sudo_add/$', perm_sudo_add), + (r'^sudo/perm_sudo_delete/$', perm_sudo_delete), + (r'^sudo/perm_sudo_edit/$', perm_sudo_edit), (r'^log/$', log), (r'^sys_user_add/$', sys_user_add), diff --git a/jperm/utils.py b/jperm/utils.py index 63054b30b..ea68488a8 100644 --- a/jperm/utils.py +++ b/jperm/utils.py @@ -6,6 +6,8 @@ import os.path from paramiko.rsakey import RSAKey from os import chmod, makedirs from uuid import uuid4 +from django.template.loader import get_template +from django.template import Context from jumpserver.settings import KEY_DIR @@ -62,6 +64,29 @@ def gen_keys(): return key_path_dir +def gen_sudo(role_custom, role_name, role_chosen): + """ + 生成sudo file, 仅测试了cenos7 + role_custom: 自定义支持的sudo 命令 格式: 'CMD1, CMD2, CMD3, ...' + role_name: role name + role_chosen: 选择那些sudo的命令别名: +     NETWORKING, SOFTWARE, SERVICES, STORAGE, +     DELEGATING, PROCESSES, LOCATE, DRIVERS + :return: + """ + sudo_file_basename = os.path.join(os.path.dirname(KEY_DIR), 'role_sudo_file') + makedirs(sudo_file_basename) + sudo_file_path = os.path.join(sudo_file_basename, role_name) + + t = get_template('role_sudo.j2') + content = t.render(Context({"role_custom": role_custom, + "role_name": role_name, + "role_chosen": role_chosen, + })) + with open(sudo_file_path, 'w') as f: + f.write(content) + return sudo_file_path + if __name__ == "__main__": diff --git a/jperm/views.py b/jperm/views.py index f961a8e07..7c4f59c82 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -9,7 +9,7 @@ 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 jperm.models import PermRole, PermRule, PermSudo from jumpserver.models import Setting from jperm.utils import updates_dict, gen_keys, get_rand_pass @@ -259,19 +259,24 @@ def perm_role_add(request): if request.method == "GET": default_password = get_rand_pass() + sudos = PermSudo.objects.all() return my_render('jperm/perm_role_add.html', locals(), request) elif request.method == "POST": - # 获取参数: name, comment + # 获取参数: name, comment, sudo name = request.POST.get("role_name") comment = request.POST.get("role_comment") password = request.POST.get("role_password") + sudos_name = request.POST.getlist("sudo_name") + sudos_obj = [PermSudo.objects.get(name=sudo_name) for sudo_name in sudos_name] encrypt_pass = CRYPTOR.encrypt(password) # 生成随机密码,生成秘钥对 key_path = gen_keys() role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) role.save() + role.sudo = sudos_obj + role.save() msg = u"添加角色: %s" % name # 渲染 刷新数据 @@ -350,6 +355,7 @@ def perm_role_edit(request): role_id = request.GET.get("id") role = PermRole.objects.get(id=role_id) role_pass = CRYPTOR.decrypt(role.password) + role_sudos = role.sudo.all() if request.method == "GET": return my_render('jperm/perm_role_edit.html', locals(), request) @@ -359,11 +365,14 @@ def perm_role_edit(request): role_password = request.POST.get("role_password") encrypt_role_pass = CRYPTOR.encrypt(role_password) role_comment = request.POST.get("role_comment") + role_sudo_names = request.POST.getlist("sudo_name") + role_sudos = [PermSudo.objects.get(name=sudo_name) for sudo_name in role_sudo_names] # 写入数据库 role.name = role_name role.password = encrypt_role_pass role.comment = role_comment + role.sudo = role_sudos role.save() msg = u"更新系统角色: %s" % role.name @@ -380,8 +389,6 @@ def perm_role_edit(request): return my_render('jperm/perm_role_list.html', locals(), request) - - @require_role('admin') def perm_role_push(request): """ @@ -461,6 +468,127 @@ def perm_role_push(request): return HttpResponse(u"推送系统角色: %s" % ','.join(role_names)) +@require_role('admin') +def perm_sudo_list(request): + """ + list sudo commands alias + :param request: + :return: + """ + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "查看别名" + + # 获取所有sudo 命令别名 + sudos_list = PermSudo.objects.all() + + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + sudos_list = sudos_list.filter(Q(name=keyword)) + + sudos_list, p, sudos, page_range, current_page, show_first, show_end = pages(sudos_list, request) + + return my_render('jperm/perm_sudo_list.html', locals(), request) + + +@require_role('admin') +def perm_sudo_add(request): + """ + list sudo commands alias + :param request: + :return: + """ + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "添加别名" + + if request.method == "GET": + return my_render('jperm/perm_sudo_add.html', locals(), request) + + elif request.method == "POST": + # 获取参数: name, comment + name = request.POST.get("sudo_name") + comment = request.POST.get("sudo_comment") + commands = request.POST.get("sudo_commands") + + sudo = PermSudo(name=name, comment=comment, commands=commands) + sudo.save() + + msg = u"添加Sudo命令别名: %s" % name + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "查看别名" + # 获取所有sudo 命令别名 + sudos_list = PermSudo.objects.all() + + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + roles_list = sudos_list.filter(Q(name=keyword)) + + sudos_list, p, sudos, page_range, current_page, show_first, show_end = pages(sudos_list, request) + + return my_render('jperm/perm_sudo_list.html', locals(), request) + else: + return HttpResponse(u"不支持该操作") + + +@require_role('admin') +def perm_sudo_edit(request): + """ + list sudo commands alias + :param request: + :return: + """ + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "编辑别名" + + sudo_id = request.GET.get("id") + sudo = PermSudo.objects.get(id=sudo_id) + if request.method == "GET": + return my_render('jperm/perm_sudo_edit.html', locals(), request) + + if request.method == "POST": + name = request.POST.get("sudo_name") + commands = request.POST.get("sudo_commands") + comment = request.POST.get("sudo_comment") + sudo.name = name + sudo.commands = commands + sudo.comment = comment + sudo.save() + + msg = u"更新命令别名: %s" % name + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "查看别名" + # 获取所有sudo 命令别名 + sudos_list = PermSudo.objects.all() + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + sudos_list = sudos_list.filter(Q(name=keyword)) + sudos_list, p, sudos, page_range, current_page, show_first, show_end = pages(sudos_list, request) + return my_render('jperm/perm_sudo_list.html', locals(), request) + + +@require_role('admin') +def perm_sudo_delete(request): + """ + list sudo commands alias + :param request: + :return: + """ + if request.method == "POST": + # 获取参数删除的role对象 + sudo_id = request.POST.get("id") + sudo = PermSudo.objects.get(id=sudo_id) + # 数据库里删除记录 + sudo.delete() + return HttpResponse(u"删除角色: %s" % sudo.name) + else: + return HttpResponse(u"不支持该操作") + + + + + diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 8dcdf377c..e99c30f05 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -226,3 +226,12 @@ def ip_str_to_list(ip_str): ip str to list """ return ip_str.split(',') + + +@register.filter(name='role_contain_which_sudos') +def role_contain_which_sudos(role): + """ + get role sudo commands + """ + sudo_names = [sudo.name for sudo in role.sudo.all()] + return ','.join(sudo_names) diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index 243452db0..1f534ac2a 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -47,6 +47,17 @@
    +
    + +
    + +
    +
    +
    diff --git a/templates/jperm/perm_role_edit.html b/templates/jperm/perm_role_edit.html index 3b5637560..b1a5cb50c 100644 --- a/templates/jperm/perm_role_edit.html +++ b/templates/jperm/perm_role_edit.html @@ -47,6 +47,17 @@
    +
    + +
    + +
    +
    +
    diff --git a/templates/jperm/perm_role_list.html b/templates/jperm/perm_role_list.html index 045012ccd..060af1760 100644 --- a/templates/jperm/perm_role_list.html +++ b/templates/jperm/perm_role_list.html @@ -53,6 +53,7 @@ 名称 备注 创建时间 + sudo别名 操作 @@ -62,6 +63,7 @@ {{ role.name }} {{ role.comment }} {{ role.date_added | date:"Y-m-d H:i:s"}} + {{ role | role_contain_which_sudos }} 详情 编辑 @@ -99,6 +101,7 @@ function remove_role(role_id){ del_row.remove() }, error: function (msg) { + console.log(msg) alert("失败: " + msg) } }); diff --git a/templates/jperm/perm_sudo_add.html b/templates/jperm/perm_sudo_add.html new file mode 100644 index 000000000..8df34a5ce --- /dev/null +++ b/templates/jperm/perm_sudo_add.html @@ -0,0 +1,120 @@ +{% extends 'base.html' %} +{% block self_head_css_js %} + + + +{% endblock %} +{% load mytags %} +{% block content %} + {% include 'nav_cat_bar.html' %} +
    +
    +
    +
    +
    +
    填写基本信息
    + +
    +
    +
    + {% if error %} +
    {{ error }}
    + {% endif %} + {% if msg %} +
    {{ msg }}
    + {% endif %} +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block self_footer_js %} + + + +{% endblock %} + diff --git a/templates/jperm/perm_sudo_edit.html b/templates/jperm/perm_sudo_edit.html new file mode 100644 index 000000000..42621b93a --- /dev/null +++ b/templates/jperm/perm_sudo_edit.html @@ -0,0 +1,120 @@ +{% extends 'base.html' %} +{% block self_head_css_js %} + + + +{% endblock %} +{% load mytags %} +{% block content %} + {% include 'nav_cat_bar.html' %} +
    +
    +
    +
    +
    +
    填写基本信息
    + +
    +
    +
    + {% if error %} +
    {{ error }}
    + {% endif %} + {% if msg %} +
    {{ msg }}
    + {% endif %} +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} +{% block self_footer_js %} + + + +{% endblock %} + diff --git a/templates/jperm/perm_sudo_list.html b/templates/jperm/perm_sudo_list.html new file mode 100644 index 000000000..43f13ec10 --- /dev/null +++ b/templates/jperm/perm_sudo_list.html @@ -0,0 +1,112 @@ +{% extends 'base.html' %} +{% load mytags %} +{% block content %} +{% include 'nav_cat_bar.html' %} + +
    +
    +
    +
    +
    + {% if error %} +
    {{ error }}
    + {% endif %} + {% if msg %} +
    {{ msg }}
    + {% endif %} +
    +
    +
    所有Sudo命令别名
    + +
    + +
    +
    + 添加别名 + 删除所选 + +
    + + + + + + + + + + + + {% for sudo in sudos %} + + + + + + + {% endfor %} + +
    命令别名 系统命令创建时间操作
    {{ sudo.name }} {{ sudo.commands }} {{ sudo.date_added | date:"Y-m-d H:i:s"}} +{# 详情#} + 编辑 + +
    +
    +
    +
    + Showing {{ users.start_index }} to {{ users.end_index }} of {{ p.count }} entries +
    +
    + {% include 'paginator.html' %} +
    +
    +
    +
    +
    +
    + + + + + +{% endblock %} + + diff --git a/templates/jperm/role_sudo.j2 b/templates/jperm/role_sudo.j2 new file mode 100644 index 000000000..ae6e5924c --- /dev/null +++ b/templates/jperm/role_sudo.j2 @@ -0,0 +1,126 @@ +## Sudoers allows particular users to run various commands as +## the root user, without needing the root password. +## +## Examples are provided at the bottom of the file for collections +## of related commands, which can then be delegated out to particular +## users or groups. +## +## This file must be edited with the 'visudo' command. + +## Host Aliases +## Groups of machines. You may prefer to use hostnames (perhaps using +## wildcards for entire domains) or IP addresses instead. +# Host_Alias FILESERVERS = fs1, fs2 +# Host_Alias MAILSERVERS = smtp, smtp2 + +## User Aliases +## These aren't often necessary, as you can use regular groups +## (ie, from files, LDAP, NIS, etc) in this file - just use %groupname +## rather than USERALIAS +# User_Alias ADMINS = jsmith, mikem + + +## Command Aliases +## These are groups of related commands... + +## Networking +Cmnd_Alias NETWORKING = /sbin/route, /sbin/ifconfig, /bin/ping, /sbin/dhclient, /usr/bin/net, /sbin/iptables, /usr/bin/rfcomm, /usr/bin/wvdial, /sbin/iwconfig, /sbin/mii-tool + +## Installation and management of software +Cmnd_Alias SOFTWARE = /bin/rpm, /usr/bin/up2date, /usr/bin/yum + +## Services +Cmnd_Alias SERVICES = /sbin/service, /sbin/chkconfig + +## Updating the locate database +Cmnd_Alias LOCATE = /usr/bin/updatedb + +## Storage +Cmnd_Alias STORAGE = /sbin/fdisk, /sbin/sfdisk, /sbin/parted, /sbin/partprobe, /bin/mount, /bin/umount + +## Delegating permissions +Cmnd_Alias DELEGATING = /bin/chown, /bin/chmod, /bin/chgrp + +## Processes +Cmnd_Alias PROCESSES = /bin/nice, /bin/kill, /usr/bin/kill, /usr/bin/killall + +## Drivers +Cmnd_Alias DRIVERS = /sbin/modprobe + +## Custom +{% if {{ role_custom }} %} +{% Cmnd_Alias CUSTOM = {{ role_custom }} %} +{% endif %} + +# Defaults specification + +# +# Disable "ssh hostname sudo ", because it will show the password in clear. +# You have to run "ssh -t hostname sudo ". +# +Defaults requiretty + +# +# Refuse to run if unable to disable echo on the tty. This setting should also be +# changed in order to be able to use sudo without a tty. See requiretty above. +# +Defaults !visiblepw + +# +# Preserving HOME has security implications since many programs +# use it when searching for configuration files. Note that HOME +# is already set when the the env_reset option is enabled, so +# this option is only effective for configurations where either +# env_reset is disabled or HOME is present in the env_keep list. +# +Defaults always_set_home + +Defaults env_reset +Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS" +Defaults env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE" +Defaults env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES" +Defaults env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE" +Defaults env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY" + +# +# Adding HOME to env_keep may enable a user to run unrestricted +# commands via sudo. +# +# Defaults env_keep += "HOME" + +Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin + +## Next comes the main part: which users can run what software on +## which machines (the sudoers file can be shared between multiple +## systems). +## Syntax: +## +## user MACHINE=COMMANDS +## +## The COMMANDS section may have other options added to it. +## +## Allow root to run any commands anywhere +root ALL=(ALL) ALL + +{{ role_name }} ALL = {{ role_chosen }} + + +## Allows members of the 'sys' group to run networking, software, +## service management apps and more. +# %sys ALL = NETWORKING, SOFTWARE, SERVICES, STORAGE, DELEGATING, PROCESSES, LOCATE, DRIVERS + +## Allows people in group wheel to run all commands +%wheel ALL=(ALL) ALL + +## Same thing without a password +# %wheel ALL=(ALL) NOPASSWD: ALL + +## Allows members of the users group to mount and unmount the +## cdrom as root +# %users ALL=/sbin/mount /mnt/cdrom, /sbin/umount /mnt/cdrom + +## Allows members of the users group to shutdown this system +# %users localhost=/sbin/shutdown -h now + +## Read drop-in files from /etc/sudoers.d (the # here does not mean a comment) +#includedir /etc/sudoers.d diff --git a/templates/nav.html b/templates/nav.html index 3f3e76d42..9ad1fb753 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -32,10 +32,12 @@
  • 授权规则
  • -
  • 系统角色
  • +
  • + Sudo命令 +
  • 权限审批
  • 授权记录
  • From 6fe6342ca4bba651084ff9c3a4f540b8a3d1c9f8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 25 Nov 2015 14:59:57 +0800 Subject: [PATCH 234/385] =?UTF-8?q?=E8=A7=92=E8=89=B2key=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/utils.py | 18 ++---- jperm/views.py | 98 +++++++++++++++-------------- jumpserver/api.py | 13 ++-- static/css/style.css | 4 +- templates/jperm/perm_role_add.html | 81 +++++++++++------------- templates/jperm/perm_role_edit.html | 73 +++++++++------------ templates/jperm/perm_role_list.html | 1 - templates/setting.html | 43 ++++++++++++- 8 files changed, 175 insertions(+), 156 deletions(-) diff --git a/jperm/utils.py b/jperm/utils.py index 8561eb8f7..0d8cf3265 100644 --- a/jperm/utils.py +++ b/jperm/utils.py @@ -6,6 +6,7 @@ import os.path from paramiko.rsakey import RSAKey from jumpserver.api import mkdir from uuid import uuid4 +from jumpserver.api import CRYPTOR from jumpserver.settings import KEY_DIR @@ -14,18 +15,7 @@ def get_rand_pass(): """ get a reandom password. """ - lower = [chr(i) for i in range(97,123)] - upper = [chr(i).upper() for i in range(97,123)] - digit = [str(i) for i in range(10)] - password_pool = [] - password_pool.extend(lower) - password_pool.extend(upper) - password_pool.extend(digit) - pass_list = [random.choice(password_pool) for i in range(1,14)] - pass_list.insert(random.choice(range(1,14)), '@') - pass_list.insert(random.choice(range(1,14)), random.choice(digit)) - password = ''.join(pass_list) - return password + CRYPTOR.gen_rand_pass(20) def updates_dict(*args): @@ -38,7 +28,7 @@ def updates_dict(*args): return result -def gen_keys(): +def gen_keys(gen=True): """ 在KEY_DIR下创建一个 uuid命名的目录, 并且在该目录下 生产一对秘钥 @@ -47,6 +37,8 @@ def gen_keys(): key_basename = "key-" + uuid4().hex key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename) mkdir(key_path_dir, mode=0755) + if not gen: + return key_path_dir 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') diff --git a/jperm/views.py b/jperm/views.py index 0128bfca2..f550f8e13 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -255,36 +255,41 @@ def perm_role_add(request): # 渲染数据 header_title, path1, path2 = "系统角色", "角色管理", "添加角色" - if request.method == "GET": - default_password = get_rand_pass() - return my_render('jperm/perm_role_add.html', locals(), request) - - elif request.method == "POST": + if request.method == "POST": # 获取参数: name, comment - name = request.POST.get("role_name") - comment = request.POST.get("role_comment") - password = request.POST.get("role_password") - encrypt_pass = CRYPTOR.encrypt(password) - # 生成随机密码,生成秘钥对 + name = request.POST.get("role_name", "") + comment = request.POST.get("role_comment", "") + password = request.POST.get("role_password", "") + key_content = request.POST.get("role_key", "") + try: + if get_object(PermRole, name=name): + raise ServerError('已经存在该用户 %s' % name) - key_path = gen_keys() - role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) - role.save() - - 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) + if '' == password and '' == key_content: + raise ServerError('账号和密码必填一项') + if password: + encrypt_pass = CRYPTOR.encrypt(password) + else: + encrypt_pass = CRYPTOR.encrypt(CRYPTOR.gen_rand_pass(20)) + # 生成随机密码,生成秘钥对 + if key_content: + key_path = gen_keys(gen=False) + with open(os.path.join(key_path, 'id_rsa'), 'w') as f: + f.write(key_content) + else: + key_path = gen_keys() + logger.debug('generate role key: %s' % key_path) + role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) + role.save() + msg = u"添加角色: %s" % name + return HttpResponseRedirect('/perm/role/') + except ServerError, e: + error = e else: return HttpResponse(u"不支持该操作") + return my_render('jperm/perm_role_add.html', locals(), request) + @require_role('admin') def perm_role_delete(request): @@ -346,36 +351,37 @@ def perm_role_edit(request): # 渲染数据 role_id = request.GET.get("id") - role = PermRole.objects.get(id=role_id) - role_pass = CRYPTOR.decrypt(role.password) - if request.method == "GET": - return my_render('jperm/perm_role_edit.html', locals(), request) + role = get_object(PermRole, id=role_id) if request.method == "POST": # 获取 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") + key_content = request.POST.get("role_key", "") + try: + if not role: + raise ServerError('角色用户不能存在') - # 写入数据库 - role.name = role_name - role.password = encrypt_role_pass - role.comment = role_comment + if role_password: + encrypt_pass = CRYPTOR.encrypt(role_password) + role.password = encrypt_pass + # 生成随机密码,生成秘钥对 + if key_content: + with open(os.path.join(role.key_path, 'id_rsa'), 'w') as f: + f.write(key_content) + logger.debug('Recreate role key: %s' % role.key_path) + # 写入数据库 + role.name = role_name + role.comment = role_comment - role.save() - msg = u"更新系统角色: %s" % role.name + role.save() + msg = u"更新系统角色: %s" % role.name + return HttpResponseRedirect('/jperm/role/') + except ServerError, e: + error = e - # 渲染 刷新数据 - 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) + return my_render('jperm/perm_role_edit.html', locals(), request) @require_role('admin') diff --git a/jumpserver/api.py b/jumpserver/api.py index fa53587f2..d36b306d2 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -48,6 +48,9 @@ def set_log(level): def get_asset_info(asset): + """ + 获取资产的相关账号端口信息 + """ default = get_object(Setting, name='default') info = {'hostname': asset.hostname, 'ip': asset.ip} if asset.use_default_auth: @@ -68,6 +71,9 @@ def get_asset_info(asset): def get_role(user, asset): + """ + 获取用户在这个资产上的授权角色列表 + """ roles = [] rules = PermRule.objects.filter(user=user, asset=asset) for rule in rules: @@ -77,20 +83,19 @@ def get_role(user, asset): def get_role_key(user, role): """ - 由于role的key的权限是所有人可以读的, ansible要求为600,所以拷贝一份到特殊目录 + 由于role的key的权限是所有人可以读的, ansible执行命令等要求为600,所以拷贝一份到特殊目录 :param user: :param role: :return: self key path """ user_role_key_dir = os.path.join(KEY_DIR, 'user') user_role_key_path = os.path.join(user_role_key_dir, '%s_%s.pem' % (user.username, role.name)) - mkdir(user_role_key_dir, mode=777) + mkdir(user_role_key_dir, mode=0777) if not os.path.isfile(user_role_key_path): with open(os.path.join(role.key_path, 'id_rsa')) as fk: with open(user_role_key_path, 'w') as fu: fu.write(fk.read()) - - print user_role_key_path, user.username + logger.debug("创建新的用户角色key %s" % user_role_key_path) chown(user_role_key_path, user.username) os.chmod(user_role_key_path, 0600) return user_role_key_path diff --git a/static/css/style.css b/static/css/style.css index 1257e3571..b8a15d909 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -4566,4 +4566,6 @@ body.skin-3 { .form-group.required .control-label:after { content: " *"; color: red; -} \ No newline at end of file +} + +.n-invalid {border: 1px solid #f00;} diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index ef0753982..b50c2a778 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -26,7 +26,7 @@
    -
    + {% if error %}
    {{ error }}
    {% endif %} @@ -41,9 +41,18 @@
    - +
    - + + 如果不添加密码,会自动生成 +
    +
    +
    +
    + +
    + + 如果不添加密钥,会自动生成, 密码密钥必填一项
    @@ -69,52 +78,34 @@ {% endblock %} {% block self_footer_js %} - - {% endblock %} diff --git a/templates/jperm/perm_role_edit.html b/templates/jperm/perm_role_edit.html index 3b5637560..ba3ec94a1 100644 --- a/templates/jperm/perm_role_edit.html +++ b/templates/jperm/perm_role_edit.html @@ -26,7 +26,7 @@
    - + {% if error %}
    {{ error }}
    {% endif %} @@ -41,9 +41,18 @@
    - +
    - + + 不修改请留空 +
    +
    +
    +
    + +
    + + 不修改请留空
    @@ -69,49 +78,25 @@ {% endblock %} {% block self_footer_js %} diff --git a/templates/jperm/perm_role_list.html b/templates/jperm/perm_role_list.html index 045012ccd..c3deffa2d 100644 --- a/templates/jperm/perm_role_list.html +++ b/templates/jperm/perm_role_list.html @@ -34,7 +34,6 @@
    添加角色 推送角色 - 删除所选
    diff --git a/templates/setting.html b/templates/setting.html index de6264d46..6324b5a89 100644 --- a/templates/setting.html +++ b/templates/setting.html @@ -37,7 +37,7 @@
    - + {% if error %}
    {{ error }}
    {% endif %} @@ -71,7 +71,7 @@
    - 如果不修改密钥,请留空 + 如果不修改密钥,请留空, 密钥密码必填一项
    @@ -101,4 +101,43 @@
    +{% endblock %} +{% block self_footer_js %} + {% endblock %} \ No newline at end of file From f760df1e340e28cb063de7758071d9a2d43b21a4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 25 Nov 2015 16:01:07 +0800 Subject: [PATCH 235/385] =?UTF-8?q?=E8=A7=92=E8=89=B2key=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/utils.py | 28 ++++++++++++++++++++-------- jperm/views.py | 18 ++++++++++-------- templates/jperm/perm_role_add.html | 1 - 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/jperm/utils.py b/jperm/utils.py index 0d8cf3265..a894f4453 100644 --- a/jperm/utils.py +++ b/jperm/utils.py @@ -2,7 +2,8 @@ import random import os.path - +import shutil +from paramiko import SSHException from paramiko.rsakey import RSAKey from jumpserver.api import mkdir from uuid import uuid4 @@ -28,21 +29,32 @@ def updates_dict(*args): return result -def gen_keys(gen=True): +def gen_keys(key="", key_path_dir=""): """ 在KEY_DIR下创建一个 uuid命名的目录, 并且在该目录下 生产一对秘钥 :return: 返回目录名(uuid) """ key_basename = "key-" + uuid4().hex - key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename) - mkdir(key_path_dir, mode=0755) - if not gen: - return key_path_dir - key = RSAKey.generate(2048) + if not key_path_dir: + key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename) private_key = os.path.join(key_path_dir, 'id_rsa') public_key = os.path.join(key_path_dir, 'id_rsa.pub') - key.write_private_key_file(private_key) + mkdir(key_path_dir, mode=0755) + if not key: + key = RSAKey.generate(2048) + key.write_private_key_file(private_key) + else: + key_file = os.path.join(key_path_dir, 'id_rsa') + with open(key_file, 'w') as f: + f.write(key) + f.close() + with open(key_file) as f: + try: + key = RSAKey.from_private_key(f) + except SSHException: + shutil.rmtree(key_path_dir, ignore_errors=True) + raise SSHException os.chmod(private_key, 0644) with open(public_key, 'w') as content_file: diff --git a/jperm/views.py b/jperm/views.py index f550f8e13..3873dd974 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from django.db.models import Q +from paramiko import SSHException from jperm.perm_api import * from juser.user_api import gen_ssh_key @@ -273,20 +274,19 @@ def perm_role_add(request): encrypt_pass = CRYPTOR.encrypt(CRYPTOR.gen_rand_pass(20)) # 生成随机密码,生成秘钥对 if key_content: - key_path = gen_keys(gen=False) - with open(os.path.join(key_path, 'id_rsa'), 'w') as f: - f.write(key_content) + try: + key_path = gen_keys(key=key_content) + except SSHException: + raise ServerError('输入的密钥不合法') else: key_path = gen_keys() logger.debug('generate role key: %s' % key_path) role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) role.save() msg = u"添加角色: %s" % name - return HttpResponseRedirect('/perm/role/') + return HttpResponseRedirect('/jperm/role/') except ServerError, e: error = e - else: - return HttpResponse(u"不支持该操作") return my_render('jperm/perm_role_add.html', locals(), request) @@ -368,8 +368,10 @@ def perm_role_edit(request): role.password = encrypt_pass # 生成随机密码,生成秘钥对 if key_content: - with open(os.path.join(role.key_path, 'id_rsa'), 'w') as f: - f.write(key_content) + try: + key_path = gen_keys(key=key_content, key_path_dir=role.key_path) + except SSHException: + raise ServerError('输入的密钥不合法') logger.debug('Recreate role key: %s' % role.key_path) # 写入数据库 role.name = role_name diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index b50c2a778..75e07e35a 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -47,7 +47,6 @@ 如果不添加密码,会自动生成
    -
    From 0335bc26ca0fc7dec25ef05bbe887a6024af4775 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 25 Nov 2015 16:37:17 +0800 Subject: [PATCH 236/385] fix rule js bug --- templates/jperm/perm_rule_add.html | 78 ++++++++++++++++++------------ 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/templates/jperm/perm_rule_add.html b/templates/jperm/perm_rule_add.html index a6d0ce530..346d444a1 100644 --- a/templates/jperm/perm_rule_add.html +++ b/templates/jperm/perm_rule_add.html @@ -41,9 +41,9 @@
    - +
    - {% for user in users %} {% endfor %} @@ -54,7 +54,7 @@
    - {% for user_group in user_groups %} {% endfor %} @@ -63,9 +63,9 @@
    - +
    - {% for asset in assets %} {% endfor %} @@ -74,7 +74,7 @@
    - +
    to - +
    @@ -89,7 +86,6 @@
    -
    @@ -116,7 +112,7 @@ {% for post in contacts.object_list %} - {{ post.id }} + {{ post.id }} {{ post.user }} {{ post.host }} {{ post.remote_ip }} @@ -201,8 +197,8 @@ {# }#} {# });#} - $('#data_5 .input-daterange').datepicker({ - dateFormat: 'yy-mm-dd', + $('#date_5 .input-daterange').datepicker({ + dateFormat: 'mm/dd/yy', keyboardNavigation: false, forceParse: false, autoclose: true diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index 75e07e35a..4fb774a41 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% block self_head_css_js %} - + {% endblock %} diff --git a/templates/jperm/perm_role_edit.html b/templates/jperm/perm_role_edit.html index ba3ec94a1..11311343a 100644 --- a/templates/jperm/perm_role_edit.html +++ b/templates/jperm/perm_role_edit.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% block self_head_css_js %} - + {% endblock %} diff --git a/templates/jperm/perm_role_push.html b/templates/jperm/perm_role_push.html index 91a4c7a0d..fe35b7d86 100644 --- a/templates/jperm/perm_role_push.html +++ b/templates/jperm/perm_role_push.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% block self_head_css_js %} - + {% endblock %} diff --git a/templates/jperm/perm_rule_add.html b/templates/jperm/perm_rule_add.html index fb408cfc7..7dcadbd3d 100644 --- a/templates/jperm/perm_rule_add.html +++ b/templates/jperm/perm_rule_add.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% block self_head_css_js %} - + {% endblock %} diff --git a/templates/jperm/perm_rule_edit.html b/templates/jperm/perm_rule_edit.html index 474f90633..3d53cad25 100644 --- a/templates/jperm/perm_rule_edit.html +++ b/templates/jperm/perm_rule_edit.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% block self_head_css_js %} - + {% endblock %} diff --git a/templates/juser/run_command.html b/templates/juser/run_command.html index f075f9c90..a86e92e9b 100644 --- a/templates/juser/run_command.html +++ b/templates/juser/run_command.html @@ -69,7 +69,7 @@ {% block self_head_css_js %} {% load staticfiles %} - + {% endblock %} From 09dcdfa3180a6dc1e5d327479e9262ac378d2269 Mon Sep 17 00:00:00 2001 From: halcyon <864072399@qq.com> Date: Thu, 26 Nov 2015 23:42:58 +0800 Subject: [PATCH 252/385] fix some bugs and hehe --- jasset/asset_api.py | 27 ------------------ jasset/models.py | 10 +++++-- jasset/views.py | 16 +++++++++-- static/files/excels/asset.xlsx | Bin 24181 -> 0 bytes .../excels/cmdb_excel_2015_11_20_00_04.xlsx | Bin 6125 -> 0 bytes templates/jasset/asset_add.html | 8 ++---- templates/jasset/asset_edit.html | 18 ++++++------ templates/jasset/asset_list.html | 4 +-- 8 files changed, 33 insertions(+), 50 deletions(-) delete mode 100644 static/files/excels/asset.xlsx delete mode 100644 static/files/excels/cmdb_excel_2015_11_20_00_04.xlsx diff --git a/jasset/asset_api.py b/jasset/asset_api.py index 9cc375509..0034f6495 100644 --- a/jasset/asset_api.py +++ b/jasset/asset_api.py @@ -69,33 +69,6 @@ def db_asset_add(**kwargs): asset.group = group_select -# -# def get_host_groups(groups): -# """ 获取主机所属的组类 """ -# ret = [] -# for group_id in groups: -# group = BisGroup.objects.filter(id=group_id) -# if group: -# group = group[0] -# ret.append(group) -# group_all = get_object_or_404(BisGroup, name='ALL') -# ret.append(group_all) -# return ret -# -# -# # def get_host_depts(depts): -# # """ 获取主机所属的部门类 """ -# # ret = [] -# # for dept_id in depts: -# # dept = DEPT.objects.filter(id=dept_id) -# # if dept: -# # dept = dept[0] -# # ret.append(dept) -# # return ret -# -# - - def db_asset_update(**kwargs): """ 修改主机时数据库操作函数 """ asset_id = kwargs.pop('id') diff --git a/jasset/models.py b/jasset/models.py index c6cbe4cb5..b3765fbe4 100644 --- a/jasset/models.py +++ b/jasset/models.py @@ -16,9 +16,13 @@ ASSET_STATUS = ( ) ASSET_TYPE = ( - (1, u"服务器"), - (2, u"网络设备"), - (3, u"其他") + (1, u"物理机"), + (2, u"虚拟机"), + (3, u"交换机"), + (4, u"路由器"), + (5, u"防火墙"), + (6, u"Docker"), + (7, u"其他") ) diff --git a/jasset/views.py b/jasset/views.py index 38a7d2a3a..dff5621d8 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -128,9 +128,10 @@ def asset_add(request): header_title, path1, path2 = u'添加资产', u'资产管理', u'添加资产' asset_group_all = AssetGroup.objects.all() af = AssetForm() + default_setting = get_object(Setting, name='default') + default_port = default_setting.field2 if default_setting else '' if request.method == 'POST': af_post = AssetForm(request.POST) - print af_post ip = request.POST.get('ip', '') hostname = request.POST.get('hostname', '') is_active = True if request.POST.get('is_active') == '1' else False @@ -147,7 +148,7 @@ def asset_add(request): asset_save = af_post.save(commit=False) if not use_default_auth: password = request.POST.get('password', '') - password_encode = password + password_encode = CRYPTOR.encrypt(password) asset_save.password = password_encode if not ip: asset_save.ip = hostname @@ -227,9 +228,11 @@ def asset_edit(request): af_save.password = '' af_save.port = None else: - if password_old != password: + if password: password_encode = CRYPTOR.encrypt(password) af_save.password = password_encode + else: + af_save.password = password_old af_save.is_active = True if is_active else False af_save.save() af_post.save_m2m() @@ -266,6 +269,7 @@ def asset_list(request): export = request.GET.get("export", False) group_id = request.GET.get("group_id", '') idc_id = request.GET.get("idc_id", '') + asset_id_all = request.GET.getlist("id", '') if group_id: group = get_object(AssetGroup, id=group_id) if group: @@ -302,6 +306,12 @@ def asset_list(request): Q(disk__contains=keyword)) if export: + if asset_id_all: + asset_find = [] + for asset_id in asset_id_all: + asset = get_object(Asset, id=asset_id) + if asset: + asset_find.append(asset) s = write_excel(asset_find) if s[0]: file_name = s[1] diff --git a/static/files/excels/asset.xlsx b/static/files/excels/asset.xlsx deleted file mode 100644 index 1b92db891d85cb5736e2ed99e166078360ea90d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24181 zcmeIa2|QG9-#>mV*%@0V#3XBpsceM|$(|@>Un*paNyx-7_B9kmktKTx*&<|LDqqEi7hu6_=R81L%`@|hXs@&HEY`u{!t z2hYIs`+es+kMinm8B%pWDHwmO)@D;#jp*e+d|s=qE3Io3dHdrq?UX=eeN_#o42R?Qli{P_90ZTtv$Ekm2o11$p-!_2-+;I2rly{T-@UZt>q^T^-Jn=#D zBq`=)pw#{M)jKn<-am8c2rfU&$9_NX{A?Cno1{&gdgtW-L(N9#kPOwPzbReU+0p zNpaYA-;QBXs`}ybrKSY>J zM6suOG3_-z!#Zqc^=kq$p}^Nij89&-KcwQ%&;MoS|6n@))zF`(wi|aH<&9a3 zSmV8kFK$YRyWR-r+myM0xUBl<<)rGPyDoJ*oBA-%CxY##&RzR9ur)t$OKmFr^_94o z$Dfw-KXGLr^tPA(;k8@aIDbUuh6u||xgWxk_)8O3)A0QVKATi2X;nWb=6`Z9rs>3$ zs@L*zep|R%^5n9_=i{{hPzQyA`EsACT;&)~Meo%^{ z{E6XAfko`|IWzbpzE7XQ*h{4BbME+)pH~$RrGNH1I+l49FCq3u;+uz9FaO(D77me< z&)GYFTqZLgH#ymAz;gSR?*(MnufbAyvCn7vC~q@- z0ma)qecz=|h07#-nbkATJ+jv5y|SKN)DLr`!>N4h_@?}PWpBkpLC0Q6ooZ65Df@h> zOe$z@oV`G4e~RRthHJ&V+xH36YfVG@CFh?imMUkufgNHk@9?d5sy#Wdp2vKEdj?^6 z$5SWHS=wqe$+TPL9U6;~FIvzX@mNw$oQ_3$LY%s9?P=EO$>HN`_W6v$^ zcf`AH8lAeoW_uK zwIn426pP$2{6_+M|Gg7m56$-#+@)4zhz&j4K7A=e@2b?(@6)F-jxHFMCmtYz^<{1XA?9DNngmU;hTb>fhVvxow>|DM|t$dJ_;6x&%F zgNFdAYEItKKv+x&Ow=a*yOkV&!1kFJmOyWB5q zdh7ncr{KJ1mQ=x{e@5X*K(I+sJz;_Z>B_r#-M+G0Rn^Zwq6g0n3n&}$ zyXz=i9x@jeZjt}-kI|QE+^DYvp^py9Gr>Qg&(HnJO;;BS|C=5@Zhk-IRr95G&uMMJ zX81PjDpGyS>uvn9kkLtIj#obhL)$E;te=%TTx#-k&b=Tfagw=2Ia-L{(Bn{eShlW& z^tpwBGnO~sA^nSz61i{rD%?xEyJ@6^o^8HYRZXbBuXe&kTv}$(*fsC+Rf}FpZh>YD zZRI?-+#N|`o{;nhX769x5;n-;?fa~{nxY=Heb&TqNYps2NLuDI$Pit!W3-4w)$7_q zQTtB*(*1Bpp){lK(t>0P&|S<&V65l3ABU-wY~J1qcRf0AyrIrA1$K_qY07l7k8Qc4 z!!c7EuH0S3ILy&&p;3{Mt$Y5maJYQK&4G&8ck0sEem*vB!m75i_QGN5KyD^?;iG!@ z4Br}|zJ?|?xe1s*J;-RBX3lreGfn?lqB}#_PH*RG4F}qBI$NbQx1v4ZM- z_c;Fx1#&NB_Z`g>4N|)Np%6@0`RXAc;oySDd%gO_Esfo9r^_nlH211g?%bIHOqG*pAK$M(zPZiqm8)O# zCKwd(HoHUTy*om=wIWY`7WcB+ftKZJWLp-;@Tl3VJyQB`=UedoK z5-XRzsN%T0-jB!QK2taPV3j$cS!#{(^Xj1mcH=C0^+gCr>=t#vDYq4~xzU%KYrC>_ z-2Yi;uT=ZTSJBAFO-I7t+laK4OlFPk>y6u|V$Stap*VIFAz#2F+`CHf^p2w3$w(e<4r~W>1O^2&sQnJU##NrS5$Z330_Q zdF5T#j^C-$`Oc&FR9orsRl*ze3YW9VnmF%OaO2?_Vbb-(k6F7GC9ej&efU@;{-UM2 z6nmGeha}q*RjeNtzo_K7yaD898SdK`XZt4Qn94^?m=L41YMS$X^BIDHVV7#EY~PV# zUtPrm!%Al#DLuaY-aEj@ZO}Da57eU3}SQMHP0#*5RF!T;MBXE3ZwL`P2|&xl?>52`LS>b$$=;%Sg&yoMx{p zu}2fW9McpMWoAfvg(kc5GQr=&-yK;6l)r#3=Mor?0%C%H?TfBYW598m@-i zLBo5?hRlcE>+lB8i{h@kGnoby8wE?ZXLb3kE6J*( z7I~+{Vn6$-72EiAe;Nlzu2=L%*)!VaWt%jG10Q86pE^z!C9Wt&2!6K4PNUQ7MwQaC)Z0{mjvd8#;Nn4}KujZ|uLa(KY>^YK~U&`pjHqYxS(lDo}j8 zoLM7;jd(2VJAThz`)K~J`;RjxwosW*W^nI2m~y5{KT6i^-o1*QhA&&qF9@*@Y^M>H z&Baz_wY!cr!{2wWg)B^XMTU~t&mK8=ZYeYre>}oMuJMSE!^I!t{U+7w?oC2fh3`iD zjW6+8sUbIFR=>C&L0j6#hAn9Fh!mWw8G#+nzJ|U?VcES^B*3!!?f{!zFgna ziIY~BD<`;KU;a?Rym9n7M#pp*9e7xwa%kapxnuCcMvh?LzJ=S0h-hTn>x2m!>K~ft zvvb>BZy*E74$@!Te>9N%Z(n!)qh-|ZDSC{z1-{L5I?&C#_uSQ*rYFhHY=!2A9IxIr zP1Ti#3K^XXvuV?SxuN4fUc?}LA>#!UnHs9*ddL!H`WI&Ivy1ARg|{JQog%AsoGoOCU303$mVgdx%%Zp zmT)7>i@Y@j9EY#STG;Y(7Qbh*a5ug{N@a${?9v<$u%&&FJgfIj_CUmf+H!t|+YE| zZ6tqPx@t!guzK!c%e|#?`vwKx7x4+Ye$k5TtOLBPz7ORpl&kZ&1XXW{8!>P2`5pew zsbBd0+L@z{w_DBZ`MkBAQ|dHs>eO|ZR$p$LILT!780i}fernBLSMJZsbwcB?O*>S| z*PRmE)SX{8)ulWYS>7CdZ}&R(ob8P+iS?Na3h>K$*Fqfk9(W!lzjS4(K{nsd^jZ6j zk49`uOr#r6{W&D77Hhqp1$Il6Jla#I(r5M6>k>n&iL1iL+!JSo-#(D~xZ#{ObR_KD z>60iwXTN(hor+3Q6IUfxj6|;N)1G+S&mX63k}}U7i`V=9LCjiq9N(FL+>3(CI2E-m zj(payUnqz9EYmR)fPOu9BqJioYJX}{yrprCJX=6bYMa*Bht~$_(qbtjyKCGYd6Prm za#G7>-XvK?p(`rUt=KRfZ_Ja=nZA9WBo8xQv-Q~-4xH#{j4k|X9n4WHTO@4b7d-2u zk7)`uvUn+!rnEg?nIP$I4g3TG>+S(Ep7I=$$xlI&aY+zINJE{9c;9#!Ux_*=Y z$E-VU-)AxY%2@;OG};T?ddv5U-Oe)0)|ML$;Td%ZWF7PXyGrhITW` ziB45Z@Li|xB|7~>$hVDYaSuR{=Ypd3`~Cp=pB|~7yQ{0eAM)3)Uv0MgmrVSoj`Fqy z7Sr2F!Bi=pD`96DCHkKzWor$bPOoA)WpAVnZaKVF<$ei!zsKmyG&-==q5KOB{rI*) zzVd^sgkGIb!DlW%vZ%5HTSI+(id#y!`A=TGa5ow!GljDbr75F~>L#QmdYD+Az(YOG zs&MKinRMLaanWO@S{QcU3on?8ZoI(!`F&i?(fIr$pW--LuRVNvY=5@k}~ znq0@-a4^6~ex}-2GbUNG4s}t@`WuJb%lqg3ZWXrlQdRl{l?`YE7SI>>;)y}^!8;1n04%FCb@}m!Qw?lLt=Tm@oTybb`rY(F zQ_IxdC$A2*AF9q{3q{8>3wUtRu?t!zYCM?y2s7p}OBl&}CBU zoxa6c5e{p+1Svz55KGjue^{Mqw?j4GVV9&s&&zkfg0D|R9|v38ZA}_Hh{7yixVE_7 zD(I`+<-Ak=m3bn-%VqFp#izHCJ3J~Ad#gtzb96K#lbFR^o}>sZAIWQ1_TTXp?(Zy+ zM)R75oWUkpb06@jlvvg(jKqAEO!Z1OMci|gbli@6yN5601UxzqZy@hdHNi0xAn3_w z`MOYG;~6?9pmtHRIJs^S6Ovg(aZSZj8aO z{`tm2Ia23RN1CTx%y~P*D=YhATaM0i6hFBue<(9>lBa`X^^H~Gt&8p{_d2+38jd~7 z={OGrOUi2mtzE9TUKFk~m1B$vV7GrF7Y1-1G7WxV_5E8ik`Be-{^YZ8Kyd$gNC3XR zcTQPr0Km`xaHzxu?QDJ%eZvp(SpvMT`dsn2uIPEg)s6mvJ_qm`>l^6<3{X3U&_94q z0K)Vz9@hZC)D$=j)nx;i8AJdm(Ee+M^y@X}pzogt z)Y`9q|KXb{kKvD3L$}If{L|G8KpxYd&VhSBtI=116Xxz{f3%-F`o;kz#iPK9(?+Ju zKU;^6U(cm~J>?x0yEB;rR5LGzu|He^PlwXm06sS09&{fDK7f&rfr*cS-UT3_UhHA` z_4u_K^ufT$#Jq=vm5rT)6S|;|7hq&yVq#=w+Oy~9Z5TqJ-vMU6J^T{KbXWw=UtvAy zD|kHo-V-*-(^c(zFAR~Tl&;>4VCN8m?-Lf0mXVb^bXZwMRqez{b=@<1`UZwZ#ugVZ zSz1}!*gCtoUUPHz@bvQ!xD|-G9TXWA9TOWDpOBh%|3P}j!$+C<1%*Y$C8bZFRoB$k zy{vC|)!5P5)!ozk=Iy)Tk+Uhx$;cs!g<@btYWRqgDON*Bm` zuihNu5Ry_RNK<|e?bnh0a|4U`ca7}N1N+OkrU6|DjQ_EN4mI*0YXPcFpMhX`fIbCq zF)=`3V&Vf(0BsGcM8I*I5n5pbsMy+*#aU;Di!wd@6WOKHZH_3+8cE%Ia^#yv>iZQF zC4M8RG;L8FIUZw0i>`#xfw`=6bburX;5wuUPTNAwC^`_~tw9Iwm^07;wv1@pf-RX2 z(AaC&Xvef!sRteR(t#&lopb<{8EFN(0mT0{^*?m35cEOXCiV~=IM^CP2UZvg=)hv* zRG8WVlj^+9(8?FD;MaDhbq#u!7)5mX+B5C|67;Z3F zpANKVXV3ve9@Brr5@tFOmDZW{k`AC3n!>oOb}sbZ8a8AV54~Y{Ks+i!!<>;0bQloU zwqfpgP^lUs+d4$H|9C-cSB}{Ew917%7q09JU0hXD1zF*J$2eFooMSO!41@nSoJr{E zBmWi;F#6E%KeRCIVK3nG3QP^B*t(*_`_h}nPv#er+Z^t$)?40;F~ny z=cqJTTQ1v9lOOX(aADMT{LYfcn@ZhVr?$Ckx6L-|(lY}5N^qXK*V60LJn+{O5HGtu z0=)gC>of1#>mD-VWuokCVs=|w4qzR@d*u1{2}BJ91?0XyVkIY5cwEzJ&Mf++dNWP{ zd&reyv8gyIQ=tdIdcvsH_GW=|uq$!Z#5QxFePV9E zQwJJ6?DLe^x|a$-5;_UzBD}7Fjtdn5o%6sZb4&{y<0FbsK*4bY{2=IMPPSd91AK0& z%bh|>w2AG{)Etq)*e-6X3*_}s_EBkyRuayY!el~TV66uco1ZAG@l-u>O@~PA%F{BB zS@{V%;EoamVLe(xDLSALu5yNY;uRf;jkU(8?{wfl&;fW|E|<;n?EW!2;H+>2^NkKz zQXKkUuh#X(Pj{*_56&*Al`?EY?Lb-uwO~!&f|LrLb_OE-zvt=rz5ty&4TwSo*RiLb zWCf6YlGLZOWzD`>x{Ysqn7sm@Y_5_obWb{9z7m&U{jAiSr|-A7K}4X0TIZg^ow!tj zcX_tjooeJp(}*cu2qt&c5s$8Gy+tgyCv-nKr!c*B&wSM|_EM3%dQ12f(xaYd z<9C;W;BgiC^@IJp6^TV*A+MY%StQvcsytciAa zL_^I((9`N{QG3)VQyk4hVkLI@tew5w0GMUp=;?5|Y&^axIIH z3o{^;<4nn|@%~QyOBP_o@N^d)m~s>uTlbb@+K}B09Xmpk$MS*}6oGC$_d1RZ9O%y3 zL;muVgyV3)9AE2PBc$wqnawP&OkNjpimzP=c(|hMB-mNz(da$%_Eqe~k@E5YQ7XmK6FZS48E6ag>w(+e#J z!>8Y)5i>h01_yL^1vOF7;CtPl&l9xDF*^E(cYC{G}v+#`6Z)bgq$78LtHo3+X5K zZN?3%pg(kWx7JNS4;IqE?9ZQ&4Z^ezW6db=NE#Q0)b1p_`n*|BS(Vo;n| zU5XnqBkmxXrD`q--frnAU-Ozm#c@(EQBXFK9U}GdG-)dT@CsHb-yP4RiMtu#B zzjW7b;pJwgi+a+?R0Hd~9IoAJ&48y^Rf<~ot{q{k9WMYHoCQ;MG%!{jBFJzW7uF0d zgxN6w*T{ZzTRquY6Dw1e1r#P4FJ5>l2-V{t**?o&Gj=_|jC+X=@cZvdc1%sgDQloa z+bcmAraEZGv9$3}h23W==(rpau!ihJu!^ojv)Fsw)p?H9BD<}^ya>zF4WS6E{FtV$ zX9jK`9k{j@Gb?7|1e!Ek5uX$ZV{c$DZU;Olk8*^K`Om21_AiAYe6Z{k@mPvg`}X@M z6(Fo52$85MOo0=vI`I2E(nVY!Z(}BCqL>kDa^>du!wJ<~k5~gr^1YHpN423N6|-J5 zY6cYPEo(eR73BtGE9F) zc`I7#@9*mB%2mkA4dCd*zoQ7>N{njix{)fy6jKOq>PoY*imnef%0iBIbsD94#(jDD z@NJXA8%dw{JEcrdXfu?7UA9}0?QZ!2_l+AsOt+kbv>*?j4scAvXgYaJJAu8Zb%|*@ zK(M<4kx!W+g(yKx5d?AeI<`~f^fZ`N|ubGIX?Ew)^;B9giLA+)T&jV(3(uBzG zpQ^O>qQtzkq($5!w1hEUb8Ar|ojp0y69KvKMcdriP%%)WQ=KQP+@o=(qI`ejsg^qF zPnq98dRl~Ghu~C!M8akY?g~8oI!%G9PBzWSDsZoXPPV#HjLVJQUz197=eRYjhlyYk z1wp7xZqWigCwf}MKpXOMM~fnwiF%b>-v2d{z%3(oeBP)9ZndS&S%Paq^J zfoShoI&ff+VYi8tyUn`_NyzdSkYE%6Q1w_p$UYHkrR{x*phjx%zhgUa2SqqFDkIJr zHj~S?$oD#zDmAWiVJ0BkyWrK1*@w+zsru=XfcBe%{6bd_a4<~#=B}udnih0`c?{Gc zo^tLDJy=DK?S^q;B8iAZtTYATFusmYcI%oL!k!}stR*4W*W%Q#I|-~iAr{kvy2pBx zd_*mVT5F2P4vRyz*U~z6GAz8!nHpm^g}@M&MA9w`H*7O(1gbUS*jHhL@!A{&=jXhI zK00A^^~q=ME#~l(S(C7b<{sX6Z{&`D0Qs<`W~H;DiAmSI4bNRM=N?F;166BLp~o=D zwjBPk(ET2nGDYN_&K3lu>nVG%YCB3)eJ~sqK~*bs^0+0=|7wh;No53Ei*4OkeX&z3 z<$3Nw;zi=GcwbeI9$EF-(2ys?ibBQRMKU@D07x;XM6`(L6k*f|1CdmvITX8xThFZt z15@%ql(HQHwxG)LC0HhBZgSB!r2JLRH4oQs)VKSZj$LNQdOZhwOxZ2x+s*Ctc3%@@ zzZ=A_6w6psZSsxKJl)k7_H=i2`?$Q9;Q#T9uz48}fcrnw>|15G*(4nx;=0jrFt|Gd zFQlc8u|=|AhVi|$J>=e~4gBN-?5R1tz%<6ro*Xp0coS2w=}j?+S24^zLqWymr$X9W zk|I{0Th@Q$UfGmK?qwk6`{vc=2~A&eD^d1N5g18w62z*F9vrLV?6>RACjcIk8eyu@Z?LkJ0tBhdnC~lpt;)*nx zeu|PcrTz|*P z(lgU74r`qJQi#viW3kdxXv*J`tn40!Q0v>_()Ap-#g6SB-W-&Q&;%_>mlQ>f>OV!B=1rmtt4NXYn5r3vh9V*eT2=?}LpMFKbHJdCLk6tuDJti2(GG-_cspgQ!opD0E==UF)_-W|+n^Kn}=*b!6XsNBlZK z94T?p3I7nk&DgPK zU*hs~4}~=vD@ur$0V64P*3}eP#|xM)NCzsY`e~$r)qA&h)IjIWhl#|Du2xXivH;cz zPyujdWUR3Esy9pBdLY;sQn=nL@@Z zisV^xN~f6+)h(kvsJ;^)j;Jf?^atm?(a%EP zhb<1D%eLjEJfu zki^N=RvI9ut`b#6idk4q;b;5GOaqi5P}X~cuuO@oVTdOm}bCS7AhLYp?RT&X++aJ z0LASvFgN2f(Cz&cbnDJ^LGY`n4Sj7x8M^~{JO>d!4vA#!#*4_(a+`Iu#a;t~3?QC} z62eH(0T1L<8Sw}m*n_*?gk-@U0Tn22Z8<8Cv~OXheW-7d-F<;ItMI~et)c6X(LuZQ zu}3ubK3j0&iH$LXqMhNq7}*}ma*jkU5!Q|8!*~v~BUo!sYC2Kam{w39WDOdJ=zyEU zfte{xrB0e1CAZ6LZ}%KR5R@m*hY>(iSK`3FrCEs)8b4^J2NDQKCaP{D8Qs|@rjPH; zRBa6*guu>FI45V8Xncc^{4gm;bTsZ`ovdZ(!R*lVAfLJ_rQK;e6B8Pyq5dmb9^kI$ z#v%B%#ISo`jkO3aI`N{Luasfx+h;}UM`@zqg{>Z2e6;eavu(5rTEKB(+o}UG#bjeD zG~6Tn90^5D=5XDd*~deShrf|J&oAC{S`IR2{Sp|B^H>Ar;Bi=l*E396rxSlOAE`fZ z6A9{&7HbooCUc4F;>@5krRm}@joXM~&@ro7o3(kFvX+F_lJZg?NTAxWc9xqEENdub zovl3A3n=?h!nkW6srxA`HN-idf-_(r^b|P2TnfBXoV6B0MHe#4k%3~>RxZ7wf{H`I zu)3}krwfe~rnR?DLK6s4v-2y1MKd}V+=cqMBv`Ulbm)0 zn+@ndaqtvCAN?I!3XSiS_j7Q$_kwAu*XA{8a%1$c%Bq!N($?Z}mp-xRu}0(0%TxBp z>|Y*xT@U!<$PpiKgMmGG&=!P(uIU${Xe8$qI-r9W#Sy%SPBfceBmWAqyR2Am$RMqZ z$Ng~CMo6Jnk>s-;P`IJuYQydmmob_U)_(})##zZ~R)Wl@U}g!fRMt z=>CbX>xnaNCKH+}q<#Ur8px7TyrTqe+$f0#J{>~?1!8xx;m9aVEAYsl4G62tyYAIDFYaPUB zbkWqvUkX#HI*^Fv@kVwi>F`vQ^pW1WC3cz-t@m_U9miblp?$Zn9{Ex|T4yJw)oD3Y zp#pHIPjsOH(2oR<3gz-Dqf8r*HHVZrfKKf>$6C^QHt7Hl)`S#{h@F)L%LC@J5#7f< zJ&|>9ASVme9V!#t7ko(bXhCOYPsFT*)80vwyg^nd%J)wIWPg+vceR$*r~>LG)}ab3 zQSg!7AhB6nG0~{p>)ykuc0CD1`!(6xsF=UAfc%WSQ9$X zcNmKQ?1B89ytSlOavcRp2ac37ec81jAv?=Oi3V|6LY_hBRatLh2(n#Eow62tYXLHv ze9E#K+^wZ@~Y zf#krhS$1%epwYS)a}Bb+dlI%$30R9cv;d@9Xa^`ViNsuXu&+I+`+Y3YjAuEWZ#i&W zf2n8l!&`Jnk*9ibVwuN;kZncE(%F!_XxvkJjHbO;`sv*OrVK@VbK`%JPDQ%loHddQKY9yvD1|) zDqpt!ob%{vn*9KGvsr*2|69$fGf{6AY8m>L#E*gSLj1=?+mhZSNz<_-%{+8K0IfWu zOS*3vxS7jcfRRii?(NSWh$l5fwk=BcSECZ8ll=GIp0jMVIP3!qjM5I$1~RqMgqog) z4yr4>yyr(=z6iFuVM9+Gxx0ko@ORK$nyT+gLcXOONqL45H5*}_u4uL>kseRnTO(CW zmM@-z<QRjVCV9m;78&ViBxy>oDOEfv z`B?lU4~#V%YBnlZ+CCM=3N8KoGXxn(!V0^3&EbVU7T1y3ZSkGz>&s#TiBugwZ%N%E zhd2DhDQ_J@L-f1oboTSa2b=~6tCcta`q=M?{dX%HJ7TiES{lhq+@6+qG4@YKG}VX- zo=4$EX<9Cgs%bdEGhan5ZT4ny*NPyu(ClrCnP9{>d_??~yjU zkF+Gg7_+Gf8oyb5D+kujdB=e2NVe)hv3WR3?#4sw1NUPIX7N~tIdoJjH}zP6YB^zG z3jNbLl&L8I-R8Dp1H6^GXnbQd!R1W16h$=Fp!nmYN5n|yt+{=;lo3sVIs@HRUX z8`X2LE;kZiliTS`$c2KNT+QA);?yf+=yj*~mIEWr=W_XJCq95#ow>rzl7s~+npCda z4c`t)@I>%zxNC`HPratdhHJ9=7T`MkNZvRnV1af#v~N}o+#sdg*~0UN9$Vz29s@}` z2cckMbw&afPQXnnkyP)IOtnTFhnfv!ib&pYqQX{>g0p_IE2T9d!1QKcXJc%q-Ofew zb6Z?w<%Yt!ZH+ZJJ`B4>bOL3x(ugKz+NQ8Mcaj$!~=_gpki z%=hkFC}w3m3~Rod+Jabg5=_O7DHK*FVNl~M1AT~_IV%#RNmxgifQ!3 zl3ZVBL_=m?o4RaN?Y_zitN^jet`a_pN$Eir>lS!zqr)|iGGzaZ@QOHXW(;X*Qmw&( zxwW_!5%OC2bxld=mu;&RKT2x{T#I3J1GeXbHPsmMvhaHyu>q7@DMiZ&J}S;zrYi6$ zri1{kKxyg`@X;?kTG{-OeA?G28Oc~rhRu4aOe2c=15*#lG=vx(_^t-6zWrU%et#{+ zVQ4k#^zurM?6<7;sZ$N-=PHG>!li=7Ur8$2q`M(6)>awEF+gih z9bV9?^FT%-jvP*bqL@nlkTLVyB8DGypcK1rw?GDbu%m~)ID+%Qu}^}1M0BJImq5o2`;d`e;0lzxV( zyaZ7PMi>Zh5pi!roC)m@g`*g0ykH<9r?s>)A>b6xvRzX%MK&^&73)eit&gkR*uA>t zG_Gkx6h(-Fog}q{x`F**mi=fQiVs8Tv7Jw;b1>-75d{qQ%klr(=3Xh0(lkl zeci2~Hn>Iy9yJ1=n$g77!}B`U1+^6QdPto|N&iA)r({15zD&6ltC6BU-%0NNa$LWU z%Z@r7pI|p#SkK=p3Iw#0Ga*&^{WB3yJ%A0Q1Ha5b3gn+D!fx!G1x;+g&*WKryA~T{ zP$#wsdAKedx*#=6t6#~PBxjHFiwS0l-j3u zqc@U@131fN9Lx;I0MsceHxzjMG>T=P<5nWnmGo?mQ5CL0@jNk*uiOJdN=?ctvcwL1 zhLX}AB1vG0pQ}u2QANW-heS*_qe*Kli*$hVD-jGPq9R3NJIurvP*aZViQQUSl-1l0 zH235ZAl4d%yDZO*BI091<+1wU5QUStSq_V&$~%*|&AM{Aa2{ezT|p^bX56#^NPJ~i zU<2o7lsXV_Wv2*7MC~|Lg_z_+n?FsNXj@BYtg8VtdM!JMeHwJacGQs1n%= zt5)TdY^$;rLscjU9cXM#QbvQ$_{3WSB=jA8^*}@&js+_MMqUYqI(!QU>QRrDl~83T zH2vmIDm3erE^lXae22$U0|}gHxP#0chK<0L4DwbFia`@j^hOXTy1Z~np(sj5VuhwJ zq{DMWb~8g2u!kwIWU58!V&<)^r(k5SWe`541v$N>{;+P?|FuA8x}#_VIW`I_Fc%`w z>BvaVjaskd0?~#w^}u!+O~3&`!#3Ofgl{q(_>l#zzy4hjk8=+VIqZXl6PGcon&#E^ z7-y$w5mLLja&5Sy!{JrI{(t}i=v|)A)%MocSK0h@R$=DFB%4tt`uOi?tA7~qVGQH= z>B|1`>mT{=fH{Nq9>6z8iw@iiz^yowt!TwLh&-P6e^c{k1D~2hpq(hPNw}2@6a?+j znmZGH;&=M;Ze@-l*m9CGIaFsV)Nw!)`K7od5AQjwfpd9V+bdlew|*%OmUXlCLi)!B z*Qr3jAJR+|{4Q50KO}IF)^y;@b{MP*T%*OH*l`5-bKLh3ZPpJ|h*i3Xs8({~IO<1B z9@9#w2DG-4(%}T^EhDISbB0Y6B>o0_2}tU}jT9QX6xcyP4`9KWoI&FY>ZJo}qd-VX zFK+!92${lcubt^Y))JuAOKRQTM}IFUX<6F7hHm+E+jd9~!^l>JwyRFjJCGgbYoy0Y6*DA+2ZPe7jPQuydw~-TnA? zZ^pkLe)y5;cXw6&zo5|XZNGW9LRBNoctO@@hRvYAgROOJ^!Oo_Q-^zKhGVsl)YxX1 zgl(9vo3T^5nO>qjoYeU7r|)ZY_i-S(UAJ0c)+ ztB)&1AjZJ;GEY5?7siMid;rNPnRVQ+dvpGa7W~vuY-ER$2&zMX7XiU#XFm<;sD}E6 z`a$JVxB48h!(R=r#kj9p8Wtz$*+{0US{Yn&l?YR2`2B_S@(okl&R$1#Q)sn0rZKq% ze%!9xXj{5;1-vurcIDrGw)W^RjAJh zpQ~2*<)%iniO9PVmiR}cGww5OKO;E*(!nsi1t**+TvPmov_0eAlC)WJ;*ViZ8vH?E zH*tpF`pG0q?KkwR2mDj2ZOJF4ZDjnT9R``6|7Xt1|NW=+1?xqG@tHz_f=;z?l`KP7 z^845OCvGl_!&BwmbB&v96s3~!HtEDO(MI=%*$N*$Fzyw7b?Gwf-?rHHFSfmYH&`t} z-dUolQ$`ajF|kJ9GCGz&FH8(h=_#(tm}Oo{AD^pH#NUr-__*u$EzBQ9j_iYss>E(7UHot#N&T^SD5Z4cJL#_mxOZ3>EN5ah3Y;Y5G_6jNu(-qWznlyU8I+;GM51VhBO5xhdOr+%R@Z(oeJ5uP5-}Mv)v-8APG~BD45Y#{HkV!sFJ{!jn4PKCAFbOOcZOrXHfq$@<2z z^1_!5d>&R8ZmaQC{m)>VlEE5d^E9{{P>I%ME-0vAlyF104*qWyd$G`_b8&srj@vF#SnCC93k&Euc(*C zUbjZO_!EAd^6jnbf(;NxRbN9e&V*p>iczCQWFSD9iPj$$5CD%U=oC8H1q-m;Z!B=`+05FNtz^X(5@W?nnCy;g+AoT|Vcv8D!jNU4HwAj{cJ^)Q#4NydB5wq-&kPC)H(3ku$SPfMl5^nVoIC2goxk{lh$N#lv zAiIb)B{mXki&jCg8M$jJRf?9b)+%_4X1o-+gwIL;)<--^Ot_VJvs>!!*|^U(#CtmW zz<&#G`uhZ_m-+z3O9v(@qJOUT8w1}&{wgx|M_Y}pcY7S#U*rW@CZ_-+C@v=WGd#xE zw`H*FA4>=Hi~dsh$KUn<5(}X*VJP|}olgJTbd>$NLr3kQcV2POTe5smTD?ENjO&cP z>H5cXvHzP*4|;+1PO2%CE>$pU%kZPbH!a`CG`4)H2S9+{9)7(8x6*fqZKZCxhz`$& zG2LMEX-k?DIbkFk!NG45{3Rsk_RZtd_oKJCrr4w{&P4FuKJbWmy5fR=)8if4H%jTo z=QyP$^z4qhr;8z^pC>#TzEOT@&lJbe97!XAn!|ctUS<27JIBp02%L!ZzgppMRIahK zw?ZzsP`>T_zH`3p={G$O`Gkdz2@LVPwMluN<3GfBqSIWPeOTlyV?^s~A%BlhG4}`I zHG;S7UY%=d&{cKI{?MeRzuwbW5g9bvbK?hlib5aR7LnYWg#OVf5CXAlC4mhc~w)JM#81pG!O< zG8&e>V&mz;S7aEj#J7@Dl%>eA&f>74dT7wuUrHkRvCG)ZFU_&NDZ0;U97r`h{EhoK zj^%zn75eT?M#fzX%8%ydEBYI6C+5c(n|>nQa8hd4VaIdF&$MbJY%Qp ze52>)<)VbA$GW_y-dSII-=SAq&&`}#a+_w^k$m93c}{Jt_Awbn^=AFCF867eW!!ryH^yU^(V|&THWe-m<3e{E zc6L|SwsMWDwcf|v^voXrT9*T3QA|z>X?xS&ag5M&kCl3{{A|8k*u2GD_?0d(-}ijs zWOn=wXRXEao5xkl*@K06#th&sa{*29@R+&KtX|!^3e$*G#N3Njs4L$Nrmg9w*Rk8l zt(}54{=+}sadH^I|MJc1Bx>2E_)}*IB%A+~^?-p<0TRytI5+oSp5q_R{z0a0bCbVq z;BQk0|8)cT5LNyvk??OT{%!8mzpgk1<=*-IOscVhyK#Qb0|Y2)PUcg^AG*D>Tff* z{H1Cxl*8hWnMMCDkINrg`Tf)ve`%!XU;iJjUV;|@ diff --git a/static/files/excels/cmdb_excel_2015_11_20_00_04.xlsx b/static/files/excels/cmdb_excel_2015_11_20_00_04.xlsx deleted file mode 100644 index 05fc5eefcdcf615a97670bbb3e883e1f28f9b3e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6125 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 Ufk;UJ0Bq#@1u{2uAiwVZ51tE?YybcN diff --git a/templates/jasset/asset_add.html b/templates/jasset/asset_add.html index 807dcd130..210d583d4 100644 --- a/templates/jasset/asset_add.html +++ b/templates/jasset/asset_add.html @@ -74,11 +74,11 @@
    - - +
    +
    @@ -143,8 +142,60 @@
    +
    + + +
    +
    +
    +
    + 推送主机 +
    + + + + + + + + + + +
    +
    +
    +
    + + + + + + + + + + + {% for host in push_info %} + + + + + + + {% endfor %} + +
    主机主机组使用密码使用秘钥
    {{ host.ip }} {{ host.group }} {{ host.password }} {{ host.pubkey }}
    +
    +
    +
    +
    diff --git a/templates/jperm/role_sudo.j2 b/templates/jperm/role_sudo.j2 index c544d33ba..a910317eb 100644 --- a/templates/jperm/role_sudo.j2 +++ b/templates/jperm/role_sudo.j2 @@ -1,106 +1,31 @@ -## Sudoers allows particular users to run various commands as -## the root user, without needing the root password. -## -## Examples are provided at the bottom of the file for collections -## of related commands, which can then be delegated out to particular -## users or groups. -## -## This file must be edited with the 'visudo' command. - -## Host Aliases -## Groups of machines. You may prefer to use hostnames (perhaps using -## wildcards for entire domains) or IP addresses instead. -# Host_Alias FILESERVERS = fs1, fs2 -# Host_Alias MAILSERVERS = smtp, smtp2 - -## User Aliases -## These aren't often necessary, as you can use regular groups -## (ie, from files, LDAP, NIS, etc) in this file - just use %groupname -## rather than USERALIAS -# User_Alias ADMINS = jsmith, mikem +#!/bin/bash -## Command Aliases -## These are groups of related commands... - -{% for sudo in sudo_chosen_obj %} -Cmnd_Alias {{ sudo.name }} = {{ sudo.commands }} -{% endfor %} +sudo_file=/etc/sudoers +# Add Command Aliases +add_cmd_alias() { + {% for sudo in sudo_chosen_obj %} + if $(grep '^Cmnd_Alias {{ sudo.name }}' ${sudo_file} &> /dev/null); then + sed -i 's@^Cmnd_Alias.*{{ sudo.name }}.*@Cmnd_Alias {{ sudo.name }} = {{ sudo.commands }}@g' ${sudo_file} + else + echo "Cmnd_Alias {{ sudo.name }} = {{ sudo.commands }}" >> ${sudo_file} + fi + {% endfor %} +} +add_role_chosen() { + {% for role, alias in sudo_chosen_aliase.items %} + if $(grep '^{{ role }}' ${sudo_file} &> /dev/null); then + sed -i 's@^{{ role }}.*@{{ role }} ALL = {{ alias }}@g' ${sudo_file} + else + echo "{{ role }} ALL = {{ alias }}" >> ${sudo_file} + fi + {% endfor %} +} -# Defaults specification -# -# Disable "ssh hostname sudo ", because it will show the password in clear. -# You have to run "ssh -t hostname sudo ". -# -Defaults requiretty - -# -# Refuse to run if unable to disable echo on the tty. This setting should also be -# changed in order to be able to use sudo without a tty. See requiretty above. -# -Defaults !visiblepw - -# -# Preserving HOME has security implications since many programs -# use it when searching for configuration files. Note that HOME -# is already set when the the env_reset option is enabled, so -# this option is only effective for configurations where either -# env_reset is disabled or HOME is present in the env_keep list. -# -Defaults always_set_home - -Defaults env_reset -Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE INPUTRC KDEDIR LS_COLORS" -Defaults env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE" -Defaults env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES" -Defaults env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE" -Defaults env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY" - -# -# Adding HOME to env_keep may enable a user to run unrestricted -# commands via sudo. -# -# Defaults env_keep += "HOME" - -Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin - -## Next comes the main part: which users can run what software on -## which machines (the sudoers file can be shared between multiple -## systems). -## Syntax: -## -## user MACHINE=COMMANDS -## -## The COMMANDS section may have other options added to it. -## -## Allow root to run any commands anywhere -root ALL=(ALL) ALL - -{% for role, alias in sudo_chosen_aliase.items %} -{{ role }} ALL = {{ alias }} -{% endfor %} - -## Allows members of the 'sys' group to run networking, software, -## service management apps and more. -# %sys ALL = NETWORKING, SOFTWARE, SERVICES, STORAGE, DELEGATING, PROCESSES, LOCATE, DRIVERS - -## Allows people in group wheel to run all commands -%wheel ALL=(ALL) ALL - -## Same thing without a password -# %wheel ALL=(ALL) NOPASSWD: ALL - -## Allows members of the users group to mount and unmount the -## cdrom as root -# %users ALL=/sbin/mount /mnt/cdrom, /sbin/umount /mnt/cdrom - -## Allows members of the users group to shutdown this system -# %users localhost=/sbin/shutdown -h now - -## Read drop-in files from /etc/sudoers.d (the # here does not mean a comment) -#includedir /etc/sudoers.d +add_cmd_alias +add_role_chosen \ No newline at end of file From 117f01e71f56e558739def474c8c3dc7fba913df Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 28 Nov 2015 21:13:44 +0800 Subject: [PATCH 259/385] =?UTF-8?q?=E4=B8=AD=E6=96=87=E6=92=AD=E6=94=BEbug?= =?UTF-8?q?=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 11 +++++++---- jlog/log_api.py | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/connect.py b/connect.py index 96418f99d..1c7218dd8 100644 --- a/connect.py +++ b/connect.py @@ -15,6 +15,7 @@ import readline import django import paramiko import struct, fcntl, signal, socket, select +from io import open as copen os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': @@ -225,6 +226,8 @@ class Tty(object): raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) try: + # log_file_f = copen(log_file_path + '.log', mode='at', encoding='utf-8', errors='replace') + # log_time_f = copen(log_file_path + '.time', mode='at', encoding='utf-8', errors='replace') log_file_f = open(log_file_path + '.log', 'a') log_time_f = open(log_file_path + '.time', 'a') except IOError: @@ -245,7 +248,7 @@ class Tty(object): log.pid = log.id log.save() - log_file_f.write('Start at %s\n' % datetime.datetime.now()) + log_file_f.write('Start at %s\r\n' % datetime.datetime.now()) return log_file_f, log_time_f, log def get_connect_info(self): @@ -366,13 +369,13 @@ class SshTty(Tty): self.vim_data += x sys.stdout.write(x) sys.stdout.flush() - es_x = escapeString(x) now_timestamp = time.time() - log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(es_x))) + log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x))) + log_time_f.flush() log_file_f.write(x) + log_file_f.flush() pre_timestamp = now_timestamp log_file_f.flush() - log_time_f.flush() if input_mode and not self.is_output(x): data += x diff --git a/jlog/log_api.py b/jlog/log_api.py index 578ea684e..cbcb38a26 100644 --- a/jlog/log_api.py +++ b/jlog/log_api.py @@ -3,7 +3,7 @@ from argparse import ArgumentParser, FileType from contextlib import closing -from codecs import open as copen +from io import open as copen from json import dumps from math import ceil import re @@ -46,20 +46,20 @@ def scriptToJSON(scriptf, timing=None): ret = [] with closing(scriptf): - scriptf.readline() # ignore first header line from script file + print "# %s #" % scriptf.readline() # ignore first header line from script file offset = 0 for t in timing: dt = scriptf.read(t[1]) data = escapeString(dt) - # print ('###### (%s, %s)' % (t[1], data)) + print ('###### (%s, %s)' % (t[1], repr(data))) offset += t[0] ret.append((data, offset)) return dumps(ret) def renderTemplate(script_path, time_file_path, dimensions=(24, 80), templatename=DEFAULT_TEMPLATE): - # with copen(script_path, encoding='utf-8', errors='replace') as scriptf: - with open(script_path) as scriptf: + with copen(script_path, encoding='utf-8', errors='replace', newline='\r\n') as scriptf: + # with open(script_path) as scriptf: with open(time_file_path) as timef: timing = getTiming(timef) json = scriptToJSON(scriptf, timing) From a0ae7ff1397e5d78c00c224f6780ecf66773c8e1 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 28 Nov 2015 22:08:47 +0800 Subject: [PATCH 260/385] =?UTF-8?q?=E4=BF=AE=E6=94=B9getresource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/utils.py | 2 +- jperm/views.py | 37 ++++++++++++---------- templates/jperm/perm_sudo_add.html | 50 ------------------------------ 3 files changed, 21 insertions(+), 68 deletions(-) diff --git a/jperm/utils.py b/jperm/utils.py index 9ba37efb3..cbe4b53da 100644 --- a/jperm/utils.py +++ b/jperm/utils.py @@ -108,7 +108,7 @@ def get_add_sudo_script(sudo_chosen_aliase, sudo_chosen_obj): sudo_file = NamedTemporaryFile(delete=False) sudo_file.write(sudo_content) sudo_file.close() - + print(sudo_file.name) return sudo_file.name diff --git a/jperm/views.py b/jperm/views.py index e095c8a0a..445d82845 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -356,7 +356,6 @@ def perm_role_edit(request): if request.method == "GET": return my_render('jperm/perm_role_edit.html', locals(), request) - if request.method == "POST": # 获取 POST 数据 role_name = request.POST.get("role_name") @@ -423,23 +422,25 @@ def perm_role_push(request): group_assets_obj = [] for asset_group in asset_groups_obj: group_assets_obj.extend(asset_group.asset_set.all()) - calc_assets = set(assets_obj) | set(group_assets_obj) + calc_assets = list(set(assets_obj) | set(group_assets_obj)) # 生成Inventory - push_resource = [] - for asset in calc_assets: - if asset.use_default_auth: - username = Setting.field1 - port = Setting.field2 - password = Setting.field3 - else: - username = asset.username - password = asset.password - port = asset.port - push_resource.append({"hostname": asset.ip, - "port": port, - "username": username, - "password": password}) + # push_resource = [] + # for asset in calc_assets: + # if asset.use_default_auth: + # username = Setting.field1 + # port = Setting.field2 + # password = Setting.field3 + # else: + # username = asset.username + # password = asset.password + # port = asset.port + # push_resource.append({"hostname": asset.ip, + # "port": port, + # "username": username, + # "password": password}) + push_resource = gen_resource(calc_assets) + print push_resource # 获取角色的推送方式,以及推送需要的信息 roles_obj = [PermRole.objects.get(name=role_name) for role_name in role_names] @@ -486,7 +487,9 @@ def perm_role_push(request): if ret_sudo["step1"] != "ok" or ret_sudo["step2"] != "ok": ret_failed["step3"] = "failed" - os.remove(add_sudo_script) + # os.remove(add_sudo_script) + + print ret # 结果汇总统计 diff --git a/templates/jperm/perm_sudo_add.html b/templates/jperm/perm_sudo_add.html index 8df34a5ce..a996ae387 100644 --- a/templates/jperm/perm_sudo_add.html +++ b/templates/jperm/perm_sudo_add.html @@ -1,9 +1,4 @@ {% extends 'base.html' %} -{% block self_head_css_js %} - - - -{% endblock %} {% load mytags %} {% block content %} {% include 'nav_cat_bar.html' %} @@ -69,52 +64,7 @@ {% endblock %} {% block self_footer_js %} - - {% endblock %} From 4672884255bf33352a0a0b53ad1e54331d9484e7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 28 Nov 2015 22:45:40 +0800 Subject: [PATCH 261/385] =?UTF-8?q?=E4=BF=AE=E6=94=B9role=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 2 +- templates/jperm/perm_role_add.html | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/connect.py b/connect.py index 1c7218dd8..9cfc7e063 100644 --- a/connect.py +++ b/connect.py @@ -602,7 +602,7 @@ class Nav(object): else: res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm) cmd = Command(res) - logger.debug("res: %s" % res) + logger.debug("批量执行res: %s" % res) asset_name_str = '' for inv in cmd.inventory.get_hosts(pattern=pattern): print inv.name diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index 78c33b38e..d62ab05b6 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -116,6 +116,21 @@ $('#roleForm').validator({ form.submit(); } }) + +var config = { + '.chosen-select' : {}, + '.chosen-select-deselect' : {allow_single_deselect:true}, + '.chosen-select-no-single' : {disable_search_threshold:10}, + '.chosen-select-no-results': {no_results_text:'Oops, nothing found!'}, + '.chosen-select-width' : {width:"95%"} + }; + +for (var selector in config) { + $(selector).chosen(config[selector]); +} + + + {% endblock %} From 3f000b9d54df60ef094d98d0f8cd9fe0f2d96581 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 28 Nov 2015 22:53:27 +0800 Subject: [PATCH 262/385] =?UTF-8?q?=E4=BF=AE=E6=94=B9role=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/jperm/perm_role_add.html | 2 +- templates/jperm/perm_role_edit.html | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index d62ab05b6..8579b6534 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -115,7 +115,7 @@ $('#roleForm').validator({ valid: function(form) { form.submit(); } -}) +}); var config = { '.chosen-select' : {}, diff --git a/templates/jperm/perm_role_edit.html b/templates/jperm/perm_role_edit.html index 2b1783b56..90bd59a87 100644 --- a/templates/jperm/perm_role_edit.html +++ b/templates/jperm/perm_role_edit.html @@ -107,7 +107,19 @@ $('#roleForm').validator({ valid: function(form) { form.submit(); } -}) +}); + +var config = { + '.chosen-select' : {}, + '.chosen-select-deselect' : {allow_single_deselect:true}, + '.chosen-select-no-single' : {disable_search_threshold:10}, + '.chosen-select-no-results': {no_results_text:'Oops, nothing found!'}, + '.chosen-select-width' : {width:"95%"} + }; + +for (var selector in config) { + $(selector).chosen(config[selector]); +} From b7d9e41b430bb7abf30a25479bf44cf6c79e6d1b Mon Sep 17 00:00:00 2001 From: yumaojun <719118794@qq.com> Date: Sat, 28 Nov 2015 22:54:59 +0800 Subject: [PATCH 263/385] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8D=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E6=8E=A8=E9=80=81=20step3=20=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/ansible_api.py | 2 +- jperm/views.py | 4 +++- jumpserver.conf | 2 +- templates/jperm/perm_role_edit.html | 7 ++++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/jperm/ansible_api.py b/jperm/ansible_api.py index 0f8036453..87ff2e72f 100644 --- a/jperm/ansible_api.py +++ b/jperm/ansible_api.py @@ -438,7 +438,7 @@ class Tasks(Command): use template to render pushed sudoers file :return: """ - module_args1 = 'test' + module_args1 = file_path ret1 = self.__run(module_args1, "script") module_args2 = 'visudo -c | grep "parsed OK" &> /dev/null && echo "ok" || echo "failed"' ret2 = self.__run(module_args2, "shell") diff --git a/jperm/views.py b/jperm/views.py index 445d82845..84b4f5e37 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -353,6 +353,7 @@ def perm_role_edit(request): role = PermRole.objects.get(id=role_id) role_pass = CRYPTOR.decrypt(role.password) role_sudos = role.sudo.all() + sudo_all = PermSudo.objects.all() if request.method == "GET": return my_render('jperm/perm_role_edit.html', locals(), request) @@ -364,6 +365,7 @@ def perm_role_edit(request): role_sudo_names = request.POST.getlist("sudo_name") role_sudos = [PermSudo.objects.get(name=sudo_name) for sudo_name in role_sudo_names] key_content = request.POST.get("role_key", "") + try: if not role: raise ServerError('角色用户不能存在') @@ -384,6 +386,7 @@ def perm_role_edit(request): role.comment = role_comment role.sudo = role_sudos + role.save() msg = u"更新系统角色: %s" % role.name return HttpResponseRedirect('/jperm/role/') @@ -393,7 +396,6 @@ def perm_role_edit(request): return my_render('jperm/perm_role_edit.html', locals(), request) - @require_role('admin') def perm_role_push(request): """ diff --git a/jumpserver.conf b/jumpserver.conf index 6297ab00a..ac9506672 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -9,7 +9,7 @@ log = debug host = 127.0.0.1 port = 3306 user = jumpserver -password = mysql234 +password = mysql1234 database = jumpserver [websocket] diff --git a/templates/jperm/perm_role_edit.html b/templates/jperm/perm_role_edit.html index 2b1783b56..bd775a5a1 100644 --- a/templates/jperm/perm_role_edit.html +++ b/templates/jperm/perm_role_edit.html @@ -58,14 +58,15 @@
    -
    +
    +
    From 98f0655da4c3fd38a63fda0079337ade3ee264ac Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 29 Nov 2015 11:33:19 +0800 Subject: [PATCH 264/385] fix rule and role bug --- jlog/log_api.py | 4 +- jperm/views.py | 177 +++++++++++++--------------- run_websocket.py | 1 + templates/jasset/asset_list.html | 8 +- templates/jasset/idc_list.html | 2 +- templates/jlog/log_online.html | 8 +- templates/jperm/perm_role_add.html | 22 ++-- templates/jperm/perm_role_edit.html | 4 +- templates/jperm/perm_role_list.html | 2 +- templates/jperm/perm_role_push.html | 64 +++++----- templates/jperm/perm_rule_add.html | 11 +- templates/jperm/perm_rule_edit.html | 41 +------ templates/nav.html | 6 +- templates/setting.html | 19 +-- 14 files changed, 159 insertions(+), 210 deletions(-) diff --git a/jlog/log_api.py b/jlog/log_api.py index cbcb38a26..b10325643 100644 --- a/jlog/log_api.py +++ b/jlog/log_api.py @@ -46,12 +46,12 @@ def scriptToJSON(scriptf, timing=None): ret = [] with closing(scriptf): - print "# %s #" % scriptf.readline() # ignore first header line from script file + scriptf.readline() # ignore first header line from script file offset = 0 for t in timing: dt = scriptf.read(t[1]) data = escapeString(dt) - print ('###### (%s, %s)' % (t[1], repr(data))) + # print ('###### (%s, %s)' % (t[1], repr(data))) offset += t[0] ret.append((data, offset)) return dumps(ret) diff --git a/jperm/views.py b/jperm/views.py index 445d82845..800a3579a 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -69,17 +69,14 @@ def perm_rule_add(request): # 渲染数据 header_title, path1, path2 = "授权规则", "规则管理", "添加规则" - if request.method == 'GET': - # 渲染数据, 获取所有 用户,用户组,资产,资产组,用户角色, 用于添加授权规则 - users = User.objects.all() - user_groups = UserGroup.objects.all() - assets = Asset.objects.all() - asset_groups = AssetGroup.objects.all() - roles = PermRole.objects.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_add.html', locals(), request) - - elif request.method == 'POST': + if request.method == 'POST': # 获取用户选择的 用户,用户组,资产,资产组,用户角色 users_select = request.POST.getlist('user', []) user_groups_select = request.POST.getlist('usergroup', []) @@ -88,45 +85,43 @@ def perm_rule_add(request): roles_select = request.POST.getlist('role', []) rule_name = request.POST.get('rulename') 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] - 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) + try: + rule = get_object(PermRule, name=rule_name) + if rule: + raise ServerError(u'授权规则 %s 已存在' % rule_name) - # 获取需要授权的用户列表 - 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) + # 获取需要授权的主机列表 + assets_obj = [Asset.objects.get(id=asset_id) for asset_id in assets_select] + asset_groups_obj = [AssetGroup.objects.get(id=group_id) for group_id 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) - # 获取授予的角色列表 - roles_obj = [PermRole.objects.get(name=role) for role in roles_select] + # 获取需要授权的用户列表 + users_obj = [User.objects.get(id=user_id) for user_id in users_select] + user_groups_obj = [UserGroup.objects.get(id=group_id) for group_id 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) - # 仅授权成功的,写回数据库(授权规则,用户,用户组,资产,资产组,用户角色) - rule = PermRule(name=rule_name, comment=rule_comment) - rule.save() - rule.user = users_obj - rule.usergroup = user_groups_obj - rule.asset = assets_obj - rule.asset_group = asset_groups_obj - rule.role = roles_obj - rule.save() + # 获取授予的角色列表 + roles_obj = [PermRole.objects.get(id=role_id) for role_id in roles_select] - msg = u"添加授权规则:%s" % rule.name - # 渲染数据 - header_title, path1, path2 = "授权规则", "规则管理", "查看规则" - rules_list = PermRule.objects.all() + # 仅授权成功的,写回数据库(授权规则,用户,用户组,资产,资产组,用户角色) + rule = PermRule(name=rule_name, comment=rule_comment) + rule.save() + rule.user = users_obj + rule.user_group = user_groups_obj + rule.asset = assets_obj + rule.asset_group = asset_groups_obj + rule.role = roles_obj + rule.save() - # 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) + msg = u"添加授权规则:%s" % rule.name + # 渲染数据 + return HttpResponseRedirect('/jperm/rule/') + except ServerError, e: + error = e + return my_render('jperm/perm_rule_add.html', locals(), request) @require_role('admin') @@ -155,7 +150,6 @@ def perm_rule_edit(request): 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: @@ -168,24 +162,23 @@ def perm_rule_edit(request): 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) + assets_obj = [Asset.objects.get(id=asset_id) for asset_id in assets_select] + asset_groups_obj = [AssetGroup.objects.get(id=group_id) for group_id 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) + users_obj = [User.objects.get(id=user_id) for user_id in users_select] + user_groups_obj = [UserGroup.objects.get(id=group_id) for group_id 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] + roles_obj = [PermRole.objects.get(id=role_id) for role_id in roles_select] # 仅授权成功的,写回数据库(授权规则,用户,用户组,资产,资产组,用户角色) rule.user = users_obj - rule.usergroup = user_groups_obj + rule.user_group = user_groups_obj rule.asset = assets_obj rule.asset_group = asset_groups_obj rule.role = roles_obj @@ -194,17 +187,8 @@ def perm_rule_edit(request): 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) + return HttpResponseRedirect('/jperm/rule/') @require_role('admin') @@ -254,37 +238,37 @@ def perm_role_add(request): """ # 渲染数据 header_title, path1, path2 = "系统角色", "角色管理", "添加角色" + sudos = PermSudo.objects.all() - if request.method == "GET": - default_password = get_rand_pass() - sudos = PermSudo.objects.all() - return my_render('jperm/perm_role_add.html', locals(), request) - - elif request.method == "POST": - # 获取参数: name, comment, sudo - name = request.POST.get("role_name") - comment = request.POST.get("role_comment") - password = request.POST.get("role_password") - sudos_name = request.POST.getlist("sudo_name") - sudos_obj = [PermSudo.objects.get(name=sudo_name) for sudo_name in sudos_name] - encrypt_pass = CRYPTOR.encrypt(password) - # 生成随机密码,生成秘钥对 - - key_path = gen_keys() - role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) - role.save() - role.sudo = sudos_obj - role.save() - - 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)) + if request.method == "POST": + # 获取参数: name, comment + name = request.POST.get("role_name", "") + comment = request.POST.get("role_comment", "") + password = request.POST.get("role_password", "") + key_content = request.POST.get("role_key", "") + sudo_ids = request.POST.getlist('sudo_name') + try: + if get_object(PermRole, name=name): + raise ServerError('已经存在该用户 %s' % name) + if password: + encrypt_pass = CRYPTOR.encrypt(password) + else: + encrypt_pass = CRYPTOR.encrypt(CRYPTOR.gen_rand_pass(20)) + # 生成随机密码,生成秘钥对 + sudos_obj = [get_object(PermSudo, id=sudo_id) for sudo_id in sudo_ids] + if key_content: + key_path = gen_keys(key=key_content) + else: + key_path = gen_keys() + logger.debug('generate role key: %s' % key_path) + role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) + role.save() + role.sudo = sudos_obj + msg = u"添加角色: %s" % name + return HttpResponseRedirect('/jperm/role/') + except ServerError, e: + error = e return my_render('jperm/perm_role_add.html', locals(), request) @@ -352,6 +336,7 @@ def perm_role_edit(request): role_id = request.GET.get("id") role = PermRole.objects.get(id=role_id) role_pass = CRYPTOR.decrypt(role.password) + sudo_all = PermSudo.objects.all() role_sudos = role.sudo.all() if request.method == "GET": return my_render('jperm/perm_role_edit.html', locals(), request) @@ -362,7 +347,7 @@ def perm_role_edit(request): role_password = request.POST.get("role_password") role_comment = request.POST.get("role_comment") role_sudo_names = request.POST.getlist("sudo_name") - role_sudos = [PermSudo.objects.get(name=sudo_name) for sudo_name in role_sudo_names] + role_sudos = [PermSudo.objects.get(id=sudo_id) for sudo_id in role_sudo_names] key_content = request.POST.get("role_key", "") try: if not role: @@ -380,10 +365,8 @@ def perm_role_edit(request): logger.debug('Recreate role key: %s' % role.key_path) # 写入数据库 role.name = role_name - role.password = encrypt_role_pass role.comment = role_comment role.sudo = role_sudos - role.save() msg = u"更新系统角色: %s" % role.name return HttpResponseRedirect('/jperm/role/') diff --git a/run_websocket.py b/run_websocket.py index e3a858d82..742220c6d 100644 --- a/run_websocket.py +++ b/run_websocket.py @@ -250,6 +250,7 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler): if asset: roles = user_have_perm(self.user, asset) logger.debug(roles) + logger.debug('rolename: %s' % role_name) login_role = '' for role in roles: if role.name == role_name: diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 3550b1248..887ce81a3 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -31,7 +31,7 @@
    +
    +
    + +
    +
    +
    + 未推送主机 + +
    +
    +
    +
    + + + + + + + + + {% for asset in need_push_asset %} + + + {% endfor %} diff --git a/templates/jperm/perm_role_list.html b/templates/jperm/perm_role_list.html index 0c48fd782..e9305a165 100644 --- a/templates/jperm/perm_role_list.html +++ b/templates/jperm/perm_role_list.html @@ -48,9 +48,9 @@ - - + + @@ -58,9 +58,9 @@ {% for role in roles %} - - + + - + - + @@ -129,6 +129,10 @@ + + + + diff --git a/templates/jasset/asset_edit.html b/templates/jasset/asset_edit.html index 0faa89a37..339fde387 100644 --- a/templates/jasset/asset_edit.html +++ b/templates/jasset/asset_edit.html @@ -105,6 +105,9 @@
    {{ af.system_version|bootstrap_horizontal }} +
    + {{ af.system_arch|bootstrap_horizontal }} +
    {{ af.number|bootstrap_horizontal }} diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 887ce81a3..8373f8a0f 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -96,31 +96,6 @@ -{#
    #} - -{#
    #} -{##} -{#
    #} - -{#
    #} -{#
    #} - -{#
    #} -{#
    #} -{#
    #} -{# #} -{# #} -{#
    #} -{# #} -{# #} -{#
    #} -{#
    #} -{#
    #} -
    主机IP
    {{ asset.hostname }} {{ asset.ip }}
    名称 备注创建时间 sudo别名创建时间备注 操作
    {{ role.name }} {{ role.comment }} {{ role.date_added | date:"Y-m-d H:i:s"}} {{ role | role_contain_which_sudos }} {{ role.date_added | date:"Y-m-d H:i:s"}} {{ role.comment }} 详情 编辑 diff --git a/templates/jperm/perm_role_push.html b/templates/jperm/perm_role_push.html index c308bbe60..dfa4a109c 100644 --- a/templates/jperm/perm_role_push.html +++ b/templates/jperm/perm_role_push.html @@ -45,7 +45,7 @@
    @@ -56,7 +56,7 @@
    diff --git a/templates/jperm/perm_rule_add.html b/templates/jperm/perm_rule_add.html index cf33cbb97..15eec08e9 100644 --- a/templates/jperm/perm_rule_add.html +++ b/templates/jperm/perm_rule_add.html @@ -68,7 +68,7 @@
    资产和资产组必选一个 diff --git a/templates/juser/group_list.html b/templates/juser/group_list.html index 86e4ec3f6..db0f59a96 100644 --- a/templates/juser/group_list.html +++ b/templates/juser/group_list.html @@ -55,11 +55,12 @@ {% for group in user_groups.object_list %}
    - + {{ group.name }} {{ group.id | members_count }} + {{ group.id | members_count }} + {{ group.comment }} 编辑 diff --git a/templates/nav.html b/templates/nav.html index b85eba8c7..61bd8fe36 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -24,7 +24,7 @@
  • 授权管理
  • 内存{{ asset.memory|default_if_none:"" }}{% if asset.memory %}M{% endif %}{{ asset.memory|default_if_none:"" }}{% if asset.memory %}G{% endif %}
    硬盘系统版本 {{ asset.system_type|default_if_none:"" }} {{ asset.system_version|default_if_none:"" }}
    系统平台{{ asset.system_arch|default_if_none:"" }}
    运行环境 {{ asset.get_env_display|default_if_none:"" }}
    @@ -134,7 +109,9 @@ {# #} - + + + @@ -150,7 +127,9 @@ {# #} - + + + - + {# #} - - + + {% endfor %} From 4959073a33c8e46bf6fe0684fdf93c39ce7e2af7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 2 Dec 2015 23:50:20 +0800 Subject: [PATCH 283/385] =?UTF-8?q?web=20=E6=89=B9=E9=87=8F=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jlog/urls.py | 2 +- jlog/views.py | 10 ---------- jperm/perm_api.py | 15 +++++++++++++++ jperm/urls.py | 1 + jumpserver/views.py | 4 ++-- run_websocket.py | 5 +++-- templates/exec_cmd.html | 8 ++++---- templates/jasset/asset_list.html | 26 ++++++++++++++++++++------ 8 files changed, 46 insertions(+), 25 deletions(-) diff --git a/jlog/urls.py b/jlog/urls.py index deb2902b4..e9a3063bd 100644 --- a/jlog/urls.py +++ b/jlog/urls.py @@ -9,5 +9,5 @@ urlpatterns = patterns('', url(r'^log_kill/', log_kill), url(r'^record/$', log_record), url(r'^web_terminal/$', web_terminal), - url(r'^get_role_name/$', get_role_name), + ) \ No newline at end of file diff --git a/jlog/views.py b/jlog/views.py index 729ececc7..462fdf1e8 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -107,16 +107,6 @@ def log_record(request): return HttpResponse('无日志记录!') -@require_role('user') -def get_role_name(request): - asset_id = request.GET.get('id', 9999) - asset = get_object(Asset, id=asset_id) - if asset: - role = user_have_perm(request.user, asset=asset) - return HttpResponse(','.join([i.name for i in role])) - return HttpResponse('error') - - @require_role('user') def web_terminal(request): asset_id = request.GET.get('id') diff --git a/jperm/perm_api.py b/jperm/perm_api.py index ca8645854..f64f32189 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -300,6 +300,21 @@ def get_role_push_host(role): asset_no_push = set(asset_all) - set(asset_pushed.keys()) return asset_pushed, asset_no_push + +@require_role('user') +def perm_role_get(request): + asset_id = request.GET.get('id', 0) + if asset_id: + asset = get_object(Asset, id=asset_id) + if asset: + role = user_have_perm(request.user, asset=asset) + return HttpResponse(','.join([i.name for i in role])) + else: + roles = get_group_user_perm(request.user).get('role').keys() + return HttpResponse(','.join(i.name for i in roles)) + return HttpResponse('error') + + if __name__ == "__main__": print get_role_info(1) diff --git a/jperm/urls.py b/jperm/urls.py index 456cf5c87..0d26aad45 100644 --- a/jperm/urls.py +++ b/jperm/urls.py @@ -14,6 +14,7 @@ urlpatterns = patterns('jperm.views', (r'^role/perm_role_edit/$', perm_role_edit), (r'^role/push/$', perm_role_push), (r'^role/recycle/$', perm_role_recycle), + (r'^role/get/$', perm_role_get), (r'^sudo/$', perm_sudo_list), (r'^sudo/perm_sudo_add/$', perm_sudo_add), (r'^sudo/perm_sudo_delete/$', perm_sudo_delete), diff --git a/jumpserver/views.py b/jumpserver/views.py index 5eb0cac80..82bef8436 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -362,6 +362,6 @@ def download(request): @login_required(login_url='/login') def exec_cmd(request): - role_name = request.GET.get('role_name') - web_terminal_uri = 'ws://%s/exec?role=%s' % (WEB_SOCKET_HOST, role_name) + role = request.GET.get('role') + web_terminal_uri = 'ws://%s/exec?role=%s' % (WEB_SOCKET_HOST, role) return my_render('exec_cmd.html', locals(), request) diff --git a/run_websocket.py b/run_websocket.py index 0bdd609cf..1d0b44f07 100644 --- a/run_websocket.py +++ b/run_websocket.py @@ -229,7 +229,8 @@ class ExecHandler(tornado.websocket.WebSocketHandler): @require_auth('user') def open(self): logger.debug('Websocket: Open exec request') - role_name = self.get_argument('role', 'dev') + role_name = self.get_argument('role', 'sb') + logger.debug('Web执行命令: 请求角色 %s' % role_name) self.role = get_object(PermRole, name=role_name) self.perm = get_group_user_perm(self.user) roles = self.perm.get('role').keys() @@ -262,7 +263,7 @@ class ExecHandler(tornado.websocket.WebSocketHandler): header = "[ %s => %s]\n" % (host, 'failed') self.write_message(header) self.write_message(output) - self.write_message('\n\n') + self.write_message('\n~o~ Task finished ~o~\n') class WebTerminalHandler(tornado.websocket.WebSocketHandler): diff --git a/templates/exec_cmd.html b/templates/exec_cmd.html index 6b9753c97..76c19c214 100644 --- a/templates/exec_cmd.html +++ b/templates/exec_cmd.html @@ -23,7 +23,7 @@ +{% endblock %} +{% block content %} +{% include 'nav_cat_bar.html' %} + + + +
    +
    +ol-sm/g +
    +
    +
    用户日志详细信息列表
    + +
    + +
    + +
    +
    +
    +
    + + to + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    + +
    + + +
    +
    所属主机组 配置信息 操作系统 使用默认管理 cpu核数 内存 硬盘 操作
    {{ asset.group.all|group_str2 }}{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }} {{ asset.use_default_auth|bool2str }} {{ asset.cpu|get_cpu_core }} {{ asset.memory }}{% if asset.memory %}G{% endif %} {{ asset.disk }} 详情 {% ifnotequal session_role_id 0 %} From 0e6fd89f0b32670da85ee0eafc10d21ad1315e72 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 2 Dec 2015 13:35:06 +0800 Subject: [PATCH 278/385] =?UTF-8?q?=E6=89=B9=E9=87=8F=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 96 ++++++++++++++++++++++++++++++++++++++++- jperm/ansible_api.py | 16 +++++++ jumpserver/api.py | 6 +++ jumpserver/urls.py | 6 --- jumpserver/views.py | 22 +++++----- templates/download.html | 6 --- templates/upload.html | 8 +++- 7 files changed, 132 insertions(+), 28 deletions(-) diff --git a/connect.py b/connect.py index 9cfc7e063..a522c6446 100644 --- a/connect.py +++ b/connect.py @@ -16,16 +16,17 @@ import django import paramiko import struct, fcntl, signal, socket, select from io import open as copen +import uuid os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() from django.contrib.sessions.models import Session from jumpserver.api import ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info, get_role -from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR +from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR, bash, get_tmp_dir from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm from jumpserver.settings import LOG_DIR -from jperm.ansible_api import Command +from jperm.ansible_api import Command, MyRunner from jlog.log_api import escapeString login_user = get_object(User, username=getpass.getuser()) @@ -484,6 +485,8 @@ class Nav(object): 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[32mU/u\033[0m 批量上传文件. + 7) 输入 \033[32mD/d\033[0m 批量下载文件. 7) 输入 \033[32mQ/q\033[0m 退出. """ print textwrap.dedent(msg) @@ -668,6 +671,91 @@ class Nav(object): log.is_finished = True log.end_time = datetime.datetime.now() + def upload(self): + while True: + if not self.user_perm: + self.user_perm = get_group_user_perm(self.user) + try: + print "请输入主机名、IP或ansile支持的pattern, q退出" + pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip() + if pattern == 'q': + break + else: + assets = self.user_perm.get('asset').keys() + res = gen_resource({'user': self.user, 'asset': assets}, perm=self.user_perm) + runner = MyRunner(res) + logger.debug("Muti upload file res: %s" % res) + asset_name_str = '' + for inv in runner.inventory.get_hosts(pattern=pattern): + print inv.name + asset_name_str += inv.name + print + tmp_dir = get_tmp_dir() + logger.debug('Upload tmp dir: %s' % tmp_dir) + os.chdir(tmp_dir) + bash('rz') + runner = MyRunner(res) + runner.run('copy', module_args='src=%s dest=%s directory_mode' + % (tmp_dir, tmp_dir), pattern=pattern) + ret = runner.get_result() + logger.debug(ret) + if ret.get('failed'): + print ret + error = '上传目录: %s \n上传失败: [ %s ] \n上传成功 [ %s ]' % (tmp_dir, + ', '.join(ret.get('failed').keys()), + ', '.join(ret.get('ok'))) + color_print(error) + else: + msg = '上传目录: %s \n传送成功 [ %s ]' % (tmp_dir, ', '.join(ret.get('ok'))) + color_print(msg, 'green') + print + + except IndexError: + pass + + def download(self): + while True: + if not self.user_perm: + self.user_perm = get_group_user_perm(self.user) + try: + print "进入批量下载模式" + print "请输入主机名、IP或ansile支持的pattern, q退出" + pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip() + if pattern == 'q': + break + else: + assets = self.user_perm.get('asset').keys() + res = gen_resource({'user': self.user, 'asset': assets}, perm=self.user_perm) + runner = MyRunner(res) + logger.debug("Muti Muti file res: %s" % res) + for inv in runner.inventory.get_hosts(pattern=pattern): + print inv.name + print + tmp_dir = get_tmp_dir() + logger.debug('Download tmp dir: %s' % tmp_dir) + while True: + print "请输入文件路径(不支持目录)" + file_path = raw_input("\033[1;32mPath>:\033[0m ").strip() + if file_path == 'q': + break + runner.run('fetch', module_args='src=%s dest=%s' % (file_path, tmp_dir), pattern=pattern) + ret = runner.get_result() + os.chdir('/tmp') + tmp_dir_name = os.path.basename(tmp_dir) + bash('tar czf %s.tar.gz %s && sz %s.tar.gz' % (tmp_dir, tmp_dir_name, tmp_dir)) + + if ret.get('failed'): + print ret + error = '文件名称: %s 下载失败: [ %s ] \n下载成功 [ %s ]' % \ + ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('failed').keys()), ', '.join(ret.get('ok'))) + color_print(error) + else: + msg = '文件名称: %s 下载成功 [ %s ]' % ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('ok'))) + color_print(msg, 'green') + print + except IndexError: + pass + def main(): """ @@ -701,6 +789,10 @@ def main(): elif option in ['E', 'e']: nav.exec_cmd() continue + elif option in ['U', 'u']: + nav.upload() + elif option in ['D', 'd']: + nav.download() elif option in ['Q', 'q', 'exit']: sys.exit() else: diff --git a/jperm/ansible_api.py b/jperm/ansible_api.py index bcd64dee8..8ce990146 100644 --- a/jperm/ansible_api.py +++ b/jperm/ansible_api.py @@ -140,6 +140,22 @@ class MyRunner(MyInventory): self.results = hoc.run() return self.results + def get_result(self): + result = {'failed': {}, 'ok': []} + dark = self.results.get('dark') + contacted = self.results.get('contacted') + if dark: + for host, info in dark.items(): + result['failed'][host] = info.get('msg') + + if contacted: + for host, info in contacted.items(): + if info.get('msg'): + result['failed'][host] = info.get('msg') + else: + result['ok'].append(host) + return result + class Command(MyInventory): """ diff --git a/jumpserver/api.py b/jumpserver/api.py index d36b306d2..d73611d59 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -9,6 +9,7 @@ import hashlib import datetime import random import subprocess +import uuid import json import logging @@ -482,5 +483,10 @@ def my_render(template, data, request): return render_to_response(template, data, context_instance=RequestContext(request)) +def get_tmp_dir(): + dir_name = os.path.join('/tmp', uuid.uuid4().hex) + mkdir(dir_name, mode=0777) + return dir_name + CRYPTOR = PyCrypt(KEY) logger = set_log(LOG_LEVEL) diff --git a/jumpserver/urls.py b/jumpserver/urls.py index 361338fb1..3e3efede8 100644 --- a/jumpserver/urls.py +++ b/jumpserver/urls.py @@ -6,19 +6,13 @@ urlpatterns = patterns('', (r'^$', 'jumpserver.views.index'), (r'^api/user/$', 'jumpserver.api.api_user'), (r'^skin_config/$', 'jumpserver.views.skin_config'), - (r'^install/$', 'jumpserver.views.install'), - (r'^base/$', 'jumpserver.views.base'), (r'^login/$', 'jumpserver.views.Login'), (r'^logout/$', 'jumpserver.views.Logout'), (r'^file/upload/$', 'jumpserver.views.upload'), (r'^file/download/$', 'jumpserver.views.download'), (r'^setting', 'jumpserver.views.setting'), - (r'^error/$', 'jumpserver.views.httperror'), (r'^juser/', include('juser.urls')), (r'^jasset/', include('jasset.urls')), (r'^jlog/', include('jlog.urls')), (r'^jperm/', include('jperm.urls')), - (r'^node_auth/', 'jumpserver.views.node_auth'), - (r'download/(\d{4}/\d\d/\d\d/.*)', 'jumpserver.views.download_file'), - (r'test2', 'jumpserver.views.test2'), ) diff --git a/jumpserver/views.py b/jumpserver/views.py index be47d85d8..1e79c20e6 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -294,7 +294,7 @@ def upload(request): upload_files = request.FILES.getlist('file[]', None) date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") upload_dir = "/tmp/%s/%s" % (user.username, date_now) - mkdir(upload_dir) + mkdir(upload_dir, mode=0777) filenames = {} for asset_id in asset_ids: asset_select.append(get_object(Asset, id=asset_id)) @@ -312,24 +312,22 @@ def upload(request): res = gen_resource({'user': user, 'asset': asset_select}) runner = MyRunner(res) - ret = runner.run('copy', module_args='src=%s dest=%s directory_mode' % (upload_dir, '/tmp/hello/'), pattern='*') + runner.run('copy', module_args='src=%s dest=%s directory_mode' + % (upload_dir, upload_dir), pattern='*') + ret = runner.get_result() logger.debug(ret) - error = '上传失败: ' - if ret.get('dark'): - error += ','.join(ret.get('dark').keys()) - - for asset, info in ret.get('contacted').items(): - if info.get('msg'): - error += ',%s' % asset - if error: + if ret.get('failed'): + error = '上传目录: %s
    上传失败: [ %s ]
    上传成功 [ %s ]' % (upload_dir, + ', '.join(ret.get('failed').keys()), + ', '.join(ret.get('ok'))) return HttpResponse(error, status=500) - return HttpResponse('传送成功') + msg = '上传目录: %s
    传送成功 [ %s ]' % (upload_dir, ', '.join(ret.get('ok'))) + return HttpResponse(msg) return my_render('upload.html', locals(), request) @login_required(login_url='/login') def download(request): - documents = [] return render_to_response('download.html', locals(), context_instance=RequestContext(request)) diff --git a/templates/download.html b/templates/download.html index b1da555cf..fb85a1ec7 100644 --- a/templates/download.html +++ b/templates/download.html @@ -16,12 +16,6 @@ - diff --git a/templates/upload.html b/templates/upload.html index 506c22ac5..9e1e91e5f 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -66,6 +66,8 @@
    + +
    @@ -111,10 +113,12 @@ this.on("sendingmultiple", function() { }); this.on("successmultiple", function(files, response) { - alert(response) + $('#msg').css('display', 'block'); + $('#msg').html(response) }); this.on("errormultiple", function(files, response) { - console.log(response) + $('#error').css('display', 'block'); + $('#error').html(response) }); } From 77da01d1eea32985cc87a85ce30225265b460519 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 2 Dec 2015 15:41:39 +0800 Subject: [PATCH 279/385] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 6 ++++- jumpserver/views.py | 33 ++++++++++++++++++++++-- templates/download.html | 57 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/connect.py b/connect.py index a522c6446..b998bf21c 100644 --- a/connect.py +++ b/connect.py @@ -694,6 +694,10 @@ class Nav(object): logger.debug('Upload tmp dir: %s' % tmp_dir) os.chdir(tmp_dir) bash('rz') + check_notempty = os.listdir(tmp_dir) + if not check_notempty: + print color_print("上传文件为空") + continue runner = MyRunner(res) runner.run('copy', module_args='src=%s dest=%s directory_mode' % (tmp_dir, tmp_dir), pattern=pattern) @@ -742,7 +746,7 @@ class Nav(object): ret = runner.get_result() os.chdir('/tmp') tmp_dir_name = os.path.basename(tmp_dir) - bash('tar czf %s.tar.gz %s && sz %s.tar.gz' % (tmp_dir, tmp_dir_name, tmp_dir)) + bash('tar czf %s.tar.gz %s ' % (tmp_dir, tmp_dir_name)) if ret.get('failed'): print ret diff --git a/jumpserver/views.py b/jumpserver/views.py index 1e79c20e6..04e906b2d 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -293,8 +293,7 @@ def upload(request): asset_ids = request.POST.getlist('asset_ids', '') upload_files = request.FILES.getlist('file[]', None) date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") - upload_dir = "/tmp/%s/%s" % (user.username, date_now) - mkdir(upload_dir, mode=0777) + upload_dir = get_tmp_dir() filenames = {} for asset_id in asset_ids: asset_select.append(get_object(Asset, id=asset_id)) @@ -328,6 +327,36 @@ def upload(request): @login_required(login_url='/login') def download(request): + user = request.user + assets = get_group_user_perm(user).get('asset').keys() + + asset_select = [] + if request.method == 'POST': + asset_ids = request.POST.getlist('asset_ids', '') + file_path = request.POST.get('file_path') + date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + upload_dir = get_tmp_dir() + for asset_id in asset_ids: + asset_select.append(get_object(Asset, id=asset_id)) + + if not set(asset_select).issubset(set(assets)): + illegal_asset = set(asset_select).issubset(set(assets)) + return HttpResponse('没有权限的服务器 %s' % ','.join([asset.hostname for asset in illegal_asset])) + res = gen_resource({'user': user, 'asset': asset_select}) + runner = MyRunner(res) + runner.run('fetch', module_args='src=%s dest=%s' % (file_path, upload_dir), pattern='*') + logger.debug(runner.get_result()) + os.chdir('/tmp') + tmp_dir_name = os.path.basename(upload_dir) + tar_file = '%s.tar.gz' % upload_dir + bash('tar czf %s %s && sz %s.tar.gz' % (tar_file, tmp_dir_name, upload_dir)) + f = open(tar_file) + data = f.read() + f.close() + response = HttpResponse(data, content_type='application/octet-stream') + response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(tar_file) + return response + return render_to_response('download.html', locals(), context_instance=RequestContext(request)) diff --git a/templates/download.html b/templates/download.html index fb85a1ec7..0c001d8d6 100644 --- a/templates/download.html +++ b/templates/download.html @@ -1,5 +1,10 @@ {% extends 'base.html' %} {% load mytags %} +{% block self_head_css_js %} + + + +{% endblock %} {% block content %} {% include 'nav_cat_bar.html' %} @@ -22,18 +27,56 @@
    -

    下载文件可联系管理员在服务器安装lrzsz,使用sz命令下载。

    -
    - {% for document in documents %} - {{ document }} - {% endfor %} -
    - + + {% if error %} +
    {{ error }}
    + {% endif %} + {% if msg %} +
    {{ msg }}
    + {% endif %} +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + + +
    +
    +
    +{% endblock %} +{% block self_footer_js %} + {% endblock %} \ No newline at end of file From 1f26e49fb8231d4da15187ff5d47027bcc3b5be3 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 2 Dec 2015 19:16:05 +0800 Subject: [PATCH 280/385] =?UTF-8?q?=E5=AE=8C=E6=88=90web=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 2 +- jumpserver/urls.py | 1 + jumpserver/views.py | 5 +++ run_websocket.py | 76 ++++++++++++++++++++++++-------- templates/jasset/asset_list.html | 41 +++++++++++++++++ templates/jlog/log_monitor.html | 56 ----------------------- 6 files changed, 106 insertions(+), 75 deletions(-) delete mode 100644 templates/jlog/log_monitor.html diff --git a/connect.py b/connect.py index b998bf21c..83db6b855 100644 --- a/connect.py +++ b/connect.py @@ -24,7 +24,7 @@ if django.get_version() != '1.6': from django.contrib.sessions.models import Session from jumpserver.api import ServerError, User, Asset, PermRole, AssetGroup, get_object, mkdir, get_asset_info, get_role from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR, bash, get_tmp_dir -from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm +from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm, user_have_perm, PermRole from jumpserver.settings import LOG_DIR from jperm.ansible_api import Command, MyRunner from jlog.log_api import escapeString diff --git a/jumpserver/urls.py b/jumpserver/urls.py index 3e3efede8..3be4e631a 100644 --- a/jumpserver/urls.py +++ b/jumpserver/urls.py @@ -8,6 +8,7 @@ urlpatterns = patterns('', (r'^skin_config/$', 'jumpserver.views.skin_config'), (r'^login/$', 'jumpserver.views.Login'), (r'^logout/$', 'jumpserver.views.Logout'), + (r'^exec_cmd/$', 'jumpserver.views.exec_cmd'), (r'^file/upload/$', 'jumpserver.views.upload'), (r'^file/download/$', 'jumpserver.views.download'), (r'^setting', 'jumpserver.views.setting'), diff --git a/jumpserver/views.py b/jumpserver/views.py index 04e906b2d..5eb0cac80 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -360,3 +360,8 @@ def download(request): return render_to_response('download.html', locals(), context_instance=RequestContext(request)) +@login_required(login_url='/login') +def exec_cmd(request): + role_name = request.GET.get('role_name') + web_terminal_uri = 'ws://%s/exec?role=%s' % (WEB_SOCKET_HOST, role_name) + return my_render('exec_cmd.html', locals(), request) diff --git a/run_websocket.py b/run_websocket.py index 742220c6d..0bdd609cf 100644 --- a/run_websocket.py +++ b/run_websocket.py @@ -23,8 +23,8 @@ from tornado.options import define, options from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE, IN_MODIFY, AsyncNotifier import select -from connect import Tty, User, Asset, PermRole, logger, get_object -from connect import TtyLog, Log, Session, user_have_perm +from connect import Tty, User, Asset, PermRole, logger, get_object, PermRole, gen_resource +from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, Command try: import simplejson as json @@ -67,22 +67,6 @@ def require_auth(role='user'): except AttributeError: pass logger.warning('Websocket: Request auth failed.') - # asset_id = int(request.get_argument('id', 9999)) - # print asset_id - # asset = Asset.objects.filter(id=asset_id) - # if asset: - # asset = asset[0] - # request.asset = asset - # else: - # request.close() - # - # if user: - # user = user[0] - # request.user = user - # - # else: - # print("No session user.") - # request.close() return _deco2 return _deco @@ -138,6 +122,7 @@ class Application(tornado.web.Application): (r'/monitor', MonitorHandler), (r'/terminal', WebTerminalHandler), (r'/kill', WebTerminalKillHandler), + (r'/exec', ExecHandler), ] setting = { @@ -225,6 +210,61 @@ class WebTerminalKillHandler(tornado.web.RequestHandler): logger.debug('Websocket: web terminal client num: %s' % len(WebTerminalHandler.clients)) +class ExecHandler(tornado.websocket.WebSocketHandler): + clients = [] + tasks = [] + + def __init__(self, *args, **kwargs): + self.id = 0 + self.user = None + self.role = None + self.cmd = None + self.assets = [] + self.perm = {} + super(ExecHandler, self).__init__(*args, **kwargs) + + def check_origin(self, origin): + return True + + @require_auth('user') + def open(self): + logger.debug('Websocket: Open exec request') + role_name = self.get_argument('role', 'dev') + self.role = get_object(PermRole, name=role_name) + self.perm = get_group_user_perm(self.user) + roles = self.perm.get('role').keys() + if self.role not in roles: + self.write_message('No perm that role %s' % role_name) + self.close() + self.assets = self.perm.get('role').get(self.role).get('asset') + res = gen_resource({'user': self.user, 'asset': self.assets, 'role': self.role}) + logger.debug('Web执行命令res: %s' % res) + self.cmd = Command(res) + message = '有权限的主机:' + ', '.join([asset.hostname for asset in self.assets]) + self.write_message(message) + + def on_message(self, message): + data = json.loads(message) + pattern = data.get('pattern', '') + command = data.get('command', '') + asset_name_str = '匹配主机: ' + if pattern and command: + for inv in self.cmd.inventory.get_hosts(pattern=pattern): + asset_name_str += '\n%s' % inv.name + self.write_message(asset_name_str) + self.write_message('Ansible> %s\n\n' % command) + result = self.cmd.run(module_name='shell', command=command, pattern=pattern) + for k, v in result.items(): + for host, output in v.items(): + if k == 'ok': + header = "[ %s => %s]\n" % (host, 'Ok') + else: + header = "[ %s => %s]\n" % (host, 'failed') + self.write_message(header) + self.write_message(output) + self.write_message('\n\n') + + class WebTerminalHandler(tornado.websocket.WebSocketHandler): clients = [] tasks = [] diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 8373f8a0f..3f9c6c6cf 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -148,6 +148,7 @@ 修改 + {% include 'paginator.html' %} @@ -175,6 +176,46 @@ } }); + $('#exec_cmd').click(function(){ + var url='/jlog/get_role_name/?id={{ user.id }}'; + var href = $(this).attr('href'); + $.ajax({ + type: 'GET', + url: url, + data: {}, + success: function(data){ + var dataArray = data.split(','); + if (dataArray.length == 1 && data != 'error'){ + var title = 'Jumpserver Exec Terminal'; + layer.open({ + type: 2, + title: title, + maxmin: true, + shade: false, + area: ['628px', '452px'], + content: new_url+data + }); + //window.open(new_url + data, '', 'location=no, resizeable=no, height=410, width=625, top=89px, left=99px,toolbar=no,menubar=no,scrollbars=auto,status=no'); + } else if (dataArray.length == '1' && data == 'error'){ + layer.alert('没有授权角色') + } else { + aUrl = ''; + $.each(dataArray, function(index, value){ + aUrl += '' + value + ' ' + }); + layer.alert(aUrl, { + skin: 'layui-layer-molv', + title: '多个角色,请选择一个连接', + shade: false, + closeBtn: 0 + }) + } + } + }); + return false + + }); + $('.conn').click(function(){ var url='/jlog/get_role_name/?id=' + $(this).attr('value'); var href = $(this).attr('href'); diff --git a/templates/jlog/log_monitor.html b/templates/jlog/log_monitor.html deleted file mode 100644 index 8f3c1ef3d..000000000 --- a/templates/jlog/log_monitor.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - Jumpserver | 开源跳板机系统 - - - {% include 'link_css.html' %} - {% include 'head_script.html' %} - - - - -
    -
    -
    -
    -
    实时监控
    -
    - -
    - 你好
    -
    -
    -
    -
    - - - {% block self_footer_js %} - - {% endblock %} - From 88fbcabcbb35736b7426fc6a5e55f778b14d3822 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 2 Dec 2015 19:17:12 +0800 Subject: [PATCH 281/385] exec-cmd tempalate --- templates/exec_cmd.html | 198 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 templates/exec_cmd.html diff --git a/templates/exec_cmd.html b/templates/exec_cmd.html new file mode 100644 index 000000000..6b9753c97 --- /dev/null +++ b/templates/exec_cmd.html @@ -0,0 +1,198 @@ + + + + + Chat + + + + +
    +
    +

    +
    + + +
    + + + + + + +
    +
    + \ No newline at end of file From f6dbec1436c13c3d9a79eb0cbc088e99b9962a65 Mon Sep 17 00:00:00 2001 From: halcyon <864072399@qq.com> Date: Wed, 2 Dec 2015 23:39:17 +0800 Subject: [PATCH 282/385] day update --- jasset/asset_api.py | 11 +- jasset/views.py | 14 +++ jumpserver/templatetags/mytags.py | 13 +++ templates/jasset/asset_detail.html | 157 ++++++++++++++++++----------- templates/jasset/asset_list.html | 15 ++- 5 files changed, 139 insertions(+), 71 deletions(-) diff --git a/jasset/asset_api.py b/jasset/asset_api.py index 2c8446f70..1e8083062 100644 --- a/jasset/asset_api.py +++ b/jasset/asset_api.py @@ -312,7 +312,14 @@ def get_ansible_asset_info(asset_ip, setup_info): disk_need = {} for disk_name, disk_info in disk_all.iteritems(): if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'): - disk_need[disk_name] = disk_info.get("size") + disk_size = disk_info.get("size") + if 'M' in disk_size: + disk_format = round(float(disk_size[:-2]) / 1000, 0) + elif 'T' in disk_size: + disk_format = round(float(disk_size[:-2]) * 1000, 0) + else: + disk_format = float(disk_size) + disk_need[disk_name] = disk_format all_ip = setup_info.get("ansible_all_ipv4_addresses") other_ip_list = all_ip.remove(asset_ip) if asset_ip in all_ip else [] other_ip = ','.join(other_ip_list) if other_ip_list else '' @@ -325,7 +332,7 @@ def get_ansible_asset_info(asset_ip, setup_info): cpu = cpu_type + ' * ' + unicode(cpu_cores) memory = setup_info.get("ansible_memtotal_mb") try: - memory_format = round((int(memory) / 1000), 1) + memory_format = int(round((int(memory) / 1000), 0)) except Exception: memory_format = memory disk = disk_need diff --git a/jasset/views.py b/jasset/views.py index dff5621d8..ac8f166dc 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -6,6 +6,7 @@ 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 jperm.perm_api import get_group_asset_perm from jperm.ansible_api import Tasks, MyRunner from jperm.perm_api import gen_resource @@ -410,6 +411,19 @@ def asset_detail(request): header_title, path1, path2 = u'主机详细信息', u'资产管理', u'主机详情' asset_id = request.GET.get('id', '') asset = get_object(Asset, id=asset_id) + perm_info = get_group_asset_perm(asset) + log = Log.objects.filter(host=asset.hostname) + if perm_info: + user_perm = [] + for perm, value in perm_info.items(): + if perm == 'user': + for user, role_dic in value.items(): + user_perm.append([user, role_dic.get('role', '')]) + elif perm == 'user_group': + user_group_perm = value + elif perm == 'rule': + user_rule_perm = value + asset_record = AssetRecord.objects.filter(asset=asset).order_by('-alert_time') return my_render('jasset/asset_detail.html', locals(), request) diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index b23a14b96..880fd2b12 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -277,3 +277,16 @@ def get_push_info(push_id, arg): @register.filter(name='get_cpu_core') def get_cpu_core(cpu_info): return cpu_info.split('* ')[1] if cpu_info else '' + +@register.filter(name='get_disk_info') +def get_disk_info(disk_info): + try: + disk_size = 0 + if disk_info: + disk_dic = ast.literal_eval(disk_info) + for disk, size in disk_dic.items(): + disk_size += size + disk_size = int(disk_size) + except Exception: + disk_size = '' + return disk_size diff --git a/templates/jasset/asset_detail.html b/templates/jasset/asset_detail.html index 7109888eb..c312ac01d 100644 --- a/templates/jasset/asset_detail.html +++ b/templates/jasset/asset_detail.html @@ -186,26 +186,65 @@
    -

    主机所有授权的用户

    - 包含了此主机所有授权的用户. +

    主机所有授权的信息

    + 包含了此主机所有授权的信息.
    -{#
    #} -{# #} -{# {% if user_permed_list %}#} -{# {% for user in user_permed_list %}#} -{# #} -{# #} -{# #} -{# #} -{# #} -{# {% endfor %}#} -{# {% else %}#} -{#

    (暂无)

    #} -{# {% endif %}#} -{#
    {{ user.name }}{{ user.dept.name }}详情
    #} -{#
    #} +
    + {% if perm_info %} + +

    授权用户信息

    + + + {% for perm in user_perm %} + + + + + {% endfor %} +
    授权用户系统角色
    {{ perm.0 }} + + {% if perm.1 %} + {% for role in perm.1 %} + + + + {% endfor %} + {% endif %} +
    {{ role }}
    +
    + {% if user_group_perm %} + +

    授权用户组信息

    + + + {% for user_group in user_group_perm %} + + + + + {% endfor %} +
    授权用户组组详情
    {{ user_group }}详情
    + {% endif %} + + {% if user_rule_perm %} + +

    授权规则信息

    + + + {% for rule in user_rule_perm %} + + + + + {% endfor %} +
    授权规则详情
    {{ rule }}详情
    + {% endif %} + {% else %} +

    (暂无)

    + {% endif %} +
    @@ -275,49 +314,47 @@ 此主机最近一周用户登录信息.
    -{# {% if log %}#} -{# {% for l in log %}#} -{#
    #} -{#
    #} -{#
    #} -{# #} -{# {{ l.user }}#} -{#
    #} -{# {{l.dept_name}}#} -{#
    #} -{#
    #} -{#

    详细信息

    #} -{#

    来源IP: {{ l.remote_ip }}

    #} -{#

    开始: {{ l.start_time |date:"Y-m-d H:i:s" }}

    #} -{#

    结束: {{ l.end_time |date:"Y-m-d H:i:s" }}

    #} -{#
    #} -{#
    #} -{#
    #} -{# {% endfor %}#} -{# #} -{# diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 3f9c6c6cf..b47f7ee24 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -121,22 +121,19 @@
    {{ asset.hostname|default_if_none:"" }} {{ asset.hostname|default_if_none:"" }} {{ asset.ip|default_if_none:"" }} {{ asset.idc.name|default_if_none:"" }} {{ asset.group.all|group_str2 }}{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }} {{ asset.cpu|get_cpu_core }} {{ asset.memory }}{% if asset.memory %}G{% endif %} {{ asset.disk }} {{ asset.memory }}{% if asset.memory %}G{% endif %} {{ asset.disk|get_disk_info }}{% if asset.memory %}G{% endif %} - 详情 - {% ifnotequal session_role_id 0 %} - 编辑 - 连接 - 删除 - {% endifnotequal %} + 编辑 + 连接 + 删除
    + + + + + + + + + + + + {% for post in contacts.object_list %} + + + + + + + {% ifnotequal session_role_id 0 %} + + {% endifnotequal %} + + + + + {% endfor %} + +
    ID 用户名 主机 来源IP 时间
    {{ post.id }} {{ post.user }} {{ post.host }} {{ post.remote_ip }} {{ post.login_type }} 统计 回放 {{ post.start_time|date:"Y-m-d H:i:s"}} {{ post.end_time|date:"Y-m-d H:i:s" }}
    +
    +
    +
    + {% include 'paginator.html' %} +
    +
    +
    +
    +
    +
    + + + +{% endblock %} From 33663783cc4d1569bccf775dbdcc3f1a4ddf5cfc Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 3 Dec 2015 18:49:04 +0800 Subject: [PATCH 288/385] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 27 ++++----------------------- jlog/models.py | 1 + jperm/views.py | 13 +++++++------ run_websocket.py | 4 +++- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/connect.py b/connect.py index 3de7b3613..0e0f6b35a 100644 --- a/connect.py +++ b/connect.py @@ -31,6 +31,7 @@ from jperm.ansible_api import Command, MyRunner from jlog.models import ExecLog login_user = get_object(User, username=getpass.getuser()) +remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n') try: import termios @@ -228,8 +229,6 @@ class Tty(object): raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) try: - # log_file_f = copen(log_file_path + '.log', mode='at', encoding='utf-8', errors='replace') - # log_time_f = copen(log_file_path + '.time', mode='at', encoding='utf-8', errors='replace') log_file_f = open(log_file_path + '.log', 'a') log_time_f = open(log_file_path + '.time', 'a') except IOError: @@ -238,13 +237,12 @@ class Tty(object): if self.login_type == 'ssh': # 如果是ssh连接过来,记录connect.py的pid,web terminal记录为日志的id pid = os.getpid() - self.remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n') # 获取远端IP + self.remote_ip = remote_ip # 获取远端IP else: pid = 0 log = Log(user=self.username, host=self.asset_name, remote_ip=self.remote_ip, login_type=self.login_type, log_path=log_file_path, start_time=date_today, pid=pid) - log.save() if self.login_type == 'web': log.pid = log.id @@ -422,9 +420,6 @@ class SshTty(Tty): Connect server. 连接服务器 """ - ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip - # 发起ssh连接请求 Make a ssh connection ssh = self.get_connection() @@ -436,20 +431,6 @@ class SshTty(Tty): signal.signal(signal.SIGWINCH, self.set_win_size) except: pass - - # 设置PS1并提示 Set PS1 and msg it - #channel.send(ps1) - #channel.send(login_msg) - # channel.send('echo ${SSH_TTY}\n') - # global SSH_TTY - # while not channel.recv_ready(): - # time.sleep(1) - # tmp = channel.recv(1024) - #print 'ok'+tmp+'ok' - # SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip() - # SSH_TTY = '' - # channel.send('clear\n') - # Make ssh interactive tunnel self.posix_shell() # Shutdown channel socket @@ -559,7 +540,7 @@ class Nav(object): color_print('错误输入') else: role = role_check[int(role_id)] - elif len(roles) == 1: # 授权角色数为1 + elif len(roles) == 1: # 授权角色数为1 role = roles[0] assets = list(self.user_perm.get('role', {}).get(role).get('asset')) # 获取该用户,角色授权主机 print "该角色有权限的所有主机" @@ -584,7 +565,7 @@ class Nav(object): while True: print "请输入执行的命令, 按q退出" command = raw_input("\033[1;32mCmds>:\033[0m ").strip() - ExecLog(host=asset_name_str, cmd=command).save() + ExecLog(host=asset_name_str, user=self.user.username, cmd=command, remote_ip=remote_ip).save() if command == 'q': break runner.run('shell', command, pattern=pattern) diff --git a/jlog/models.py b/jlog/models.py index 0df3360bf..f37105216 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -32,5 +32,6 @@ class ExecLog(models.Model): user = models.CharField(max_length=100) host = models.TextField() cmd = models.TextField() + remote_ip = models.CharField(max_length=100) datetime = models.DateTimeField(auto_now=True) diff --git a/jperm/views.py b/jperm/views.py index da33d7ff7..ce0de68a2 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -444,13 +444,14 @@ def perm_role_push(request): if password_push or key_push: role_chosen_aliase = {} # {'dev': 'NETWORKING, SHUTDOWN'} sudo_alias = set([sudo for sudo in role.sudo.all()]) # set(sudo1, sudo2, sudo3) - role_chosen_aliase[role.name] = ','.join(sudo.name for sudo in sudo_alias) - add_sudo_script = get_add_sudo_script(role_chosen_aliase, sudo_alias) - ret['sudo'] = task.push_sudo_file(add_sudo_script) + if sudo_alias: + role_chosen_aliase[role.name] = ','.join(sudo.name for sudo in sudo_alias if sudo.name) + add_sudo_script = get_add_sudo_script(role_chosen_aliase, sudo_alias) + ret['sudo'] = task.push_sudo_file(add_sudo_script) - if ret['sudo'].get('msg'): - ret_failed = ret['sudo'].get('msg') - # os.remove(add_sudo_script) + if ret['sudo'].get('msg'): + ret_failed = ret['sudo'].get('msg') + os.remove(add_sudo_script) logger.debug('推送role结果: %s' % ret) logger.debug('推送role错误: %s' % ret_failed) diff --git a/run_websocket.py b/run_websocket.py index 5d84ef1c3..fd8090813 100644 --- a/run_websocket.py +++ b/run_websocket.py @@ -221,6 +221,7 @@ class ExecHandler(tornado.websocket.WebSocketHandler): self.runner = None self.assets = [] self.perm = {} + self.remote_ip = '' super(ExecHandler, self).__init__(*args, **kwargs) def check_origin(self, origin): @@ -230,6 +231,7 @@ class ExecHandler(tornado.websocket.WebSocketHandler): def open(self): logger.debug('Websocket: Open exec request') role_name = self.get_argument('role', 'sb') + self.remote_ip = self.request.remote_ip logger.debug('Web执行命令: 请求角色 %s' % role_name) self.role = get_object(PermRole, name=role_name) self.perm = get_group_user_perm(self.user) @@ -256,7 +258,7 @@ class ExecHandler(tornado.websocket.WebSocketHandler): self.write_message('匹配主机: ' + asset_name_str) self.write_message('Ansible> %s\n\n' % command) self.__class__.tasks.append(MyThread(target=self.run_cmd, args=(command, pattern))) - ExecLog(host=asset_name_str, cmd=command).save() + ExecLog(host=asset_name_str, cmd=command, user=self.user.username, remote_ip=self.remote_ip).save() for t in self.__class__.tasks: if t.is_alive(): From 9233fef63fae38da0d126c4a5f1148ff670c8bee Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 3 Dec 2015 19:10:37 +0800 Subject: [PATCH 289/385] exec log --- jlog/urls.py | 13 ++- jlog/views.py | 6 +- templates/jlog/log_exec.html | 171 ++++++++++++-------------------- templates/jlog/log_offline.html | 1 + templates/jlog/log_online.html | 1 + templates/juser/user_list.html | 22 ++-- 6 files changed, 84 insertions(+), 130 deletions(-) diff --git a/jlog/urls.py b/jlog/urls.py index e9a3063bd..956fbf839 100644 --- a/jlog/urls.py +++ b/jlog/urls.py @@ -3,11 +3,10 @@ from django.conf.urls import patterns, include, url from jlog.views import * urlpatterns = patterns('', - url(r'^$', log_list), - url(r'^log_list/(\w+)/$', log_list), - url(r'^history/$', log_history), - url(r'^log_kill/', log_kill), - url(r'^record/$', log_record), - url(r'^web_terminal/$', web_terminal), - + (r'^$', log_list), + (r'^log_list/(\w+)/$', log_list), + (r'^history/$', log_history), + (r'^log_kill/', log_kill), + (r'^record/$', log_record), + (r'^web_terminal/$', web_terminal), ) \ No newline at end of file diff --git a/jlog/views.py b/jlog/views.py index 462fdf1e8..85d953f86 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -8,7 +8,7 @@ from jperm.perm_api import user_have_perm from django.http import HttpResponseNotFound from jlog.log_api import renderTemplate -from models import Log +from jlog.models import Log, ExecLog from jumpserver.settings import WEB_SOCKET_HOST @@ -21,9 +21,11 @@ def log_list(request, offset): username_list = request.GET.getlist('username', []) host_list = request.GET.getlist('host', []) cmd = request.GET.get('cmd', '') - print date_seven_day, date_now_str + if offset == 'online': posts = Log.objects.filter(is_finished=False).order_by('-start_time') + if offset == 'exec': + posts = ExecLog.objects.all().order_by('-id') else: posts = Log.objects.filter(is_finished=True).order_by('-start_time') username_all = set([log.user for log in Log.objects.all()]) diff --git a/templates/jlog/log_exec.html b/templates/jlog/log_exec.html index 024d88c5c..d1e096c82 100644 --- a/templates/jlog/log_exec.html +++ b/templates/jlog/log_exec.html @@ -7,125 +7,76 @@ {% block content %} {% include 'nav_cat_bar.html' %} - -
    -ol-sm/g -
    -
    -
    用户日志详细信息列表
    - +
    +
    +
    批量命令日志
    + +
    -
    - -
    -
    -
    -
    - - to - +
    + +
    +
    + +
    + +
    +
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    - -
    - -
    - - - - - - - - - - - + +
    +
    ID 用户名 主机 来源IP 时间
    + + + + + + + + + + + - {% for post in contacts.object_list %} - - - - - - - {% ifnotequal session_role_id 0 %} - - {% endifnotequal %} - - - - - {% endfor %} - -
    ID 用户名 主机 命令 来源IP 时间
    {{ post.id }} {{ post.user }} {{ post.host }} {{ post.remote_ip }} {{ post.login_type }} 统计 回放 {{ post.start_time|date:"Y-m-d H:i:s"}} {{ post.end_time|date:"Y-m-d H:i:s" }}
    -
    -
    -
    - {% include 'paginator.html' %} + {% for post in contacts.object_list %} + + {{ post.id }} + {{ post.user }} + {{ post.host }} + {{ post.cmd }} + {{ post.remote_ip }} + {{ post.datetime|date:"Y-m-d H:i:s"}} + + {% endfor %} + + +
    +
    -
    + {% include 'paginator.html' %} +
    diff --git a/templates/jlog/log_offline.html b/templates/jlog/log_offline.html index 6559dc344..66184cbe7 100644 --- a/templates/jlog/log_offline.html +++ b/templates/jlog/log_offline.html @@ -54,6 +54,7 @@

    diff --git a/templates/jlog/log_online.html b/templates/jlog/log_online.html index d214d39a9..686091a40 100644 --- a/templates/jlog/log_online.html +++ b/templates/jlog/log_online.html @@ -69,6 +69,7 @@

    diff --git a/templates/juser/user_list.html b/templates/juser/user_list.html index 38b726168..e7fef1231 100644 --- a/templates/juser/user_list.html +++ b/templates/juser/user_list.html @@ -24,18 +24,18 @@
    - 添加用户 - 删除所选 - From 6975acfc8acaca3e5844bf1c70f33ec03f16187f Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 3 Dec 2015 23:24:34 +0800 Subject: [PATCH 290/385] =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 33 +++-- jlog/models.py | 10 ++ jlog/views.py | 17 ++- jumpserver/views.py | 18 ++- templates/jlog/exec_detail.html | 200 ++++++++++++++++++++++++++ templates/jlog/log_exec.html | 132 +++++++++-------- templates/jlog/log_file.html | 109 ++++++++++++++ templates/jlog/log_offline.html | 1 + templates/jlog/log_online.html | 1 + templates/jperm/perm_role_detail.html | 10 +- templates/nav.html | 9 +- 11 files changed, 455 insertions(+), 85 deletions(-) create mode 100644 templates/jlog/exec_detail.html create mode 100644 templates/jlog/log_file.html diff --git a/connect.py b/connect.py index 0e0f6b35a..7111436f4 100644 --- a/connect.py +++ b/connect.py @@ -28,7 +28,7 @@ from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_pe from jumpserver.settings import LOG_DIR from jperm.ansible_api import Command, MyRunner # from jlog.log_api import escapeString -from jlog.models import ExecLog +from jlog.models import ExecLog, FileLog login_user = get_object(User, username=getpass.getuser()) remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n') @@ -601,16 +601,23 @@ class Nav(object): print "匹配主机:\n" for inv in runner.inventory.get_hosts(pattern=pattern): print inv.name - asset_name_str += inv.name - print + asset_name_str += '%s ' % inv.name + + if not asset_name_str: + color_print('没有匹配主机') + print + continue tmp_dir = get_tmp_dir() logger.debug('Upload tmp dir: %s' % tmp_dir) os.chdir(tmp_dir) bash('rz') - check_notempty = os.listdir(tmp_dir) - if not check_notempty: + filename_str = ' '.join(os.listdir(tmp_dir)) + if not filename_str: print color_print("上传文件为空") continue + logger.debug('上传文件: %s' % filename_str) + FileLog(user=self.user.name, host=asset_name_str, filename=filename_str, + remote_ip=remote_ip, type='upload').save() runner = MyRunner(res) runner.run('copy', module_args='src=%s dest=%s directory_mode' % (tmp_dir, tmp_dir), pattern=pattern) @@ -644,9 +651,14 @@ class Nav(object): res = gen_resource({'user': self.user, 'asset': assets}, perm=self.user_perm) runner = MyRunner(res) logger.debug("Muti download file res: %s" % res) + asset_name_str = '' print "匹配用户:\n" for inv in runner.inventory.get_hosts(pattern=pattern): - print inv.name + asset_name_str += '%s ' % inv.name + print ' %s' % inv.name + if not asset_name_str: + color_print('没有匹配主机') + continue print while True: tmp_dir = get_tmp_dir() @@ -655,19 +667,24 @@ class Nav(object): file_path = raw_input("\033[1;32mPath>:\033[0m ").strip() if file_path == 'q': break + FileLog(user=self.user.name, host=asset_name_str, filename=file_path, type='download', + remote_ip=remote_ip).save() runner.run('fetch', module_args='src=%s dest=%s' % (file_path, tmp_dir), pattern=pattern) ret = runner.results logger.debug('Download file result: %s' % ret) os.chdir('/tmp') tmp_dir_name = os.path.basename(tmp_dir) + if not os.listdir(tmp_dir): + color_print('下载全部失败') + continue bash('tar czf %s.tar.gz %s && sz %s.tar.gz' % (tmp_dir, tmp_dir_name, tmp_dir)) if ret.get('failed'): - error = '文件名称: %s 下载失败: [ %s ] \n下载成功 [ %s ]' % \ + error = '文件名称: %s \n下载失败: [ %s ] \n下载成功 [ %s ]' % \ ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('failed').keys()), ', '.join(ret.get('ok').keys())) color_print(error) else: - msg = '文件名称: %s 下载成功 [ %s ]' % ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('ok').keys())) + msg = '文件名称: %s \n下载成功 [ %s ]' % ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('ok').keys())) color_print(msg, 'green') print except IndexError: diff --git a/jlog/models.py b/jlog/models.py index f37105216..ef1f81a33 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -35,3 +35,13 @@ class ExecLog(models.Model): remote_ip = models.CharField(max_length=100) datetime = models.DateTimeField(auto_now=True) + +class FileLog(models.Model): + user = models.CharField(max_length=100) + host = models.TextField() + filename = models.TextField() + type = models.CharField(max_length=20) + remote_ip = models.CharField(max_length=100) + datetime = models.DateTimeField(auto_now=True) + + diff --git a/jlog/views.py b/jlog/views.py index 85d953f86..9b492fa9a 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -8,7 +8,7 @@ from jperm.perm_api import user_have_perm from django.http import HttpResponseNotFound from jlog.log_api import renderTemplate -from jlog.models import Log, ExecLog +from jlog.models import Log, ExecLog, FileLog from jumpserver.settings import WEB_SOCKET_HOST @@ -24,8 +24,16 @@ def log_list(request, offset): if offset == 'online': posts = Log.objects.filter(is_finished=False).order_by('-start_time') - if offset == 'exec': + elif offset == 'exec': posts = ExecLog.objects.all().order_by('-id') + keyword = request.GET.get('keyword', '') + if keyword: + posts = posts.filter(Q(user__icontains=keyword)|Q(host__icontains=keyword)|Q(cmd__icontains=keyword)) + elif offset == 'file': + posts = FileLog.objects.all().order_by('-id') + keyword = request.GET.get('keyword', '') + if keyword: + posts = posts.filter(Q(user__icontains=keyword)|Q(host__icontains=keyword)|Q(filename__icontains=keyword)) else: posts = Log.objects.filter(is_finished=True).order_by('-start_time') username_all = set([log.user for log in Log.objects.all()]) @@ -59,6 +67,11 @@ def log_list(request, offset): return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request)) +@require_role('admin') +def log_detail(request): + return my_render('jlog/exec_detail.html', locals(), request) + + @require_role('admin') def log_kill(request): """ 杀掉connect进程 """ diff --git a/jumpserver/views.py b/jumpserver/views.py index 20003e9c3..cd646214f 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -15,7 +15,7 @@ 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 jlog.models import Log +from jlog.models import Log, FileLog from jperm.perm_api import get_group_user_perm, gen_resource from jasset.models import Asset, IDC from jperm.ansible_api import MyRunner @@ -287,14 +287,14 @@ def setting(request): def upload(request): user = request.user assets = get_group_user_perm(user).get('asset').keys() - asset_select = [] if request.method == 'POST': + remote_ip = request.META.get('REMOTE_ADDR') asset_ids = request.POST.getlist('asset_ids', '') upload_files = request.FILES.getlist('file[]', None) date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") upload_dir = get_tmp_dir() - filenames = {} + # file_dict = {} for asset_id in asset_ids: asset_select.append(get_object(Asset, id=asset_id)) @@ -302,9 +302,12 @@ def upload(request): illegal_asset = set(asset_select).issubset(set(assets)) return HttpResponse('没有权限的服务器 %s' % ','.join([asset.hostname for asset in illegal_asset])) + FileLog(user=request.user.username, host=' '.join([asset.hostname for asset in asset_select]), + filename=' '.join([f.name for f in upload_files]), type='upload', remote_ip=remote_ip).save() + for upload_file in upload_files: file_path = '%s/%s' % (upload_dir, upload_file.name) - filenames[upload_file.name] = file_path + # file_dict[upload_file.name] = file_path with open(file_path, 'w') as f: for chunk in upload_file.chunks(): f.write(chunk) @@ -329,9 +332,9 @@ def upload(request): def download(request): user = request.user assets = get_group_user_perm(user).get('asset').keys() - asset_select = [] if request.method == 'POST': + remote_ip = request.META.get('REMOTE_ADDR') asset_ids = request.POST.getlist('asset_ids', '') file_path = request.POST.get('file_path') date_now = datetime.datetime.now().strftime("%Y%m%d%H%M%S") @@ -342,6 +345,9 @@ def download(request): if not set(asset_select).issubset(set(assets)): illegal_asset = set(asset_select).issubset(set(assets)) return HttpResponse('没有权限的服务器 %s' % ','.join([asset.hostname for asset in illegal_asset])) + + FileLog(user=request.user.username, host=' '.join([asset.hostname for asset in asset_select]), + filename=file_path, type='download', remote_ip=remote_ip).save() res = gen_resource({'user': user, 'asset': asset_select}) runner = MyRunner(res) runner.run('fetch', module_args='src=%s dest=%s' % (file_path, upload_dir), pattern='*') @@ -349,7 +355,7 @@ def download(request): os.chdir('/tmp') tmp_dir_name = os.path.basename(upload_dir) tar_file = '%s.tar.gz' % upload_dir - bash('tar czf %s %s && sz %s.tar.gz' % (tar_file, tmp_dir_name, upload_dir)) + bash('tar czf %s %s' % (tar_file, tmp_dir_name)) f = open(tar_file) data = f.read() f.close() diff --git a/templates/jlog/exec_detail.html b/templates/jlog/exec_detail.html new file mode 100644 index 000000000..45eb21d4d --- /dev/null +++ b/templates/jlog/exec_detail.html @@ -0,0 +1,200 @@ +{% extends 'base.html' %} +{% load mytags %} +{% block content %} + {% include 'nav_cat_bar.html' %} +
    +
    +
    +
    +
    + +
    + + + + + + + + + + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ID{{ user.id }}
    用户名{{ user.username }}
    来源IP{{ user.name }}
    角色{{ user.id | get_role }}
    Email{{ user.email }}
    激活{{ user.is_active|bool2str }}
    添加日期{{ user.date_joined|date:"Y-m-d H:i:s" }}
    最后登录{{ user.last_login|date:"Y-m-d H:i:s" }}
    所在用户组 + + {% for group in user.group.all %} + + + + {% endfor %} +
    {{ group.name }}
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    授权主机/组
    + +
    +
    +

    用户的所有授权主机

    + 这里包含了用户所有的主机组和组下的主机. +
    +
    +{# {% for group in user|get_user_asset_group %}#} +{#
    #} +{#
    #} +{#
    #} +{# #} +{# {{ group.name }}#} +{#
    #} +{# 共: {{ group | group_asset_list_count }}台#} +{#
    #} +{#
    #} +{#

    {{ group.comment }}

    #} +{#

    #} +{# {% for asset in group|group_asset_list %}#} +{# {{ asset.ip }}
    #} +{# {% endfor %}#} +{#

    #} +{#

    #} +{#
    #} +{#
    #} +{#
    #} +{# {% endfor %}#} +{# {% if not user|get_user_asset_group %}#} +{# (无)#} +{# {% endif %}#} +
    +
    +
    + +
    +
    +
    +
    登录记录
    +
    + 最近登录 +
    +
    +
    +
    +
    + {% for log in logs_last %} +
    + + image + +
    +{# {{ log.start_time|time_delta }}#} + {{ log.start_time }} + {{ log.user }} 登录了 {{ log.host }}.
    + {{ log.start_time|date:"Y-m-d H:i:s" }} +
    +
    + {% endfor %} + {% if not logs_last %} + (暂无) + {% endif %} +
    + {% if logs_num > 10 %} + + {% endif %} +
    + + +
    +
    +
    +
    +
    + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/jlog/log_exec.html b/templates/jlog/log_exec.html index d1e096c82..9cd7a6b4f 100644 --- a/templates/jlog/log_exec.html +++ b/templates/jlog/log_exec.html @@ -9,73 +9,81 @@
    -
    -
    -
    批量命令日志
    -
    - - - - - - - - - +
    +
    +
    +
    批量命令日志
    +
    -
    -
    - -
    -
    -
    diff --git a/templates/jperm/perm_role_detail.html b/templates/jperm/perm_role_detail.html index d0cc9a00d..0c5ca95ea 100644 --- a/templates/jperm/perm_role_detail.html +++ b/templates/jperm/perm_role_detail.html @@ -8,7 +8,7 @@
    - 授权规则 + {{ role.name }} - 授权规则
    @@ -54,7 +54,7 @@
    - 授权用户/用户组 + {{ role.name }} - 授权用户/用户组
    @@ -100,7 +100,7 @@
    - 授权主机/主机组 + {{ role.name }} - 授权主机/主机组
    @@ -148,7 +148,7 @@
    - 推送主机 + {{ role.name }} - 推送主机
    @@ -212,7 +212,7 @@
    - 未推送主机 + {{ role.name }} - 未推送主机
    diff --git a/templates/nav.html b/templates/nav.html index 27c2a675e..1949a66c2 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -27,19 +27,24 @@
  • 授权规则
  • -
  • 系统角色
  • Sudo命令
  • -
  • 日志审计
  • +
  • + 上传下载 + +
  • 设置
  • From 69bbdab45052a61dd120cfc7bffa67d96302fbdc Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 4 Dec 2015 13:42:05 +0800 Subject: [PATCH 291/385] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- connect.py | 15 ++-- jlog/models.py | 2 + jlog/urls.py | 1 + jlog/views.py | 18 ++++ jumpserver/views.py | 11 +-- static/css/style.css | 4 +- templates/jlog/exec_detail.html | 152 ++++++++------------------------ templates/jlog/log_exec.html | 2 +- templates/jlog/log_file.html | 4 +- 9 files changed, 80 insertions(+), 129 deletions(-) diff --git a/connect.py b/connect.py index 7111436f4..4ab1f4934 100644 --- a/connect.py +++ b/connect.py @@ -565,10 +565,11 @@ class Nav(object): while True: print "请输入执行的命令, 按q退出" command = raw_input("\033[1;32mCmds>:\033[0m ").strip() - ExecLog(host=asset_name_str, user=self.user.username, cmd=command, remote_ip=remote_ip).save() if command == 'q': break runner.run('shell', command, pattern=pattern) + ExecLog(host=asset_name_str, user=self.user.username, cmd=command, remote_ip=remote_ip, + result=runner.results).save() for k, v in runner.results.items(): if k == 'ok': for host, output in v.items(): @@ -605,7 +606,6 @@ class Nav(object): if not asset_name_str: color_print('没有匹配主机') - print continue tmp_dir = get_tmp_dir() logger.debug('Upload tmp dir: %s' % tmp_dir) @@ -613,15 +613,16 @@ class Nav(object): bash('rz') filename_str = ' '.join(os.listdir(tmp_dir)) if not filename_str: - print color_print("上传文件为空") + color_print("上传文件为空") continue logger.debug('上传文件: %s' % filename_str) - FileLog(user=self.user.name, host=asset_name_str, filename=filename_str, - remote_ip=remote_ip, type='upload').save() + runner = MyRunner(res) runner.run('copy', module_args='src=%s dest=%s directory_mode' % (tmp_dir, tmp_dir), pattern=pattern) ret = runner.results + FileLog(user=self.user.name, host=asset_name_str, filename=filename_str, + remote_ip=remote_ip, type='upload', result=ret).save() logger.debug('Upload file: %s' % ret) if ret.get('failed'): error = '上传目录: %s \n上传失败: [ %s ] \n上传成功 [ %s ]' % (tmp_dir, @@ -667,10 +668,10 @@ class Nav(object): file_path = raw_input("\033[1;32mPath>:\033[0m ").strip() if file_path == 'q': break - FileLog(user=self.user.name, host=asset_name_str, filename=file_path, type='download', - remote_ip=remote_ip).save() runner.run('fetch', module_args='src=%s dest=%s' % (file_path, tmp_dir), pattern=pattern) ret = runner.results + FileLog(user=self.user.name, host=asset_name_str, filename=file_path, type='download', + remote_ip=remote_ip, result=ret).save() logger.debug('Download file result: %s' % ret) os.chdir('/tmp') tmp_dir_name = os.path.basename(tmp_dir) diff --git a/jlog/models.py b/jlog/models.py index ef1f81a33..c8ffd77a2 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -33,6 +33,7 @@ class ExecLog(models.Model): host = models.TextField() cmd = models.TextField() remote_ip = models.CharField(max_length=100) + result = models.TextField(default='') datetime = models.DateTimeField(auto_now=True) @@ -42,6 +43,7 @@ class FileLog(models.Model): filename = models.TextField() type = models.CharField(max_length=20) remote_ip = models.CharField(max_length=100) + result = models.TextField(default='') datetime = models.DateTimeField(auto_now=True) diff --git a/jlog/urls.py b/jlog/urls.py index 956fbf839..a490f46eb 100644 --- a/jlog/urls.py +++ b/jlog/urls.py @@ -5,6 +5,7 @@ from jlog.views import * urlpatterns = patterns('', (r'^$', log_list), (r'^log_list/(\w+)/$', log_list), + (r'^log_detail/(\w+)/$', log_detail), (r'^history/$', log_history), (r'^log_kill/', log_kill), (r'^record/$', log_record), diff --git a/jlog/views.py b/jlog/views.py index 9b492fa9a..a6564cbfb 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -129,3 +129,21 @@ def web_terminal(request): web_terminal_uri = 'ws://%s/terminal?id=%s&role=%s' % (WEB_SOCKET_HOST, asset_id, role_name) return render_to_response('jlog/web_terminal.html', locals()) + +@require_role('admin') +def log_detail(request, offset): + log_id = request.GET.get('id') + if offset == 'exec': + log = get_object(ExecLog, id=log_id) + assets_hostname = log.host.split(' ') + result = eval(str(log.result)) + return my_render('jlog/exec_detail.html', locals(), request) + elif offset == 'file': + log = get_object(FileLog, id=log_id) + assets_hostname = log.host.split(' ') + file_list = log.filename.split(' ') + try: + result = eval(str(log.result)) + except (SyntaxError, NameError): + result = {} + return my_render('jlog/file_detail.html', locals(), request) diff --git a/jumpserver/views.py b/jumpserver/views.py index cd646214f..dfa312819 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -302,12 +302,10 @@ def upload(request): illegal_asset = set(asset_select).issubset(set(assets)) return HttpResponse('没有权限的服务器 %s' % ','.join([asset.hostname for asset in illegal_asset])) - FileLog(user=request.user.username, host=' '.join([asset.hostname for asset in asset_select]), - filename=' '.join([f.name for f in upload_files]), type='upload', remote_ip=remote_ip).save() + for upload_file in upload_files: file_path = '%s/%s' % (upload_dir, upload_file.name) - # file_dict[upload_file.name] = file_path with open(file_path, 'w') as f: for chunk in upload_file.chunks(): f.write(chunk) @@ -318,6 +316,9 @@ def upload(request): % (upload_dir, upload_dir), pattern='*') ret = runner.results logger.debug(ret) + FileLog(user=request.user.username, host=' '.join([asset.hostname for asset in asset_select]), + filename=' '.join([f.name for f in upload_files]), type='upload', remote_ip=remote_ip, + result=ret).save() if ret.get('failed'): error = '上传目录: %s
    上传失败: [ %s ]
    上传成功 [ %s ]' % (upload_dir, ', '.join(ret.get('failed').keys()), @@ -346,11 +347,11 @@ def download(request): illegal_asset = set(asset_select).issubset(set(assets)) return HttpResponse('没有权限的服务器 %s' % ','.join([asset.hostname for asset in illegal_asset])) - FileLog(user=request.user.username, host=' '.join([asset.hostname for asset in asset_select]), - filename=file_path, type='download', remote_ip=remote_ip).save() res = gen_resource({'user': user, 'asset': asset_select}) runner = MyRunner(res) runner.run('fetch', module_args='src=%s dest=%s' % (file_path, upload_dir), pattern='*') + FileLog(user=request.user.username, host=' '.join([asset.hostname for asset in asset_select]), + filename=file_path, type='download', remote_ip=remote_ip, result=runner.results).save() logger.debug(runner.results) os.chdir('/tmp') tmp_dir_name = os.path.basename(upload_dir) diff --git a/static/css/style.css b/static/css/style.css index b8a15d909..2dc7a2591 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -2822,7 +2822,9 @@ body.body-small .footer.fixed { .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { - border-top: 1px solid #e7eaec; + /*border-top: 1px solid #e7eaec;*/ + border-bottom: 1px solid #e7eaec; + border-top: none; line-height: 1.42857; padding: 8px; vertical-align: top; diff --git a/templates/jlog/exec_detail.html b/templates/jlog/exec_detail.html index 45eb21d4d..1a0baf7b2 100644 --- a/templates/jlog/exec_detail.html +++ b/templates/jlog/exec_detail.html @@ -7,7 +7,7 @@
    - + {{ log.id }}
    @@ -28,44 +28,30 @@ - + - + - + - - + + - - - - - - - - - - - - - - - - - + @@ -76,10 +62,10 @@ -
    +
    -
    授权主机/组
    +
    结果
    -
    -

    用户的所有授权主机

    - 这里包含了用户所有的主机组和组下的主机. -
    -{# {% for group in user|get_user_asset_group %}#} -{#
    #} -{#
    #} -{#
    #} -{# #} -{# {{ group.name }}#} -{#
    #} -{# 共: {{ group | group_asset_list_count }}台#} -{#
    #} -{#
    #} -{#

    {{ group.comment }}

    #} -{#

    #} -{# {% for asset in group|group_asset_list %}#} -{# {{ asset.ip }}
    #} -{# {% endfor %}#} -{#

    #} -{#

    #} -{#
    #} -{#
    #} -{#
    #} -{# {% endfor %}#} -{# {% if not user|get_user_asset_group %}#} -{# (无)#} -{# {% endif %}#} -
    -
    -
    - -
    -
    -
    -
    登录记录
    -
    - 最近登录 -
    -
    -
    -
    -
    - {% for log in logs_last %} -
    - - image - -
    -{# {{ log.start_time|time_delta }}#} - {{ log.start_time }} - {{ log.user }} 登录了 {{ log.host }}.
    - {{ log.start_time|date:"Y-m-d H:i:s" }} -
    -
    +
    +
    +
    ID{{ user.id }}{{ log.id }}
    用户名{{ user.username }}{{ log.user }}
    来源IP{{ user.name }}{{ log.remote_ip }}
    角色{{ user.id | get_role }}日期{{ log.datetime|date:"Y-m-d H:i:s" }}
    Email{{ user.email }}
    激活{{ user.is_active|bool2str }}
    添加日期{{ user.date_joined|date:"Y-m-d H:i:s" }}
    最后登录{{ user.last_login|date:"Y-m-d H:i:s" }}
    所在用户组主机 - {% for group in user.group.all %} - - - + {% for asset_name in assets_hostname %} + {% if asset_name %} + + + + {% endif %} {% endfor %}
    {{ group.name }}
    {{ asset_name }}
    + + + + + {% for result, info in result.items %} + {% for host, msg in info.items %} + {% ifequal result 'failed' %} + + + + + {% else %} + + + + + {% endifequal %} + {% endfor %} {% endfor %} - {% if not logs_last %} - (暂无) - {% endif %} - - {% if logs_num > 10 %} - - {% endif %} - - -
    命令{{ log.cmd }}
    {{ host }}{{ msg }}
    {{ host }}{{ msg }}
    + +
    + + +{% endblock %} \ No newline at end of file From 2c3e68194258efae5584596d620ce14e4eb1bd88 Mon Sep 17 00:00:00 2001 From: wangyong <864072399@qq.com> Date: Sat, 5 Dec 2015 16:15:19 +0800 Subject: [PATCH 296/385] fix bugs --- jasset/asset_api.py | 43 +++++++----- jasset/views.py | 5 +- jumpserver/templatetags/mytags.py | 6 +- static/files/excels/asset.xlsx | Bin 0 -> 24181 bytes .../excels/cmdb_excel_2015_12_01_22_17.xlsx | Bin 5898 -> 0 bytes templates/jasset/asset_detail.html | 44 ++++++------ templates/jasset/asset_edit.html | 3 + templates/jasset/asset_list.html | 63 +++++++++--------- 8 files changed, 92 insertions(+), 72 deletions(-) create mode 100644 static/files/excels/asset.xlsx delete mode 100644 static/files/excels/cmdb_excel_2015_12_01_22_17.xlsx diff --git a/jasset/asset_api.py b/jasset/asset_api.py index 1e8083062..36569a436 100644 --- a/jasset/asset_api.py +++ b/jasset/asset_api.py @@ -7,6 +7,7 @@ from jumpserver.api import * from jasset.models import ASSET_STATUS, ASSET_TYPE, ASSET_ENV, IDC, AssetRecord from jperm.ansible_api import MyRunner from jperm.perm_api import gen_resource +from jumpserver.templatetags.mytags import get_disk_info def group_add_asset(group, asset_id=None, asset_ip=None): @@ -156,7 +157,7 @@ def db_asset_alert(asset, username, alert_dic): for group_id in value[1]: group_name = AssetGroup.objects.get(id=int(group_id)).name new.append(group_name) - if old == new: + if sorted(old) == sorted(new): continue else: alert_info = [field_name, ','.join(old), ','.join(new)] @@ -198,14 +199,17 @@ def write_excel(asset_all): workbook = xlsxwriter.Workbook('static/files/excels/%s' % file_name) worksheet = workbook.add_worksheet(u'CMDB数据') worksheet.set_first_sheet() - worksheet.set_column('A:Z', 14) - title = [u'主机名', u'IP', u'IDC', u'MAC', u'远控IP', u'CPU', u'内存', u'硬盘', u'操作系统', u'机柜位置', + worksheet.set_column('A:E', 15) + worksheet.set_column('F:F', 40) + worksheet.set_column('G:Z', 15) + title = [u'主机名', u'IP', u'IDC', u'MAC', u'远控IP', u'CPU', u'内存(G)', u'硬盘(G)', u'操作系统', u'机柜位置', u'所属主机组', u'机器状态', u'备注'] for asset in asset_all: group_list = [] for p in asset.group.all(): group_list.append(p.name) + disk = get_disk_info(asset.disk) group_all = '/'.join(group_list) status = asset.get_status_display() idc_name = asset.idc.name if asset.idc else u'' @@ -214,12 +218,13 @@ def write_excel(asset_all): 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, system_os, asset.cabinet, group_all, status, - asset.comment] + disk, system_os, asset.cabinet, group_all, status, asset.comment] data.append(alter_dic) format = workbook.add_format() format.set_border(1) format.set_align('center') + format.set_align('vcenter') + format.set_text_wrap() format_title = workbook.add_format() format_title.set_border(1) @@ -308,18 +313,21 @@ def excel_to_db(excel_file): def get_ansible_asset_info(asset_ip, setup_info): - disk_all = setup_info.get("ansible_devices") + print asset_ip disk_need = {} - for disk_name, disk_info in disk_all.iteritems(): - if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'): - disk_size = disk_info.get("size") - if 'M' in disk_size: - disk_format = round(float(disk_size[:-2]) / 1000, 0) - elif 'T' in disk_size: - disk_format = round(float(disk_size[:-2]) * 1000, 0) - else: - disk_format = float(disk_size) - disk_need[disk_name] = disk_format + disk_all = setup_info.get("ansible_devices") + if disk_all: + for disk_name, disk_info in disk_all.iteritems(): + print disk_name, disk_info + if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'): + disk_size = disk_info.get("size", '') + if 'M' in disk_size: + disk_format = round(float(disk_size[:-2]) / 1000, 0) + elif 'T' in disk_size: + disk_format = round(float(disk_size[:-2]) * 1000, 0) + else: + disk_format = float(disk_size[:-2]) + disk_need[disk_name] = disk_format all_ip = setup_info.get("ansible_all_ipv4_addresses") other_ip_list = all_ip.remove(asset_ip) if asset_ip in all_ip else [] other_ip = ','.join(other_ip_list) if other_ip_list else '' @@ -342,7 +350,7 @@ def get_ansible_asset_info(asset_ip, setup_info): # asset_type = setup_info.get("ansible_system") sn = setup_info.get("ansible_product_serial") asset_info = [other_ip, mac, cpu, memory_format, disk, sn, system_type, system_version, brand, system_arch] - + print asset_info return asset_info @@ -357,6 +365,7 @@ def asset_ansible_update(obj_list, name=''): continue else: asset_info = get_ansible_asset_info(asset.ip, setup_info) + print asset other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand, system_arch = asset_info asset_dic = {"other_ip": other_ip, "mac": mac, diff --git a/jasset/views.py b/jasset/views.py index ac8f166dc..37023353e 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -419,10 +419,9 @@ def asset_detail(request): if perm == 'user': for user, role_dic in value.items(): user_perm.append([user, role_dic.get('role', '')]) - elif perm == 'user_group': + elif perm == 'user_group' or perm == 'rule': user_group_perm = value - elif perm == 'rule': - user_rule_perm = value + print perm_info asset_record = AssetRecord.objects.filter(asset=asset).order_by('-alert_time') diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 880fd2b12..af41f6b0e 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -276,7 +276,9 @@ def get_push_info(push_id, arg): @register.filter(name='get_cpu_core') def get_cpu_core(cpu_info): - return cpu_info.split('* ')[1] if cpu_info else '' + cpu_core = cpu_info.split('* ')[1] if cpu_info and '*' in cpu_info else cpu_info + return cpu_core + @register.filter(name='get_disk_info') def get_disk_info(disk_info): @@ -287,6 +289,8 @@ def get_disk_info(disk_info): for disk, size in disk_dic.items(): disk_size += size disk_size = int(disk_size) + else: + disk_size = '' except Exception: disk_size = '' return disk_size diff --git a/static/files/excels/asset.xlsx b/static/files/excels/asset.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1b92db891d85cb5736e2ed99e166078360ea90d1 GIT binary patch literal 24181 zcmeIa2|QG9-#>mV*%@0V#3XBpsceM|$(|@>Un*paNyx-7_B9kmktKTx*&<|LDqqEi7hu6_=R81L%`@|hXs@&HEY`u{!t z2hYIs`+es+kMinm8B%pWDHwmO)@D;#jp*e+d|s=qE3Io3dHdrq?UX=eeN_#o42R?Qli{P_90ZTtv$Ekm2o11$p-!_2-+;I2rly{T-@UZt>q^T^-Jn=#D zBq`=)pw#{M)jKn<-am8c2rfU&$9_NX{A?Cno1{&gdgtW-L(N9#kPOwPzbReU+0p zNpaYA-;QBXs`}ybrKSY>J zM6suOG3_-z!#Zqc^=kq$p}^Nij89&-KcwQ%&;MoS|6n@))zF`(wi|aH<&9a3 zSmV8kFK$YRyWR-r+myM0xUBl<<)rGPyDoJ*oBA-%CxY##&RzR9ur)t$OKmFr^_94o z$Dfw-KXGLr^tPA(;k8@aIDbUuh6u||xgWxk_)8O3)A0QVKATi2X;nWb=6`Z9rs>3$ zs@L*zep|R%^5n9_=i{{hPzQyA`EsACT;&)~Meo%^{ z{E6XAfko`|IWzbpzE7XQ*h{4BbME+)pH~$RrGNH1I+l49FCq3u;+uz9FaO(D77me< z&)GYFTqZLgH#ymAz;gSR?*(MnufbAyvCn7vC~q@- z0ma)qecz=|h07#-nbkATJ+jv5y|SKN)DLr`!>N4h_@?}PWpBkpLC0Q6ooZ65Df@h> zOe$z@oV`G4e~RRthHJ&V+xH36YfVG@CFh?imMUkufgNHk@9?d5sy#Wdp2vKEdj?^6 z$5SWHS=wqe$+TPL9U6;~FIvzX@mNw$oQ_3$LY%s9?P=EO$>HN`_W6v$^ zcf`AH8lAeoW_uK zwIn426pP$2{6_+M|Gg7m56$-#+@)4zhz&j4K7A=e@2b?(@6)F-jxHFMCmtYz^<{1XA?9DNngmU;hTb>fhVvxow>|DM|t$dJ_;6x&%F zgNFdAYEItKKv+x&Ow=a*yOkV&!1kFJmOyWB5q zdh7ncr{KJ1mQ=x{e@5X*K(I+sJz;_Z>B_r#-M+G0Rn^Zwq6g0n3n&}$ zyXz=i9x@jeZjt}-kI|QE+^DYvp^py9Gr>Qg&(HnJO;;BS|C=5@Zhk-IRr95G&uMMJ zX81PjDpGyS>uvn9kkLtIj#obhL)$E;te=%TTx#-k&b=Tfagw=2Ia-L{(Bn{eShlW& z^tpwBGnO~sA^nSz61i{rD%?xEyJ@6^o^8HYRZXbBuXe&kTv}$(*fsC+Rf}FpZh>YD zZRI?-+#N|`o{;nhX769x5;n-;?fa~{nxY=Heb&TqNYps2NLuDI$Pit!W3-4w)$7_q zQTtB*(*1Bpp){lK(t>0P&|S<&V65l3ABU-wY~J1qcRf0AyrIrA1$K_qY07l7k8Qc4 z!!c7EuH0S3ILy&&p;3{Mt$Y5maJYQK&4G&8ck0sEem*vB!m75i_QGN5KyD^?;iG!@ z4Br}|zJ?|?xe1s*J;-RBX3lreGfn?lqB}#_PH*RG4F}qBI$NbQx1v4ZM- z_c;Fx1#&NB_Z`g>4N|)Np%6@0`RXAc;oySDd%gO_Esfo9r^_nlH211g?%bIHOqG*pAK$M(zPZiqm8)O# zCKwd(HoHUTy*om=wIWY`7WcB+ftKZJWLp-;@Tl3VJyQB`=UedoK z5-XRzsN%T0-jB!QK2taPV3j$cS!#{(^Xj1mcH=C0^+gCr>=t#vDYq4~xzU%KYrC>_ z-2Yi;uT=ZTSJBAFO-I7t+laK4OlFPk>y6u|V$Stap*VIFAz#2F+`CHf^p2w3$w(e<4r~W>1O^2&sQnJU##NrS5$Z330_Q zdF5T#j^C-$`Oc&FR9orsRl*ze3YW9VnmF%OaO2?_Vbb-(k6F7GC9ej&efU@;{-UM2 z6nmGeha}q*RjeNtzo_K7yaD898SdK`XZt4Qn94^?m=L41YMS$X^BIDHVV7#EY~PV# zUtPrm!%Al#DLuaY-aEj@ZO}Da57eU3}SQMHP0#*5RF!T;MBXE3ZwL`P2|&xl?>52`LS>b$$=;%Sg&yoMx{p zu}2fW9McpMWoAfvg(kc5GQr=&-yK;6l)r#3=Mor?0%C%H?TfBYW598m@-i zLBo5?hRlcE>+lB8i{h@kGnoby8wE?ZXLb3kE6J*( z7I~+{Vn6$-72EiAe;Nlzu2=L%*)!VaWt%jG10Q86pE^z!C9Wt&2!6K4PNUQ7MwQaC)Z0{mjvd8#;Nn4}KujZ|uLa(KY>^YK~U&`pjHqYxS(lDo}j8 zoLM7;jd(2VJAThz`)K~J`;RjxwosW*W^nI2m~y5{KT6i^-o1*QhA&&qF9@*@Y^M>H z&Baz_wY!cr!{2wWg)B^XMTU~t&mK8=ZYeYre>}oMuJMSE!^I!t{U+7w?oC2fh3`iD zjW6+8sUbIFR=>C&L0j6#hAn9Fh!mWw8G#+nzJ|U?VcES^B*3!!?f{!zFgna ziIY~BD<`;KU;a?Rym9n7M#pp*9e7xwa%kapxnuCcMvh?LzJ=S0h-hTn>x2m!>K~ft zvvb>BZy*E74$@!Te>9N%Z(n!)qh-|ZDSC{z1-{L5I?&C#_uSQ*rYFhHY=!2A9IxIr zP1Ti#3K^XXvuV?SxuN4fUc?}LA>#!UnHs9*ddL!H`WI&Ivy1ARg|{JQog%AsoGoOCU303$mVgdx%%Zp zmT)7>i@Y@j9EY#STG;Y(7Qbh*a5ug{N@a${?9v<$u%&&FJgfIj_CUmf+H!t|+YE| zZ6tqPx@t!guzK!c%e|#?`vwKx7x4+Ye$k5TtOLBPz7ORpl&kZ&1XXW{8!>P2`5pew zsbBd0+L@z{w_DBZ`MkBAQ|dHs>eO|ZR$p$LILT!780i}fernBLSMJZsbwcB?O*>S| z*PRmE)SX{8)ulWYS>7CdZ}&R(ob8P+iS?Na3h>K$*Fqfk9(W!lzjS4(K{nsd^jZ6j zk49`uOr#r6{W&D77Hhqp1$Il6Jla#I(r5M6>k>n&iL1iL+!JSo-#(D~xZ#{ObR_KD z>60iwXTN(hor+3Q6IUfxj6|;N)1G+S&mX63k}}U7i`V=9LCjiq9N(FL+>3(CI2E-m zj(payUnqz9EYmR)fPOu9BqJioYJX}{yrprCJX=6bYMa*Bht~$_(qbtjyKCGYd6Prm za#G7>-XvK?p(`rUt=KRfZ_Ja=nZA9WBo8xQv-Q~-4xH#{j4k|X9n4WHTO@4b7d-2u zk7)`uvUn+!rnEg?nIP$I4g3TG>+S(Ep7I=$$xlI&aY+zINJE{9c;9#!Ux_*=Y z$E-VU-)AxY%2@;OG};T?ddv5U-Oe)0)|ML$;Td%ZWF7PXyGrhITW` ziB45Z@Li|xB|7~>$hVDYaSuR{=Ypd3`~Cp=pB|~7yQ{0eAM)3)Uv0MgmrVSoj`Fqy z7Sr2F!Bi=pD`96DCHkKzWor$bPOoA)WpAVnZaKVF<$ei!zsKmyG&-==q5KOB{rI*) zzVd^sgkGIb!DlW%vZ%5HTSI+(id#y!`A=TGa5ow!GljDbr75F~>L#QmdYD+Az(YOG zs&MKinRMLaanWO@S{QcU3on?8ZoI(!`F&i?(fIr$pW--LuRVNvY=5@k}~ znq0@-a4^6~ex}-2GbUNG4s}t@`WuJb%lqg3ZWXrlQdRl{l?`YE7SI>>;)y}^!8;1n04%FCb@}m!Qw?lLt=Tm@oTybb`rY(F zQ_IxdC$A2*AF9q{3q{8>3wUtRu?t!zYCM?y2s7p}OBl&}CBU zoxa6c5e{p+1Svz55KGjue^{Mqw?j4GVV9&s&&zkfg0D|R9|v38ZA}_Hh{7yixVE_7 zD(I`+<-Ak=m3bn-%VqFp#izHCJ3J~Ad#gtzb96K#lbFR^o}>sZAIWQ1_TTXp?(Zy+ zM)R75oWUkpb06@jlvvg(jKqAEO!Z1OMci|gbli@6yN5601UxzqZy@hdHNi0xAn3_w z`MOYG;~6?9pmtHRIJs^S6Ovg(aZSZj8aO z{`tm2Ia23RN1CTx%y~P*D=YhATaM0i6hFBue<(9>lBa`X^^H~Gt&8p{_d2+38jd~7 z={OGrOUi2mtzE9TUKFk~m1B$vV7GrF7Y1-1G7WxV_5E8ik`Be-{^YZ8Kyd$gNC3XR zcTQPr0Km`xaHzxu?QDJ%eZvp(SpvMT`dsn2uIPEg)s6mvJ_qm`>l^6<3{X3U&_94q z0K)Vz9@hZC)D$=j)nx;i8AJdm(Ee+M^y@X}pzogt z)Y`9q|KXb{kKvD3L$}If{L|G8KpxYd&VhSBtI=116Xxz{f3%-F`o;kz#iPK9(?+Ju zKU;^6U(cm~J>?x0yEB;rR5LGzu|He^PlwXm06sS09&{fDK7f&rfr*cS-UT3_UhHA` z_4u_K^ufT$#Jq=vm5rT)6S|;|7hq&yVq#=w+Oy~9Z5TqJ-vMU6J^T{KbXWw=UtvAy zD|kHo-V-*-(^c(zFAR~Tl&;>4VCN8m?-Lf0mXVb^bXZwMRqez{b=@<1`UZwZ#ugVZ zSz1}!*gCtoUUPHz@bvQ!xD|-G9TXWA9TOWDpOBh%|3P}j!$+C<1%*Y$C8bZFRoB$k zy{vC|)!5P5)!ozk=Iy)Tk+Uhx$;cs!g<@btYWRqgDON*Bm` zuihNu5Ry_RNK<|e?bnh0a|4U`ca7}N1N+OkrU6|DjQ_EN4mI*0YXPcFpMhX`fIbCq zF)=`3V&Vf(0BsGcM8I*I5n5pbsMy+*#aU;Di!wd@6WOKHZH_3+8cE%Ia^#yv>iZQF zC4M8RG;L8FIUZw0i>`#xfw`=6bburX;5wuUPTNAwC^`_~tw9Iwm^07;wv1@pf-RX2 z(AaC&Xvef!sRteR(t#&lopb<{8EFN(0mT0{^*?m35cEOXCiV~=IM^CP2UZvg=)hv* zRG8WVlj^+9(8?FD;MaDhbq#u!7)5mX+B5C|67;Z3F zpANKVXV3ve9@Brr5@tFOmDZW{k`AC3n!>oOb}sbZ8a8AV54~Y{Ks+i!!<>;0bQloU zwqfpgP^lUs+d4$H|9C-cSB}{Ew917%7q09JU0hXD1zF*J$2eFooMSO!41@nSoJr{E zBmWi;F#6E%KeRCIVK3nG3QP^B*t(*_`_h}nPv#er+Z^t$)?40;F~ny z=cqJTTQ1v9lOOX(aADMT{LYfcn@ZhVr?$Ckx6L-|(lY}5N^qXK*V60LJn+{O5HGtu z0=)gC>of1#>mD-VWuokCVs=|w4qzR@d*u1{2}BJ91?0XyVkIY5cwEzJ&Mf++dNWP{ zd&reyv8gyIQ=tdIdcvsH_GW=|uq$!Z#5QxFePV9E zQwJJ6?DLe^x|a$-5;_UzBD}7Fjtdn5o%6sZb4&{y<0FbsK*4bY{2=IMPPSd91AK0& z%bh|>w2AG{)Etq)*e-6X3*_}s_EBkyRuayY!el~TV66uco1ZAG@l-u>O@~PA%F{BB zS@{V%;EoamVLe(xDLSALu5yNY;uRf;jkU(8?{wfl&;fW|E|<;n?EW!2;H+>2^NkKz zQXKkUuh#X(Pj{*_56&*Al`?EY?Lb-uwO~!&f|LrLb_OE-zvt=rz5ty&4TwSo*RiLb zWCf6YlGLZOWzD`>x{Ysqn7sm@Y_5_obWb{9z7m&U{jAiSr|-A7K}4X0TIZg^ow!tj zcX_tjooeJp(}*cu2qt&c5s$8Gy+tgyCv-nKr!c*B&wSM|_EM3%dQ12f(xaYd z<9C;W;BgiC^@IJp6^TV*A+MY%StQvcsytciAa zL_^I((9`N{QG3)VQyk4hVkLI@tew5w0GMUp=;?5|Y&^axIIH z3o{^;<4nn|@%~QyOBP_o@N^d)m~s>uTlbb@+K}B09Xmpk$MS*}6oGC$_d1RZ9O%y3 zL;muVgyV3)9AE2PBc$wqnawP&OkNjpimzP=c(|hMB-mNz(da$%_Eqe~k@E5YQ7XmK6FZS48E6ag>w(+e#J z!>8Y)5i>h01_yL^1vOF7;CtPl&l9xDF*^E(cYC{G}v+#`6Z)bgq$78LtHo3+X5K zZN?3%pg(kWx7JNS4;IqE?9ZQ&4Z^ezW6db=NE#Q0)b1p_`n*|BS(Vo;n| zU5XnqBkmxXrD`q--frnAU-Ozm#c@(EQBXFK9U}GdG-)dT@CsHb-yP4RiMtu#B zzjW7b;pJwgi+a+?R0Hd~9IoAJ&48y^Rf<~ot{q{k9WMYHoCQ;MG%!{jBFJzW7uF0d zgxN6w*T{ZzTRquY6Dw1e1r#P4FJ5>l2-V{t**?o&Gj=_|jC+X=@cZvdc1%sgDQloa z+bcmAraEZGv9$3}h23W==(rpau!ihJu!^ojv)Fsw)p?H9BD<}^ya>zF4WS6E{FtV$ zX9jK`9k{j@Gb?7|1e!Ek5uX$ZV{c$DZU;Olk8*^K`Om21_AiAYe6Z{k@mPvg`}X@M z6(Fo52$85MOo0=vI`I2E(nVY!Z(}BCqL>kDa^>du!wJ<~k5~gr^1YHpN423N6|-J5 zY6cYPEo(eR73BtGE9F) zc`I7#@9*mB%2mkA4dCd*zoQ7>N{njix{)fy6jKOq>PoY*imnef%0iBIbsD94#(jDD z@NJXA8%dw{JEcrdXfu?7UA9}0?QZ!2_l+AsOt+kbv>*?j4scAvXgYaJJAu8Zb%|*@ zK(M<4kx!W+g(yKx5d?AeI<`~f^fZ`N|ubGIX?Ew)^;B9giLA+)T&jV(3(uBzG zpQ^O>qQtzkq($5!w1hEUb8Ar|ojp0y69KvKMcdriP%%)WQ=KQP+@o=(qI`ejsg^qF zPnq98dRl~Ghu~C!M8akY?g~8oI!%G9PBzWSDsZoXPPV#HjLVJQUz197=eRYjhlyYk z1wp7xZqWigCwf}MKpXOMM~fnwiF%b>-v2d{z%3(oeBP)9ZndS&S%Paq^J zfoShoI&ff+VYi8tyUn`_NyzdSkYE%6Q1w_p$UYHkrR{x*phjx%zhgUa2SqqFDkIJr zHj~S?$oD#zDmAWiVJ0BkyWrK1*@w+zsru=XfcBe%{6bd_a4<~#=B}udnih0`c?{Gc zo^tLDJy=DK?S^q;B8iAZtTYATFusmYcI%oL!k!}stR*4W*W%Q#I|-~iAr{kvy2pBx zd_*mVT5F2P4vRyz*U~z6GAz8!nHpm^g}@M&MA9w`H*7O(1gbUS*jHhL@!A{&=jXhI zK00A^^~q=ME#~l(S(C7b<{sX6Z{&`D0Qs<`W~H;DiAmSI4bNRM=N?F;166BLp~o=D zwjBPk(ET2nGDYN_&K3lu>nVG%YCB3)eJ~sqK~*bs^0+0=|7wh;No53Ei*4OkeX&z3 z<$3Nw;zi=GcwbeI9$EF-(2ys?ibBQRMKU@D07x;XM6`(L6k*f|1CdmvITX8xThFZt z15@%ql(HQHwxG)LC0HhBZgSB!r2JLRH4oQs)VKSZj$LNQdOZhwOxZ2x+s*Ctc3%@@ zzZ=A_6w6psZSsxKJl)k7_H=i2`?$Q9;Q#T9uz48}fcrnw>|15G*(4nx;=0jrFt|Gd zFQlc8u|=|AhVi|$J>=e~4gBN-?5R1tz%<6ro*Xp0coS2w=}j?+S24^zLqWymr$X9W zk|I{0Th@Q$UfGmK?qwk6`{vc=2~A&eD^d1N5g18w62z*F9vrLV?6>RACjcIk8eyu@Z?LkJ0tBhdnC~lpt;)*nx zeu|PcrTz|*P z(lgU74r`qJQi#viW3kdxXv*J`tn40!Q0v>_()Ap-#g6SB-W-&Q&;%_>mlQ>f>OV!B=1rmtt4NXYn5r3vh9V*eT2=?}LpMFKbHJdCLk6tuDJti2(GG-_cspgQ!opD0E==UF)_-W|+n^Kn}=*b!6XsNBlZK z94T?p3I7nk&DgPK zU*hs~4}~=vD@ur$0V64P*3}eP#|xM)NCzsY`e~$r)qA&h)IjIWhl#|Du2xXivH;cz zPyujdWUR3Esy9pBdLY;sQn=nL@@Z zisV^xN~f6+)h(kvsJ;^)j;Jf?^atm?(a%EP zhb<1D%eLjEJfu zki^N=RvI9ut`b#6idk4q;b;5GOaqi5P}X~cuuO@oVTdOm}bCS7AhLYp?RT&X++aJ z0LASvFgN2f(Cz&cbnDJ^LGY`n4Sj7x8M^~{JO>d!4vA#!#*4_(a+`Iu#a;t~3?QC} z62eH(0T1L<8Sw}m*n_*?gk-@U0Tn22Z8<8Cv~OXheW-7d-F<;ItMI~et)c6X(LuZQ zu}3ubK3j0&iH$LXqMhNq7}*}ma*jkU5!Q|8!*~v~BUo!sYC2Kam{w39WDOdJ=zyEU zfte{xrB0e1CAZ6LZ}%KR5R@m*hY>(iSK`3FrCEs)8b4^J2NDQKCaP{D8Qs|@rjPH; zRBa6*guu>FI45V8Xncc^{4gm;bTsZ`ovdZ(!R*lVAfLJ_rQK;e6B8Pyq5dmb9^kI$ z#v%B%#ISo`jkO3aI`N{Luasfx+h;}UM`@zqg{>Z2e6;eavu(5rTEKB(+o}UG#bjeD zG~6Tn90^5D=5XDd*~deShrf|J&oAC{S`IR2{Sp|B^H>Ar;Bi=l*E396rxSlOAE`fZ z6A9{&7HbooCUc4F;>@5krRm}@joXM~&@ro7o3(kFvX+F_lJZg?NTAxWc9xqEENdub zovl3A3n=?h!nkW6srxA`HN-idf-_(r^b|P2TnfBXoV6B0MHe#4k%3~>RxZ7wf{H`I zu)3}krwfe~rnR?DLK6s4v-2y1MKd}V+=cqMBv`Ulbm)0 zn+@ndaqtvCAN?I!3XSiS_j7Q$_kwAu*XA{8a%1$c%Bq!N($?Z}mp-xRu}0(0%TxBp z>|Y*xT@U!<$PpiKgMmGG&=!P(uIU${Xe8$qI-r9W#Sy%SPBfceBmWAqyR2Am$RMqZ z$Ng~CMo6Jnk>s-;P`IJuYQydmmob_U)_(})##zZ~R)Wl@U}g!fRMt z=>CbX>xnaNCKH+}q<#Ur8px7TyrTqe+$f0#J{>~?1!8xx;m9aVEAYsl4G62tyYAIDFYaPUB zbkWqvUkX#HI*^Fv@kVwi>F`vQ^pW1WC3cz-t@m_U9miblp?$Zn9{Ex|T4yJw)oD3Y zp#pHIPjsOH(2oR<3gz-Dqf8r*HHVZrfKKf>$6C^QHt7Hl)`S#{h@F)L%LC@J5#7f< zJ&|>9ASVme9V!#t7ko(bXhCOYPsFT*)80vwyg^nd%J)wIWPg+vceR$*r~>LG)}ab3 zQSg!7AhB6nG0~{p>)ykuc0CD1`!(6xsF=UAfc%WSQ9$X zcNmKQ?1B89ytSlOavcRp2ac37ec81jAv?=Oi3V|6LY_hBRatLh2(n#Eow62tYXLHv ze9E#K+^wZ@~Y zf#krhS$1%epwYS)a}Bb+dlI%$30R9cv;d@9Xa^`ViNsuXu&+I+`+Y3YjAuEWZ#i&W zf2n8l!&`Jnk*9ibVwuN;kZncE(%F!_XxvkJjHbO;`sv*OrVK@VbK`%JPDQ%loHddQKY9yvD1|) zDqpt!ob%{vn*9KGvsr*2|69$fGf{6AY8m>L#E*gSLj1=?+mhZSNz<_-%{+8K0IfWu zOS*3vxS7jcfRRii?(NSWh$l5fwk=BcSECZ8ll=GIp0jMVIP3!qjM5I$1~RqMgqog) z4yr4>yyr(=z6iFuVM9+Gxx0ko@ORK$nyT+gLcXOONqL45H5*}_u4uL>kseRnTO(CW zmM@-z<QRjVCV9m;78&ViBxy>oDOEfv z`B?lU4~#V%YBnlZ+CCM=3N8KoGXxn(!V0^3&EbVU7T1y3ZSkGz>&s#TiBugwZ%N%E zhd2DhDQ_J@L-f1oboTSa2b=~6tCcta`q=M?{dX%HJ7TiES{lhq+@6+qG4@YKG}VX- zo=4$EX<9Cgs%bdEGhan5ZT4ny*NPyu(ClrCnP9{>d_??~yjU zkF+Gg7_+Gf8oyb5D+kujdB=e2NVe)hv3WR3?#4sw1NUPIX7N~tIdoJjH}zP6YB^zG z3jNbLl&L8I-R8Dp1H6^GXnbQd!R1W16h$=Fp!nmYN5n|yt+{=;lo3sVIs@HRUX z8`X2LE;kZiliTS`$c2KNT+QA);?yf+=yj*~mIEWr=W_XJCq95#ow>rzl7s~+npCda z4c`t)@I>%zxNC`HPratdhHJ9=7T`MkNZvRnV1af#v~N}o+#sdg*~0UN9$Vz29s@}` z2cckMbw&afPQXnnkyP)IOtnTFhnfv!ib&pYqQX{>g0p_IE2T9d!1QKcXJc%q-Ofew zb6Z?w<%Yt!ZH+ZJJ`B4>bOL3x(ugKz+NQ8Mcaj$!~=_gpki z%=hkFC}w3m3~Rod+Jabg5=_O7DHK*FVNl~M1AT~_IV%#RNmxgifQ!3 zl3ZVBL_=m?o4RaN?Y_zitN^jet`a_pN$Eir>lS!zqr)|iGGzaZ@QOHXW(;X*Qmw&( zxwW_!5%OC2bxld=mu;&RKT2x{T#I3J1GeXbHPsmMvhaHyu>q7@DMiZ&J}S;zrYi6$ zri1{kKxyg`@X;?kTG{-OeA?G28Oc~rhRu4aOe2c=15*#lG=vx(_^t-6zWrU%et#{+ zVQ4k#^zurM?6<7;sZ$N-=PHG>!li=7Ur8$2q`M(6)>awEF+gih z9bV9?^FT%-jvP*bqL@nlkTLVyB8DGypcK1rw?GDbu%m~)ID+%Qu}^}1M0BJImq5o2`;d`e;0lzxV( zyaZ7PMi>Zh5pi!roC)m@g`*g0ykH<9r?s>)A>b6xvRzX%MK&^&73)eit&gkR*uA>t zG_Gkx6h(-Fog}q{x`F**mi=fQiVs8Tv7Jw;b1>-75d{qQ%klr(=3Xh0(lkl zeci2~Hn>Iy9yJ1=n$g77!}B`U1+^6QdPto|N&iA)r({15zD&6ltC6BU-%0NNa$LWU z%Z@r7pI|p#SkK=p3Iw#0Ga*&^{WB3yJ%A0Q1Ha5b3gn+D!fx!G1x;+g&*WKryA~T{ zP$#wsdAKedx*#=6t6#~PBxjHFiwS0l-j3u zqc@U@131fN9Lx;I0MsceHxzjMG>T=P<5nWnmGo?mQ5CL0@jNk*uiOJdN=?ctvcwL1 zhLX}AB1vG0pQ}u2QANW-heS*_qe*Kli*$hVD-jGPq9R3NJIurvP*aZViQQUSl-1l0 zH235ZAl4d%yDZO*BI091<+1wU5QUStSq_V&$~%*|&AM{Aa2{ezT|p^bX56#^NPJ~i zU<2o7lsXV_Wv2*7MC~|Lg_z_+n?FsNXj@BYtg8VtdM!JMeHwJacGQs1n%= zt5)TdY^$;rLscjU9cXM#QbvQ$_{3WSB=jA8^*}@&js+_MMqUYqI(!QU>QRrDl~83T zH2vmIDm3erE^lXae22$U0|}gHxP#0chK<0L4DwbFia`@j^hOXTy1Z~np(sj5VuhwJ zq{DMWb~8g2u!kwIWU58!V&<)^r(k5SWe`541v$N>{;+P?|FuA8x}#_VIW`I_Fc%`w z>BvaVjaskd0?~#w^}u!+O~3&`!#3Ofgl{q(_>l#zzy4hjk8=+VIqZXl6PGcon&#E^ z7-y$w5mLLja&5Sy!{JrI{(t}i=v|)A)%MocSK0h@R$=DFB%4tt`uOi?tA7~qVGQH= z>B|1`>mT{=fH{Nq9>6z8iw@iiz^yowt!TwLh&-P6e^c{k1D~2hpq(hPNw}2@6a?+j znmZGH;&=M;Ze@-l*m9CGIaFsV)Nw!)`K7od5AQjwfpd9V+bdlew|*%OmUXlCLi)!B z*Qr3jAJR+|{4Q50KO}IF)^y;@b{MP*T%*OH*l`5-bKLh3ZPpJ|h*i3Xs8({~IO<1B z9@9#w2DG-4(%}T^EhDISbB0Y6B>o0_2}tU}jT9QX6xcyP4`9KWoI&FY>ZJo}qd-VX zFK+!92${lcubt^Y))JuAOKRQTM}IFUX<6F7hHm+E+jd9~!^l>JwyRFjJCGgbYoy0Y6*DA+2ZPe7jPQuydw~-TnA? zZ^pkLe)y5;cXw6&zo5|XZNGW9LRBNoctO@@hRvYAgROOJ^!Oo_Q-^zKhGVsl)YxX1 zgl(9vo3T^5nO>qjoYeU7r|)ZY_i-S(UAJ0c)+ ztB)&1AjZJ;GEY5?7siMid;rNPnRVQ+dvpGa7W~vuY-ER$2&zMX7XiU#XFm<;sD}E6 z`a$JVxB48h!(R=r#kj9p8Wtz$*+{0US{Yn&l?YR2`2B_S@(okl&R$1#Q)sn0rZKq% ze%!9xXj{5;1-vurcIDrGw)W^RjAJh zpQ~2*<)%iniO9PVmiR}cGww5OKO;E*(!nsi1t**+TvPmov_0eAlC)WJ;*ViZ8vH?E zH*tpF`pG0q?KkwR2mDj2ZOJF4ZDjnT9R``6|7Xt1|NW=+1?xqG@tHz_f=;z?l`KP7 z^845OCvGl_!&BwmbB&v96s3~!HtEDO(MI=%*$N*$Fzyw7b?Gwf-?rHHFSfmYH&`t} z-dUolQ$`ajF|kJ9GCGz&FH8(h=_#(tm}Oo{AD^pH#NUr-__*u$EzBQ9j_iYss>E(7UHot#N&T^SD5Z4cJL#_mxOZ3>EN5ah3Y;Y5G_6jNu(-qWznlyU8I+;GM51VhBO5xhdOr+%R@Z(oeJ5uP5-}Mv)v-8APG~BD45Y#{HkV!sFJ{!jn4PKCAFbOOcZOrXHfq$@<2z z^1_!5d>&R8ZmaQC{m)>VlEE5d^E9{{P>I%ME-0vAlyF104*qWyd$G`_b8&srj@vF#SnCC93k&Euc(*C zUbjZO_!EAd^6jnbf(;NxRbN9e&V*p>iczCQWFSD9iPj$$5CD%U=oC8H1q-m;Z!B=`+05FNtz^X(5@W?nnCy;g+AoT|Vcv8D!jNU4HwAj{cJ^)Q#4NydB5wq-&kPC)H(3ku$SPfMl5^nVoIC2goxk{lh$N#lv zAiIb)B{mXki&jCg8M$jJRf?9b)+%_4X1o-+gwIL;)<--^Ot_VJvs>!!*|^U(#CtmW zz<&#G`uhZ_m-+z3O9v(@qJOUT8w1}&{wgx|M_Y}pcY7S#U*rW@CZ_-+C@v=WGd#xE zw`H*FA4>=Hi~dsh$KUn<5(}X*VJP|}olgJTbd>$NLr3kQcV2POTe5smTD?ENjO&cP z>H5cXvHzP*4|;+1PO2%CE>$pU%kZPbH!a`CG`4)H2S9+{9)7(8x6*fqZKZCxhz`$& zG2LMEX-k?DIbkFk!NG45{3Rsk_RZtd_oKJCrr4w{&P4FuKJbWmy5fR=)8if4H%jTo z=QyP$^z4qhr;8z^pC>#TzEOT@&lJbe97!XAn!|ctUS<27JIBp02%L!ZzgppMRIahK zw?ZzsP`>T_zH`3p={G$O`Gkdz2@LVPwMluN<3GfBqSIWPeOTlyV?^s~A%BlhG4}`I zHG;S7UY%=d&{cKI{?MeRzuwbW5g9bvbK?hlib5aR7LnYWg#OVf5CXAlC4mhc~w)JM#81pG!O< zG8&e>V&mz;S7aEj#J7@Dl%>eA&f>74dT7wuUrHkRvCG)ZFU_&NDZ0;U97r`h{EhoK zj^%zn75eT?M#fzX%8%ydEBYI6C+5c(n|>nQa8hd4VaIdF&$MbJY%Qp ze52>)<)VbA$GW_y-dSII-=SAq&&`}#a+_w^k$m93c}{Jt_Awbn^=AFCF867eW!!ryH^yU^(V|&THWe-m<3e{E zc6L|SwsMWDwcf|v^voXrT9*T3QA|z>X?xS&ag5M&kCl3{{A|8k*u2GD_?0d(-}ijs zWOn=wXRXEao5xkl*@K06#th&sa{*29@R+&KtX|!^3e$*G#N3Njs4L$Nrmg9w*Rk8l zt(}54{=+}sadH^I|MJc1Bx>2E_)}*IB%A+~^?-p<0TRytI5+oSp5q_R{z0a0bCbVq z;BQk0|8)cT5LNyvk??OT{%!8mzpgk1<=*-IOscVhyK#Qb0|Y2)PUcg^AG*D>Tff* z{H1Cxl*8hWnMMCDkINrg`Tf)ve`%!XU;iJjUV;|@ literal 0 HcmV?d00001 diff --git a/static/files/excels/cmdb_excel_2015_12_01_22_17.xlsx b/static/files/excels/cmdb_excel_2015_12_01_22_17.xlsx deleted file mode 100644 index 37718bab375fd8f0f1228321a03d78923e381f02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5898 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 D8uumu diff --git a/templates/jasset/asset_detail.html b/templates/jasset/asset_detail.html index dd5305d8b..53f9f357a 100644 --- a/templates/jasset/asset_detail.html +++ b/templates/jasset/asset_detail.html @@ -193,27 +193,29 @@
    {% if perm_info %} - -

    授权用户信息

    - - - {% for perm in user_perm %} - - - - - {% endfor %} -
    授权用户系统角色
    {{ perm.0 }} - - {% if perm.1 %} - {% for role in perm.1 %} - - - - {% endfor %} - {% endif %} -
    {{ role }}
    -
    + {% if user_perm %} + +

    授权用户信息

    + + + {% for perm in user_perm %} + + + + + {% endfor %} +
    授权用户系统角色
    {{ perm.0 }} + + {% if perm.1 %} + {% for role in perm.1 %} + + + + {% endfor %} + {% endif %} +
    {{ role }}
    +
    + {% endif %} {% if user_group_perm %}

    授权用户组信息

    diff --git a/templates/jasset/asset_edit.html b/templates/jasset/asset_edit.html index a2db11d00..870579738 100644 --- a/templates/jasset/asset_edit.html +++ b/templates/jasset/asset_edit.html @@ -46,6 +46,9 @@
    {{ af.remote_ip|bootstrap_horizontal }} +
    + {{ af.mac|bootstrap_horizontal }} + {#
    #} {# {{ af.port|bootstrap_horizontal }}#} diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index dac011e49..175271e73 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -127,7 +127,7 @@ {# #} - +
    {{ asset.group.all|group_str2 }}{{ asset.cpu }}|{{ asset.memory }}|{{ asset.disk }}{{ asset.system_type|default_if_none:"" }}{{ asset.system_version|default_if_none:"" }} {{ asset.cpu }} {{ asset.cpu|get_cpu_core|default_if_none:"" }} {{ asset.memory }}{% if asset.memory %}G{% endif %} {{ asset.disk|get_disk_info }}{% if asset.memory %}G{% endif %} @@ -144,8 +144,8 @@ 修改 - - +{# #} + {% include 'paginator.html' %} @@ -347,37 +347,40 @@ $('#asset_update').click(function () { var asset_id_all = getIDall(); if (asset_id_all == ''){ - alert("请至少选择一行!"); - return false; + if (confirm("更新全部资产信息?")) { + layer.msg('玩命更新中...', {time: 200000}); + $.ajax({ + type: "post", + url: "/jasset/asset_update_batch/?arg=all", + success: function () { + parent.location.reload(); + } + }); + } + } + else { + layer.msg('玩命更新中...', {time: 200000}); + $.ajax({ + type: "post", + data: {asset_id_all: asset_id_all}, + url: "/jasset/asset_update_batch/", + success: function () { + parent.location.reload(); + } + }); } - layer.msg('玩命更新中...', {time: 200000}); - $.ajax({ - type: "post", - data: {asset_id_all: asset_id_all}, - url: "/jasset/asset_update_batch/", - success: function () { - parent.location.reload(); - } - }); }); -{# function update_tips(){#} -{# layer.tips('我是另外一个tips,只不过我长得跟之前那位稍有些不一样。', '吸附元素选择器', {#} -{# tips: [1, '#3595CC'],#} -{# time: 4000#} +{# $('#asset_update_all').click(function () {#} +{# layer.msg('玩命更新中...', {time: 200000});#} +{# $.ajax({#} +{# type: "post",#} +{# url: "/jasset/asset_update_batch/?arg=all",#} +{# success: function () {#} +{# parent.location.reload();#} +{# }#} {# });#} -{# }#} - - $('#asset_update_all').click(function () { - layer.msg('玩命更新中...', {time: 200000}); - $.ajax({ - type: "post", - url: "/jasset/asset_update_batch/?arg=all", - success: function () { - parent.location.reload(); - } - }); - }); +{# });#} function change_info(){ var args = $("#asset_form").serialize(); From 2f0e91a53847f341256960ad562c2dc3dbcee1bf Mon Sep 17 00:00:00 2001 From: wangyong <864072399@qq.com> Date: Sat, 5 Dec 2015 16:57:47 +0800 Subject: [PATCH 297/385] fix bug --- jasset/asset_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jasset/asset_api.py b/jasset/asset_api.py index 36569a436..bf2b93923 100644 --- a/jasset/asset_api.py +++ b/jasset/asset_api.py @@ -202,8 +202,8 @@ def write_excel(asset_all): worksheet.set_column('A:E', 15) worksheet.set_column('F:F', 40) worksheet.set_column('G:Z', 15) - title = [u'主机名', u'IP', u'IDC', u'MAC', u'远控IP', u'CPU', u'内存(G)', u'硬盘(G)', u'操作系统', u'机柜位置', - u'所属主机组', u'机器状态', u'备注'] + title = [u'主机名', u'IP', u'IDC', u'所属主机组', u'操作系统', u'CPU', u'内存(G)', u'硬盘(G)', + u'机柜位置', u'MAC', u'远控IP', u'机器状态', u'备注'] for asset in asset_all: group_list = [] for p in asset.group.all(): @@ -217,8 +217,8 @@ def write_excel(asset_all): 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, - disk, system_os, asset.cabinet, group_all, status, asset.comment] + alter_dic = [asset.hostname, asset.ip, idc_name, group_all, system_os, asset.cpu, asset.memory, + disk, asset.cabinet, asset.mac, asset.remote_ip, status, asset.comment] data.append(alter_dic) format = workbook.add_format() format.set_border(1) From 0fd7b980de3de11d55e586993123acec1986e7c0 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 5 Dec 2015 17:55:45 +0800 Subject: [PATCH 298/385] fix some bug --- connect.py | 4 ++-- jumpserver/api.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/connect.py b/connect.py index 4ab1f4934..54a4b03b4 100644 --- a/connect.py +++ b/connect.py @@ -458,8 +458,8 @@ class Nav(object): 5) 输入 \033[32mG/g\033[0m\033[0m + \033[32m组ID\033[0m 显示该组下主机. 6) 输入 \033[32mE/e\033[0m 批量执行命令. 7) 输入 \033[32mU/u\033[0m 批量上传文件. - 7) 输入 \033[32mD/d\033[0m 批量下载文件. - 7) 输入 \033[32mQ/q\033[0m 退出. + 8) 输入 \033[32mD/d\033[0m 批量下载文件. + 9) 输入 \033[32mQ/q\033[0m 退出. """ print textwrap.dedent(msg) diff --git a/jumpserver/api.py b/jumpserver/api.py index 3d7cd7f0e..f7ab1b7fa 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -96,7 +96,7 @@ def get_role_key(user, role): with open(os.path.join(role.key_path, 'id_rsa')) as fk: with open(user_role_key_path, 'w') as fu: fu.write(fk.read()) - logger.debug(u"创建新的用户角色key %s" % user_role_key_path) + logger.debug(u"创建新的用户角色key %s, Owner: %s" % (user_role_key_path, user.username)) chown(user_role_key_path, user.username) os.chmod(user_role_key_path, 0600) return user_role_key_path From 35c818f4a08a83b4610267db91e4c7eb67929051 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 6 Dec 2015 00:28:43 +0800 Subject: [PATCH 299/385] =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jasset/views.py | 9 ++- jperm/perm_api.py | 9 ++- jperm/views.py | 11 +++- jumpserver/templatetags/mytags.py | 12 +++- juser/views.py | 43 +++++--------- templates/jasset/asset_list.html | 3 +- templates/juser/user_detail.html | 97 ++++++++++++++++++++----------- templates/juser/user_list.html | 7 ++- 8 files changed, 114 insertions(+), 77 deletions(-) diff --git a/jasset/views.py b/jasset/views.py index 0f7cb55b6..ea0c1ccf9 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -97,7 +97,9 @@ def group_list(request): header_title, path1, path2 = u'查看资产组', u'资产管理', u'查看资产组' keyword = request.GET.get('keyword', '') asset_group_list = AssetGroup.objects.all() - + group_id = request.GET.get('id') + if group_id: + asset_group_list = asset_group_list.filter(id=group_id) if keyword: asset_group_list = asset_group_list.filter(Q(name__contains=keyword) | Q(comment__contains=keyword)) @@ -261,7 +263,7 @@ def asset_list(request): asset_group_all = AssetGroup.objects.all() asset_types = ASSET_TYPE asset_status = ASSET_STATUS - + asset_id = request.GET.get('id') idc_name = request.GET.get('idc', '') group_name = request.GET.get('group', '') asset_type = request.GET.get('asset_type', '') @@ -294,6 +296,9 @@ def asset_list(request): if status: asset_find = asset_find.filter(status__contains=status) + if asset_id: + asset_find = asset_find.filter(id=asset_id) + if keyword: asset_find = asset_find.filter( Q(hostname__contains=keyword) | diff --git a/jperm/perm_api.py b/jperm/perm_api.py index 224d6d2a5..9e4d014cd 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -43,13 +43,16 @@ def get_group_user_perm(ob): asset_groups = rule.asset_group.all() assets = rule.asset.all() perm_roles = rule.role.all() + group_assets = [] + for asset_group in asset_groups: + group_assets.extend(asset_group.asset_set.all()) # 获取一个规则授权的角色和对应主机 for role in perm_roles: - if perm_role.get('role'): - perm_role[role]['asset'] = perm_role[role].get('asset', set()).union(set(assets)) + if perm_role.get(role): + perm_role[role]['asset'] = perm_role[role].get('asset', set()).union(set(assets).union(set(group_assets))) perm_role[role]['asset_group'] = perm_role[role].get('asset_group', set()).union(set(asset_groups)) else: - perm_role[role] = {'asset': set(assets), 'asset_group': set(asset_groups)} + perm_role[role] = {'asset': set(assets).union(set(group_assets)), 'asset_group': set(asset_groups)} # 获取一个规则用户授权的资产 for asset in assets: diff --git a/jperm/views.py b/jperm/views.py index 9d8fdc281..1529907d7 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -23,12 +23,14 @@ def perm_rule_list(request): """ # 渲染数据 header_title, path1, path2 = "授权规则", "规则管理", "查看规则" - # 获取所有规则 rules_list = PermRule.objects.all() - + rule_id = request.GET.get('id') # TODO: 搜索和分页 keyword = request.GET.get('search', '') + if rule_id: + rules_list = rules_list.filter(id=rule_id) + if keyword: rules_list = rules_list.filter(Q(name=keyword)) @@ -226,12 +228,15 @@ def perm_role_list(request): # 获取所有系统角色 roles_list = PermRole.objects.all() - + role_id = request.GET.get('id') # TODO: 搜索和分页 keyword = request.GET.get('search', '') if keyword: roles_list = roles_list.filter(Q(name=keyword)) + if role_id: + roles_list = roles_list.filter(id=role_id) + 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) diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index af41f6b0e..b7d7fde93 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -7,7 +7,7 @@ import time from django import template from jperm.models import PermPush from jumpserver.api import * -from jasset.models import AssetAlias +from jperm.perm_api import get_group_user_perm register = template.Library() @@ -294,3 +294,13 @@ def get_disk_info(disk_info): except Exception: disk_size = '' return disk_size + + +@register.filter(name='user_perm_asset_num') +def user_perm_asset_num(user_id): + user = get_object(User, id=user_id) + if user: + user_perm_info = get_group_user_perm(user) + return len(user_perm_info.get('asset').keys()) + else: + return 0 diff --git a/juser/views.py b/juser/views.py index 4f1183d84..695873135 100644 --- a/juser/views.py +++ b/juser/views.py @@ -231,24 +231,20 @@ def user_list(request): @require_role(role='user') def user_detail(request): header_title, path1, path2 = '用户详情', '用户管理', '用户详情' - # if request.session.get('role_id') == 0: - # user_id = request.user.id - # else: - # user_id = request.GET.get('id', '') - # if request.session.get('role_id') == 1: - # user, dept = get_session_user_dept(request) - # if not validate(request, user=[user_id]): - # return HttpResponseRedirect('/') - user_id = request.GET.get('id', '') - if not user_id: + if request.session.get('role_id') == 0: + user_id = request.user.id + else: + user_id = request.GET.get('id', '') + + user = get_object(User, id=user_id) + if not user: return HttpResponseRedirect('/juser/user_list/') - user = User.objects.get(id=user_id) - # if user: - # pass - # asset_group_permed = user.get_asset_group() - # logs_last = Log.objects.filter(user=user.name).order_by('-start_time')[0:10] - # logs_all = Log.objects.filter(user=user.name).order_by('-start_time') - # logs_num = len(logs_all) + + user_perm_info = get_group_user_perm(user) + role_assets = user_perm_info.get('role') + user_log_ten = Log.objects.filter(user=user.username).order_by('id')[0:10] + user_log_last = Log.objects.filter(user=user.username).order_by('id')[0:50] + user_log_last_num = len(user_log_last) return my_render('juser/user_detail.html', locals(), request) @@ -406,11 +402,6 @@ def user_edit(request): return my_render('juser/user_edit.html', locals(), request) -# @require_role(role='admin') -def user_edit_adm(request): - pass - - def profile(request): a = request.user.id a = request.user.groups @@ -489,11 +480,3 @@ def down_key(request): return response return HttpResponse('No Key File. Contact Admin.') - -@require_role(role='user') -def RunCommand(request): - if request.method == 'GET': - GUP = get_group_user_perm(request.user) - print GUP - assets = GUP.get('asset') - return render_to_response('juser/run_command.html', locals(), context_instance=RequestContext(request)) \ No newline at end of file diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 175271e73..6f7e856cd 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -222,7 +222,7 @@ var url='/jperm/role/get/?id=' + $(this).attr('value'); var href = $(this).attr('href'); var new_url = '/jlog/web_terminal/?id=' + $(this).attr('value') + '&role='; - var hostname = $(this).closest('tr').find('.hostname')[0].innerHTML; + var hostname = $(this).closest('tr').find('.hostname a')[0].innerHTML; $.ajax({ type: 'GET', url: url, @@ -247,6 +247,7 @@ $.each(dataArray, function(index, value){ aUrl += '' + value + ' ' }); + console.log(aUrl); layer.alert(aUrl, { skin: 'layui-layer-molv', title: '多个角色,请选择一个连接', diff --git a/templates/juser/user_detail.html b/templates/juser/user_detail.html index 8f5517af2..12d47e84f 100644 --- a/templates/juser/user_detail.html +++ b/templates/juser/user_detail.html @@ -76,6 +76,36 @@
    + + 授权主机数量 + + {{ user_perm_info.asset.keys | length }} + + + + 授权主机组 + + + {% for group in user_perm_info.asset_group.keys%} + + + + {% endfor %} +
    {{ group.name }}
    + + + + 授权规则 + + + {% for rule in user_perm_info.rule%} + + + + {% endfor %} +
    {{ rule.name }}
    + +
    @@ -94,10 +124,6 @@ @@ -106,33 +132,35 @@

    用户的所有授权主机

    - 这里包含了用户所有的主机组和组下的主机. + 这里包含了用户授权角色和角色下的主机.
    -{# {% for group in user|get_user_asset_group %}#} -{#
    #} -{#
    #} -{#
    #} -{# #} -{# {{ group.name }}#} -{#
    #} -{# 共: {{ group | group_asset_list_count }}台#} -{#
    #} -{#
    #} -{#

    {{ group.comment }}

    #} -{#

    #} -{# {% for asset in group|group_asset_list %}#} -{# {{ asset.ip }}
    #} -{# {% endfor %}#} -{#

    #} -{#

    #} -{#
    #} -{#
    #} -{#
    #} -{# {% endfor %}#} -{# {% if not user|get_user_asset_group %}#} -{# (无)#} -{# {% endif %}#} + {% for role, assets in role_assets.items %} +
    +
    +
    + + + {{ role.name }} +
    + 共: {{ assets.asset | length }}台 +
    +
    +

    + {{ role.comment }}

    +

    + {% for asset in assets.asset %} + {{ asset.hostname }}
    + {% endfor %} +

    +

    +
    +
    +
    + {% endfor %} + {% ifequal '{{ asset.asset | length }}' 0 %} + (无) + {% endifequal %}
    @@ -147,8 +175,9 @@