diff --git a/connect.py b/connect.py index 83db6b855..4ab1f4934 100644 --- a/connect.py +++ b/connect.py @@ -27,9 +27,11 @@ from jumpserver.api import logger, Log, TtyLog, get_role_key, CRYPTOR, bash, get 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 +# from jlog.log_api import escapeString +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') try: import termios @@ -227,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: @@ -237,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 @@ -421,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() @@ -435,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 @@ -468,16 +450,6 @@ class Nav(object): Print prompt 打印提示导航 """ - msg = """\n\033[1;32m### Welcome To Use JumpServer, A Open Source System . ### \033[0m - 1) Type \033[32mID\033[0m To Login. - 2) Type \033[32m/\033[0m + \033[32mIP, Host Name, Host Alias or Comments \033[0mTo Search. - 3) Type \033[32mP/p\033[0m To Print The Servers You Available. - 4) Type \033[32mG/g\033[0m To Print The Server Groups You Available. - 5) Type \033[32mG/g\033[0m\033[0m + \033[32mGroup ID\033[0m To Print The Server Group You Available. - 6) Type \033[32mE/e\033[0m To Execute Command On Several Servers. - 7) Type \033[32mQ/q\033[0m To Quit. - """ - msg = """\n\033[1;32m### 欢迎使用Jumpserver开源跳板机 ### \033[0m 1) 输入 \033[32mID\033[0m 直接登录. 2) 输入 \033[32m/\033[0m + \033[32mIP, 主机名, 主机别名 or 备注 \033[0m搜索. @@ -542,35 +514,6 @@ class Nav(object): print '[%-3s] %-15s' % (asset_group.id, asset_group.name) print - def get_exec_log(self, assets_name_str): - exec_log_dir = os.path.join(LOG_DIR, 'exec') - date_today = datetime.datetime.now() - date_start = date_today.strftime('%Y%m%d') - time_start = date_today.strftime('%H%M%S') - today_connect_log_dir = os.path.join(exec_log_dir, date_start) - log_file_path = os.path.join(today_connect_log_dir, '%s_%s' % (self.user.username, time_start)) - - try: - mkdir(os.path.dirname(today_connect_log_dir), mode=0777) - mkdir(today_connect_log_dir, mode=0777) - except OSError: - logger.debug('创建目录 %s 失败,请修改%s目录权限' % (today_connect_log_dir, exec_log_dir)) - raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, exec_log_dir)) - - try: - log_file_f = open(log_file_path + '.log', 'a') - log_file_f.write('Start at %s\r\n' % datetime.datetime.now()) - log_time_f = open(log_file_path + '.time', 'a') - except IOError: - logger.debug('创建tty日志文件失败, 请修改目录%s权限' % today_connect_log_dir) - raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - - remote_ip = os.popen("who -m | awk '{ print $5 }'").read().strip('()\n') - log = Log(user=self.user.username, host=assets_name_str, remote_ip=remote_ip, login_type='exec', - log_path=log_file_path, start_time=datetime.datetime.now(), pid=os.getpid()) - log.save() - return log_file_f, log_time_f, log - def exec_cmd(self): """ 批量执行命令 @@ -578,104 +521,75 @@ class Nav(object): while True: if not self.user_perm: self.user_perm = get_group_user_perm(self.user) - print '\033[32m[%-2s] %-15s \033[0m' % ('ID', '角色') + roles = self.user_perm.get('role').keys() - role_check = dict(zip(range(len(roles)), roles)) + if len(roles) > 1: # 授权角色数大于1 + print '\033[32m[%-2s] %-15s \033[0m' % ('ID', '角色') + role_check = dict(zip(range(len(roles)), roles)) - for i, r in role_check.items(): - print '[%-2s] %-15s' % (i, r.name) - print - print "请输入运行命令角色的ID, q退出" + for i, r in role_check.items(): + print '[%-2s] %-15s' % (i, r.name) + print + print "请输入运行命令角色的ID, q退出" - try: - role_id = raw_input("\033[1;32mRole>:\033[0m ").strip() - if role_id == 'q': - break + try: + role_id = raw_input("\033[1;32mRole>:\033[0m ").strip() + if role_id == 'q': + break + except (IndexError, ValueError): + color_print('错误输入') else: role = role_check[int(role_id)] - assets = list(self.user_perm.get('role', {}).get(role).get('asset')) - print "该角色有权限的所有主机" - for asset in assets: - print asset.hostname - print - print "请输入主机名、IP或ansile支持的pattern, q退出" - pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip() - if pattern == 'q': - break - else: - res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm) - cmd = Command(res) - logger.debug("批量执行res: %s" % res) - asset_name_str = '' - for inv in cmd.inventory.get_hosts(pattern=pattern): - print inv.name - asset_name_str += inv.name - print - - log_file_f, log_time_f, log = self.get_exec_log(asset_name_str) - pre_timestamp = time.time() - while True: - print "请输入执行的命令, 按q退出" - data = 'ansible> ' - write_log(log_file_f, data) - now_timestamp = time.time() - write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data))) - pre_timestamp = now_timestamp - command = raw_input("\033[1;32mCmds>:\033[0m ").strip() - data = '%s\r\n' % command - write_log(log_file_f, data) - now_timestamp = time.time() - write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data))) - pre_timestamp = now_timestamp - TtyLog(log=log, cmd=command, datetime=datetime.datetime.now()).save() - if command == 'q': - log.is_finished = True - log.end_time = datetime.datetime.now() - log.save() - break - result = cmd.run(module_name='shell', command=command, pattern=pattern) - for k, v in result.items(): - if k == 'ok': - for host, output in v.items(): - header = color_print("%s => %s" % (host, 'Ok'), 'green') - print output - output = re.sub(r'[\r\n]', '\r\n', output) - data = '%s\r\n%s\r\n' % (header, output) - now_timestamp = time.time() - write_log(log_file_f, data) - write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data))) - pre_timestamp = now_timestamp - print - else: - for host, output in v.items(): - header = color_print("%s => %s" % (host, k), 'red') - output = color_print(output, 'red') - output = re.sub(r'[\r\n]', '\r\n', output) - data = '%s\r\n%s\r\n' % (header, output) - now_timestamp = time.time() - write_log(log_file_f, data) - write_log(log_time_f, '%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(data))) - pre_timestamp = now_timestamp - print - print "=" * 20 - print - - except (IndexError, KeyError): - color_print('ID输入错误') - continue - - except EOFError: - print + elif len(roles) == 1: # 授权角色数为1 + role = roles[0] + assets = list(self.user_perm.get('role', {}).get(role).get('asset')) # 获取该用户,角色授权主机 + print "该角色有权限的所有主机" + for asset in assets: + print ' %s' % asset.hostname + print + print "请输入主机名、IP或ansile支持的pattern, q退出" + pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip() + if pattern == 'q': break - finally: - log.is_finished = True - log.end_time = datetime.datetime.now() + else: + res = gen_resource({'user': self.user, 'asset': assets, 'role': role}, perm=self.user_perm) + runner = MyRunner(res) + logger.debug("批量执行res: %s" % res) + asset_name_str = '' + print "匹配主机:" + for inv in runner.inventory.get_hosts(pattern=pattern): + print ' %s' % inv.name + asset_name_str += '%s ' % inv.name + print + + while True: + print "请输入执行的命令, 按q退出" + command = raw_input("\033[1;32mCmds>:\033[0m ").strip() + 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(): + color_print("%s => %s" % (host, 'Ok'), 'green') + print output + print + else: + for host, output in v.items(): + color_print("%s => %s" % (host, k), 'red') + color_print(output, 'red') + print + print "~o~ Task finished ~o~" + print def upload(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': @@ -684,33 +598,39 @@ class Nav(object): 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 = '' + 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('没有匹配主机') + 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: - print color_print("上传文件为空") + filename_str = ' '.join(os.listdir(tmp_dir)) + if not filename_str: + color_print("上传文件为空") continue + logger.debug('上传文件: %s' % filename_str) + 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) + 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'): - print ret error = '上传目录: %s \n上传失败: [ %s ] \n上传成功 [ %s ]' % (tmp_dir, ', '.join(ret.get('failed').keys()), - ', '.join(ret.get('ok'))) + ', '.join(ret.get('ok').keys())) color_print(error) else: - msg = '上传目录: %s \n传送成功 [ %s ]' % (tmp_dir, ', '.join(ret.get('ok'))) + msg = '上传目录: %s \n传送成功 [ %s ]' % (tmp_dir, ', '.join(ret.get('ok').keys())) color_print(msg, 'green') print @@ -731,30 +651,41 @@ class Nav(object): 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) + 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 - tmp_dir = get_tmp_dir() - logger.debug('Download tmp dir: %s' % tmp_dir) while True: + tmp_dir = get_tmp_dir() + logger.debug('Download tmp dir: %s' % tmp_dir) 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() + 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) - bash('tar czf %s.tar.gz %s ' % (tmp_dir, tmp_dir_name)) + 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'): - print ret - error = '文件名称: %s 下载失败: [ %s ] \n下载成功 [ %s ]' % \ - ('%s.tar.gz' % tmp_dir_name, ', '.join(ret.get('failed').keys()), ', '.join(ret.get('ok'))) + 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'))) + 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 2b43d3e52..c8ffd77a2 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -3,7 +3,7 @@ from django.db import models class Log(models.Model): user = models.CharField(max_length=20, null=True) - host = models.CharField(max_length=20, null=True) + host = models.CharField(max_length=200, null=True) remote_ip = models.CharField(max_length=100) login_type = models.CharField(max_length=100) log_path = models.CharField(max_length=100) @@ -24,5 +24,26 @@ class Alert(models.Model): class TtyLog(models.Model): log = models.ForeignKey(Log) - datetime = models.DateTimeField() + datetime = models.DateTimeField(auto_now=True) cmd = models.CharField(max_length=200) + + +class ExecLog(models.Model): + user = models.CharField(max_length=100) + host = models.TextField() + cmd = models.TextField() + remote_ip = models.CharField(max_length=100) + result = models.TextField(default='') + 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) + result = models.TextField(default='') + datetime = models.DateTimeField(auto_now=True) + + diff --git a/jlog/urls.py b/jlog/urls.py index e9a3063bd..a490f46eb 100644 --- a/jlog/urls.py +++ b/jlog/urls.py @@ -3,11 +3,11 @@ 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'^log_detail/(\w+)/$', log_detail), + (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..a6564cbfb 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, FileLog from jumpserver.settings import WEB_SOCKET_HOST @@ -21,9 +21,19 @@ 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') + 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()]) @@ -57,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进程 """ @@ -114,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/jperm/ansible_api.py b/jperm/ansible_api.py index 8ce990146..422c65a03 100644 --- a/jperm/ansible_api.py +++ b/jperm/ansible_api.py @@ -117,9 +117,9 @@ class MyRunner(MyInventory): """ def __init__(self, *args, **kwargs): super(MyRunner, self).__init__(*args, **kwargs) - self.results = {} + self.results_raw = {} - def run(self, module_name, module_args='', timeout=10, forks=10, pattern='', + def run(self, module_name='shell', module_args='', timeout=10, forks=10, pattern='', sudo=False, sudo_user='root', sudo_pass=''): """ run module from andible ad-hoc. @@ -137,23 +137,29 @@ class MyRunner(MyInventory): become_user=sudo_user, become_pass=sudo_pass ) - self.results = hoc.run() - return self.results + self.results_raw = hoc.run() + return self.results_raw - def get_result(self): - result = {'failed': {}, 'ok': []} - dark = self.results.get('dark') - contacted = self.results.get('contacted') + @property + def results(self): + """ + {'failed': {'localhost': ''}, 'ok': {'jumpserver': ''}} + """ + result = {'failed': {}, 'ok': {}} + dark = self.results_raw.get('dark') + contacted = self.results_raw.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') + if info.get('failed'): + result['failed'][host] = info.get('msg') + info.get('stderr', '') + elif info.get('stderr'): + result['failed'][host] = info.get('stderr') + str(info.get('warnings')) else: - result['ok'].append(host) + result['ok'][host] = info.get('stdout') return result @@ -163,9 +169,9 @@ class Command(MyInventory): """ def __init__(self, *args, **kwargs): super(Command, self).__init__(*args, **kwargs) - self.results = {} + self.results_raw = {} - def run(self, command, module_name="command", timeout=10, forks=10, pattern='*'): + def run(self, command, module_name="command", timeout=10, forks=10, pattern=''): """ run command from andible ad-hoc. command : 必须是一个需要执行的命令字符串, 比如 @@ -183,25 +189,34 @@ class Command(MyInventory): pattern=pattern, forks=forks, ) - self.results = hoc.run() - - ret = {} - if self.stdout: - data['ok'] = self.stdout - if self.stderr: - data['err'] = self.stderr - if self.dark: - data['dark'] = self.dark - - return data - + self.results_raw = hoc.run() @property - def raw_results(self): - """ - get the ansible raw results. - """ - return self.results + def result(self): + result = {} + for k, v in self.results_raw.items(): + if k == 'dark': + for host, info in v.items(): + result[host] = {'dark': info.get('msg')} + elif k == 'contacted': + for host, info in v.items(): + result[host] = {} + if info.get('stdout'): + result[host]['stdout'] = info.get('stdout') + elif info.get('stderr'): + result[host]['stderr'] = info.get('stderr') + return result + + @property + def state(self): + result = {} + if self.stdout: + result['ok'] = self.stdout + if self.stderr: + result['err'] = self.stderr + if self.dark: + result['dark'] = self.dark + return result @property def exec_time(self): @@ -209,7 +224,7 @@ class Command(MyInventory): get the command execute time. """ result = {} - all = self.results.get("contacted") + all = self.results_raw.get("contacted") for key, value in all.iteritems(): result[key] = { "start": value.get("start"), @@ -223,7 +238,7 @@ class Command(MyInventory): get the comamnd standard output. """ result = {} - all = self.results.get("contacted") + all = self.results_raw.get("contacted") for key, value in all.iteritems(): result[key] = value.get("stdout") return result @@ -234,7 +249,7 @@ class Command(MyInventory): get the command standard error. """ result = {} - all = self.results.get("contacted") + all = self.results_raw.get("contacted") for key, value in all.iteritems(): if value.get("stderr") or value.get("warnings"): result[key] = { @@ -247,7 +262,7 @@ class Command(MyInventory): """ get the dark results. """ - return self.results.get("dark") + return self.results_raw.get("dark") class Tasks(Command): diff --git a/jperm/models.py b/jperm/models.py index 3a280762e..00f43a1ca 100644 --- a/jperm/models.py +++ b/jperm/models.py @@ -55,6 +55,6 @@ class PermPush(models.Model): is_public_key = models.BooleanField(default=False) is_password = models.BooleanField(default=False) success = models.BooleanField(default=False) - result = models.TextField() + result = models.TextField(default='') date_added = models.DateTimeField(auto_now=True) diff --git a/jperm/perm_api.py b/jperm/perm_api.py index f64f32189..7366ea284 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -217,6 +217,7 @@ def gen_resource(ob, perm=None): for asset in ob: info = get_asset_info(asset) res.append(info) + logger.debug('生成res: %s' % res) return res @@ -295,9 +296,11 @@ def get_role_push_host(role): asset_all = Asset.objects.all() asset_pushed = {} for push in pushs: + print push.result asset_pushed[push.asset] = {'success': push.success, 'key': push.is_public_key, 'password': push.is_password, 'result': push.result} asset_no_push = set(asset_all) - set(asset_pushed.keys()) + print asset_no_push, asset_pushed return asset_pushed, asset_no_push diff --git a/jperm/views.py b/jperm/views.py index da33d7ff7..110b36c76 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -78,11 +78,11 @@ def perm_rule_add(request): if request.method == 'POST': # 获取用户选择的 用户,用户组,资产,资产组,用户角色 - users_select = request.POST.getlist('user', []) - user_groups_select = request.POST.getlist('usergroup', []) - assets_select = request.POST.getlist('asset', []) - asset_groups_select = request.POST.getlist('assetgroup', []) - roles_select = request.POST.getlist('role', []) + users_select = request.POST.getlist('user', []) # 需要授权用户 + user_groups_select = request.POST.getlist('usergroup', []) # 需要授权用户组 + assets_select = request.POST.getlist('asset', []) # 需要授权资产 + asset_groups_select = request.POST.getlist('assetgroup', []) # 需要授权资产组 + roles_select = request.POST.getlist('role', []) # 需要授权角色 rule_name = request.POST.get('rulename') rule_comment = request.POST.get('rule_comment') @@ -94,8 +94,10 @@ def perm_rule_add(request): # 获取需要授权的主机列表 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) + group_assets_obj = [] + for asset_group in asset_groups_obj: + group_assets_obj.extend(list(asset_group.asset_set.all())) + calc_assets = set(group_assets_obj) | set(assets_obj) # 授权资产和资产组包含的资产 # 获取需要授权的用户列表 users_obj = [User.objects.get(id=user_id) for user_id in users_select] @@ -106,8 +108,9 @@ def perm_rule_add(request): # 获取授予的角色列表 roles_obj = [PermRole.objects.get(id=role_id) for role_id in roles_select] need_push_asset = set() + for role in roles_obj: - asset_no_push = get_role_push_host(role=role)[1] + asset_no_push = get_role_push_host(role=role)[0] # 获取某角色已经推送的资产 need_push_asset.update(set(calc_assets) - set(asset_no_push)) if need_push_asset: raise ServerError(u'没有推送角色 %s 的主机 %s' @@ -444,13 +447,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/jumpserver/api.py b/jumpserver/api.py index d73611d59..3d7cd7f0e 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("创建新的用户角色key %s" % user_role_key_path) + logger.debug(u"创建新的用户角色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/jumpserver/views.py b/jumpserver/views.py index 900d39fc8..dfa312819 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,10 @@ def upload(request): illegal_asset = set(asset_select).issubset(set(assets)) return HttpResponse('没有权限的服务器 %s' % ','.join([asset.hostname for asset in illegal_asset])) + + for upload_file in upload_files: file_path = '%s/%s' % (upload_dir, upload_file.name) - filenames[upload_file.name] = file_path with open(file_path, 'w') as f: for chunk in upload_file.chunks(): f.write(chunk) @@ -313,14 +314,17 @@ def upload(request): runner = MyRunner(res) runner.run('copy', module_args='src=%s dest=%s directory_mode' % (upload_dir, upload_dir), pattern='*') - ret = runner.get_result() + 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()), - ', '.join(ret.get('ok'))) + ', '.join(ret.get('ok').keys())) return HttpResponse(error, status=500) - msg = '上传目录: %s
传送成功 [ %s ]' % (upload_dir, ', '.join(ret.get('ok'))) + msg = '上传目录: %s
传送成功 [ %s ]' % (upload_dir, ', '.join(ret.get('ok')).keys()) return HttpResponse(msg) return my_render('upload.html', locals(), request) @@ -329,9 +333,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,14 +346,17 @@ 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])) + 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()) + 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) 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/run_websocket.py b/run_websocket.py index 1d0b44f07..fd8090813 100644 --- a/run_websocket.py +++ b/run_websocket.py @@ -8,7 +8,7 @@ import sys import os.path import threading import datetime -import urllib +import re import tornado.ioloop import tornado.options @@ -24,7 +24,7 @@ from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE import select 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 +from connect import TtyLog, Log, Session, user_have_perm, get_group_user_perm, MyRunner, ExecLog try: import simplejson as json @@ -218,9 +218,10 @@ class ExecHandler(tornado.websocket.WebSocketHandler): self.id = 0 self.user = None self.role = None - self.cmd = None + 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) @@ -238,32 +240,50 @@ class ExecHandler(tornado.websocket.WebSocketHandler): 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.runner = MyRunner(res) + message = '有权限的主机: ' + ', '.join([asset.hostname for asset in self.assets]) + self.__class__.clients.append(self) self.write_message(message) def on_message(self, message): data = json.loads(message) pattern = data.get('pattern', '') command = data.get('command', '') - asset_name_str = '匹配主机: ' + 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) + for inv in self.runner.inventory.get_hosts(pattern=pattern): + asset_name_str += '%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~o~ Task finished ~o~\n') + self.__class__.tasks.append(MyThread(target=self.run_cmd, args=(command, pattern))) + 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(): + continue + try: + t.setDaemon(True) + t.start() + except RuntimeError: + pass + + def run_cmd(self, command, pattern): + self.runner.run('shell', command, pattern=pattern) + for k, v in self.runner.results.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~o~ Task finished ~o~\n') + + def on_close(self): + logger.debug('关闭web_exec请求') class WebTerminalHandler(tornado.websocket.WebSocketHandler): 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/download.html b/templates/download.html index 0c001d8d6..3be809660 100644 --- a/templates/download.html +++ b/templates/download.html @@ -10,7 +10,7 @@
-
+
下载文件
diff --git a/templates/exec_cmd.html b/templates/exec_cmd.html index cfadcfa58..b3a9d8ffe 100644 --- a/templates/exec_cmd.html +++ b/templates/exec_cmd.html @@ -2,7 +2,7 @@ - Chat + Jumpserver Exec Terminal diff --git a/templates/index.html b/templates/index.html index 0468ecbde..ec6afc4e5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -6,7 +6,7 @@
-
+
Users @@ -18,7 +18,7 @@
-
+
Hosts @@ -31,7 +31,7 @@
-
+
Online @@ -45,7 +45,7 @@
-
+
Connected @@ -74,7 +74,7 @@
-
+

活跃用户资产占比 @@ -83,13 +83,13 @@ 以下图形分别描述一个月活跃用户和资产占所有用户主机的百分比

-
+
用户
-
+
主机
@@ -103,7 +103,7 @@
-
+
{#
#} {#
#} {#
权限申请
#} @@ -192,7 +192,7 @@
-
+
最近十次登录
@@ -258,7 +258,7 @@
-
+
一周Top10用户
diff --git a/templates/index_cu.html b/templates/index_cu.html index 5130338d7..45632fe98 100644 --- a/templates/index_cu.html +++ b/templates/index_cu.html @@ -6,7 +6,7 @@
-
+
使用说明
@@ -30,7 +30,7 @@
-
+
登录记录
@@ -85,7 +85,7 @@
-
+
{{ user.username }} diff --git a/templates/jasset/asset_add.html b/templates/jasset/asset_add.html index d2761a3b0..ac7b9e71c 100644 --- a/templates/jasset/asset_add.html +++ b/templates/jasset/asset_add.html @@ -5,7 +5,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写资产基本信息
diff --git a/templates/jasset/asset_add_batch.html b/templates/jasset/asset_add_batch.html index 2a9512a98..56ba03ff9 100644 --- a/templates/jasset/asset_add_batch.html +++ b/templates/jasset/asset_add_batch.html @@ -8,7 +8,7 @@
-
+
填写主机基本信息
diff --git a/templates/jasset/asset_detail.html b/templates/jasset/asset_detail.html index c312ac01d..dd5305d8b 100644 --- a/templates/jasset/asset_detail.html +++ b/templates/jasset/asset_detail.html @@ -6,7 +6,7 @@ {% include 'nav_cat_bar.html' %}
-
+
{{ asset.ip }} @@ -167,7 +167,7 @@
-
+
拥有权限的用户
@@ -291,7 +291,7 @@
-
+
最近一周登录记录
diff --git a/templates/jasset/asset_edit.html b/templates/jasset/asset_edit.html index 339fde387..a2db11d00 100644 --- a/templates/jasset/asset_edit.html +++ b/templates/jasset/asset_edit.html @@ -5,7 +5,7 @@ {% include 'nav_cat_bar.html' %}
-
+
修改资产基本信息
diff --git a/templates/jasset/asset_edit_batch.html b/templates/jasset/asset_edit_batch.html index 415146733..39be48671 100644 --- a/templates/jasset/asset_edit_batch.html +++ b/templates/jasset/asset_edit_batch.html @@ -18,7 +18,7 @@
-
+
{#
#} {#
填写修改主机信息.
#} diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 401d4adc6..dac011e49 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -194,7 +194,7 @@ title: title, maxmin: true, shade: false, - area: ['800px', '700px'], + area: ['800px', '600px'], content: new_url+data+'&check_assets='+check_assets }); //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'); @@ -282,7 +282,7 @@ type: 2, title: title, maxmin: true, - area: ['800px', '700px'], + area: ['800px', '600px'], shade: false, content: new_url }); diff --git a/templates/jasset/group_add.html b/templates/jasset/group_add.html index 357de00bd..716c789e6 100644 --- a/templates/jasset/group_add.html +++ b/templates/jasset/group_add.html @@ -19,7 +19,7 @@
-
+
填写主机组基本信息
diff --git a/templates/jasset/group_edit.html b/templates/jasset/group_edit.html index a105c278b..249db163d 100644 --- a/templates/jasset/group_edit.html +++ b/templates/jasset/group_edit.html @@ -19,7 +19,7 @@
-
+
填写主机组基本信息
diff --git a/templates/jasset/group_list.html b/templates/jasset/group_list.html index c5bd6aea4..ce647c2bc 100644 --- a/templates/jasset/group_list.html +++ b/templates/jasset/group_list.html @@ -5,7 +5,7 @@
-
+
主机组详细信息列表
diff --git a/templates/jasset/idc_add.html b/templates/jasset/idc_add.html index a093254d0..930384d3e 100644 --- a/templates/jasset/idc_add.html +++ b/templates/jasset/idc_add.html @@ -4,7 +4,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写IDC基本信息
diff --git a/templates/jasset/idc_edit.html b/templates/jasset/idc_edit.html index b4db0c424..13c7ec369 100644 --- a/templates/jasset/idc_edit.html +++ b/templates/jasset/idc_edit.html @@ -4,7 +4,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写IDC基本信息
diff --git a/templates/jasset/idc_list.html b/templates/jasset/idc_list.html index e8a214547..399fac549 100644 --- a/templates/jasset/idc_list.html +++ b/templates/jasset/idc_list.html @@ -5,7 +5,7 @@
-
+
IDC详细信息列表
diff --git a/templates/jlog/exec_detail.html b/templates/jlog/exec_detail.html new file mode 100644 index 000000000..1a0baf7b2 --- /dev/null +++ b/templates/jlog/exec_detail.html @@ -0,0 +1,126 @@ +{% extends 'base.html' %} +{% load mytags %} +{% block content %} + {% include 'nav_cat_bar.html' %} +
+
+
+
+
+ {{ log.id }} +
+ + + + + + + + + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
ID{{ log.id }}
用户名{{ log.user }}
来源IP{{ log.remote_ip }}
日期{{ log.datetime|date:"Y-m-d H:i:s" }}
主机 + + {% for asset_name in assets_hostname %} + {% if asset_name %} + + + + {% endif %} + {% endfor %} +
{{ asset_name }}
+
+
+
+
+
+
+
+
+
+
结果
+
+ + + + + + + + + + +
+
+
+
+
+ + + + + + {% for result, info in result.items %} + {% for host, msg in info.items %} + {% ifequal result 'failed' %} + + + + + {% else %} + + + + + {% endifequal %} + {% endfor %} + {% endfor %} +
命令{{ log.cmd }}
{{ host }}{{ msg }}
{{ host }}{{ msg }}
+
+
+
+
+
+ +
+
+ + + + +{% endblock %} \ No newline at end of file diff --git a/templates/jlog/log_exec.html b/templates/jlog/log_exec.html new file mode 100644 index 000000000..8dcf31711 --- /dev/null +++ b/templates/jlog/log_exec.html @@ -0,0 +1,107 @@ +{% extends 'base.html' %} +{% block self_head_css_js %} + + + +{% endblock %} +{% block content %} +{% include 'nav_cat_bar.html' %} + +
+
+
+
+
+
批量命令日志
+ +
+ +
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + {% for post in contacts.object_list %} + + + + + + + + + + {% endfor %} + +
ID 用户名 主机 命令 来源IP 时间 详情
{{ post.id }} {{ post.user }} {{ post.host | truncatechars:30 }} {{ post.cmd | truncatechars:30 }} {{ post.remote_ip }} {{ post.datetime|date:"Y-m-d H:i:s"}} + 详情 +
+
+
+
+ {% include 'paginator.html' %} +
+
+
+
+
+
+
+ + +{% endblock %} diff --git a/templates/jlog/log_file.html b/templates/jlog/log_file.html new file mode 100644 index 000000000..4aaf4ba90 --- /dev/null +++ b/templates/jlog/log_file.html @@ -0,0 +1,109 @@ +{% extends 'base.html' %} +{% block self_head_css_js %} + + + +{% endblock %} +{% block content %} +{% include 'nav_cat_bar.html' %} + +
+
+
+
+
+
上传下载日志
+ +
+ +
+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + {% for post in contacts.object_list %} + + + + + + + + + + + {% endfor %} + +
ID 用户名 主机 文件 类型 来源IP 时间 详情
{{ post.id }} {{ post.user }} {{ post.host | truncatechars:30 }} {{ post.filename | truncatechars:30 }} {{ post.type }} {{ post.remote_ip }} {{ post.datetime|date:"Y-m-d H:i:s"}} + 详情 +
+
+
+
+ {% include 'paginator.html' %} +
+
+
+
+
+
+
+ + +{% endblock %} diff --git a/templates/jlog/log_filter.html b/templates/jlog/log_filter.html index 3512e0568..e1752dec1 100644 --- a/templates/jlog/log_filter.html +++ b/templates/jlog/log_filter.html @@ -9,7 +9,7 @@
-
+
用户日志详细信息列表
diff --git a/templates/jlog/log_offline.html b/templates/jlog/log_offline.html index abb9723a6..c2ca33459 100644 --- a/templates/jlog/log_offline.html +++ b/templates/jlog/log_offline.html @@ -32,7 +32,7 @@
-
+
用户日志详细信息列表
@@ -54,6 +54,8 @@

diff --git a/templates/jlog/log_online.html b/templates/jlog/log_online.html index 604c77b95..3c522e056 100644 --- a/templates/jlog/log_online.html +++ b/templates/jlog/log_online.html @@ -47,7 +47,7 @@
-
+
用户日志详细信息列表
@@ -69,6 +69,8 @@

diff --git a/templates/jlog/user_history.html b/templates/jlog/user_history.html index 4e166b52e..0d45d3f79 100644 --- a/templates/jlog/user_history.html +++ b/templates/jlog/user_history.html @@ -28,7 +28,7 @@
-
+
用户{{ username }}日志详细信息列表
diff --git a/templates/jperm/perm_group_edit.html b/templates/jperm/perm_group_edit.html index 7f4019d70..30096e7e5 100644 --- a/templates/jperm/perm_group_edit.html +++ b/templates/jperm/perm_group_edit.html @@ -5,7 +5,7 @@
-
+
{{ user_group.name }}授权修改
diff --git a/templates/jperm/perm_group_list.html b/templates/jperm/perm_group_list.html index 6a1e8be85..f9c6bf2bc 100644 --- a/templates/jperm/perm_group_list.html +++ b/templates/jperm/perm_group_list.html @@ -5,7 +5,7 @@
-
+
查看小组
diff --git a/templates/jperm/perm_log.html b/templates/jperm/perm_log.html index ead5f399d..32e9f8ff0 100644 --- a/templates/jperm/perm_log.html +++ b/templates/jperm/perm_log.html @@ -5,7 +5,7 @@
-
+
查看小组
diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index 69c43fddc..43ed50e8c 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -9,7 +9,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
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/jperm/perm_role_edit.html b/templates/jperm/perm_role_edit.html index 4d587a972..8a3a858f2 100644 --- a/templates/jperm/perm_role_edit.html +++ b/templates/jperm/perm_role_edit.html @@ -9,7 +9,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
diff --git a/templates/jperm/perm_role_list.html b/templates/jperm/perm_role_list.html index 36c7023ba..3d799a808 100644 --- a/templates/jperm/perm_role_list.html +++ b/templates/jperm/perm_role_list.html @@ -5,7 +5,7 @@
-
+
{% if error %} diff --git a/templates/jperm/perm_role_push.html b/templates/jperm/perm_role_push.html index 480e4ef1b..767c496b5 100644 --- a/templates/jperm/perm_role_push.html +++ b/templates/jperm/perm_role_push.html @@ -9,7 +9,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
diff --git a/templates/jperm/perm_rule_add.html b/templates/jperm/perm_rule_add.html index 15eec08e9..e484ff4b1 100644 --- a/templates/jperm/perm_rule_add.html +++ b/templates/jperm/perm_rule_add.html @@ -9,7 +9,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
diff --git a/templates/jperm/perm_rule_detail.html b/templates/jperm/perm_rule_detail.html index c2e362b75..4a7a835d5 100644 --- a/templates/jperm/perm_rule_detail.html +++ b/templates/jperm/perm_rule_detail.html @@ -7,7 +7,7 @@
-
+
{{ rule.name }} @@ -60,7 +60,7 @@
-
+
授权用户/用户组 @@ -106,7 +106,7 @@
-
+
授权主机/主机组 diff --git a/templates/jperm/perm_rule_edit.html b/templates/jperm/perm_rule_edit.html index 603e98dbe..bbb7234b2 100644 --- a/templates/jperm/perm_rule_edit.html +++ b/templates/jperm/perm_rule_edit.html @@ -9,7 +9,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
diff --git a/templates/jperm/perm_rule_list.html b/templates/jperm/perm_rule_list.html index 923fc1442..8077a3842 100644 --- a/templates/jperm/perm_rule_list.html +++ b/templates/jperm/perm_rule_list.html @@ -7,7 +7,7 @@
-
+
{% if error %} diff --git a/templates/jperm/perm_sudo_add.html b/templates/jperm/perm_sudo_add.html index e46890cbb..f99587596 100644 --- a/templates/jperm/perm_sudo_add.html +++ b/templates/jperm/perm_sudo_add.html @@ -4,7 +4,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
diff --git a/templates/jperm/perm_sudo_edit.html b/templates/jperm/perm_sudo_edit.html index 42621b93a..b90fb151d 100644 --- a/templates/jperm/perm_sudo_edit.html +++ b/templates/jperm/perm_sudo_edit.html @@ -9,7 +9,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
diff --git a/templates/jperm/perm_sudo_list.html b/templates/jperm/perm_sudo_list.html index 2508eec8c..a48184f13 100644 --- a/templates/jperm/perm_sudo_list.html +++ b/templates/jperm/perm_sudo_list.html @@ -5,7 +5,7 @@
-
+
{% if error %} diff --git a/templates/jperm/sys_user_add.html b/templates/jperm/sys_user_add.html index ac4fd2db1..c6af5c183 100644 --- a/templates/jperm/sys_user_add.html +++ b/templates/jperm/sys_user_add.html @@ -4,7 +4,7 @@ {% include 'nav_cat_bar.html' %}
-
+
diff --git a/templates/jperm/sys_user_list.html b/templates/jperm/sys_user_list.html index 20236a0ea..3050cd985 100644 --- a/templates/jperm/sys_user_list.html +++ b/templates/jperm/sys_user_list.html @@ -5,7 +5,7 @@
-
+
diff --git a/templates/juser/change_info.html b/templates/juser/change_info.html index 40ec3cd55..ed8e3fe32 100644 --- a/templates/juser/change_info.html +++ b/templates/juser/change_info.html @@ -6,7 +6,7 @@ {% include 'nav_cat_bar.html' %}
-
+
编辑用户信息
diff --git a/templates/juser/group_add.html b/templates/juser/group_add.html index 3201a91d0..7257f5e58 100644 --- a/templates/juser/group_add.html +++ b/templates/juser/group_add.html @@ -4,7 +4,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
diff --git a/templates/juser/group_list.html b/templates/juser/group_list.html index db0f59a96..590b9f592 100644 --- a/templates/juser/group_list.html +++ b/templates/juser/group_list.html @@ -5,7 +5,7 @@
-
+
diff --git a/templates/juser/run_command.html b/templates/juser/run_command.html index a86e92e9b..bc64a9b46 100644 --- a/templates/juser/run_command.html +++ b/templates/juser/run_command.html @@ -6,7 +6,7 @@
-
+
命令批量执行
diff --git a/templates/juser/user_add.html b/templates/juser/user_add.html index 9824d9c7c..89b0d76d0 100644 --- a/templates/juser/user_add.html +++ b/templates/juser/user_add.html @@ -6,7 +6,7 @@ {% include 'nav_cat_bar.html' %}
-
+
填写基本信息
diff --git a/templates/juser/user_detail.html b/templates/juser/user_detail.html index 479dd991d..8f5517af2 100644 --- a/templates/juser/user_detail.html +++ b/templates/juser/user_detail.html @@ -6,7 +6,7 @@ {% include 'nav_cat_bar.html' %}
-
+
{{ user.name }} @@ -82,7 +82,7 @@
-
+
授权主机/组
@@ -137,7 +137,7 @@
-
+
登录记录
diff --git a/templates/juser/user_edit.html b/templates/juser/user_edit.html index 5dbeb05c4..80a125d65 100644 --- a/templates/juser/user_edit.html +++ b/templates/juser/user_edit.html @@ -6,7 +6,7 @@ {% include 'nav_cat_bar.html' %}
-
+
编辑用户信息
@@ -55,7 +55,7 @@
- +
-
- + 添加用户 + 删除所选 +
- +
diff --git a/templates/log_watch.html b/templates/log_watch.html index 9be625298..9099934f5 100644 --- a/templates/log_watch.html +++ b/templates/log_watch.html @@ -99,7 +99,7 @@ {#
#} {# #} {# #} -
+
实时监控
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命令
  • -
  • 日志审计
  • +
  • + 上传下载 + +
  • 设置
  • diff --git a/templates/nav_cat_bar.html b/templates/nav_cat_bar.html index 78c02e9c1..b0af56512 100644 --- a/templates/nav_cat_bar.html +++ b/templates/nav_cat_bar.html @@ -1,5 +1,5 @@
    -
    +

    {{ header_title }}

    -
    +
    diff --git a/templates/setting.html b/templates/setting.html index baf3c49ad..05104ac62 100644 --- a/templates/setting.html +++ b/templates/setting.html @@ -6,7 +6,7 @@ {% include 'nav_cat_bar.html' %}
    -
    +
    项目设置
    diff --git a/templates/upload.html b/templates/upload.html index 9e1e91e5f..2a5d712d9 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -50,7 +50,7 @@
    -
    +
    上传文件