diff --git a/jperm/views.py b/jperm/views.py index 3735b2001..57f2aaadd 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -3,7 +3,6 @@ import sys reload(sys) sys.setdefaultencoding('utf8') -from django.core.mail import send_mail from django.shortcuts import render_to_response from django.template import RequestContext from jperm.models import Perm, SudoPerm, CmdGroup, Apply @@ -11,12 +10,6 @@ from django.db.models import Q from jumpserver.api import * -CONF = ConfigParser() -CONF.read('%s/jumpserver.conf' % BASE_DIR) -send_ip = CONF.get('base', 'ip') -send_port = CONF.get('base', 'port') - - def asset_cmd_groups_get(asset_groups_select='', cmd_groups_select=''): asset_groups_select_list = [] cmd_groups_select_list = [] @@ -120,7 +113,7 @@ def dept_perm_list(request): if keyword: contact_list = DEPT.objects.filter(Q(name__icontains=keyword) | Q(comment__icontains=keyword)).order_by('name') else: - contact_list = DEPT.objects.filter(id__gt=1) + contact_list = DEPT.objects.filter(id__gt=2) contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(contact_list, request) @@ -533,14 +526,23 @@ def cmd_add(request): dept_id = request.POST.get('dept_id') cmd = ','.join(request.POST.get('cmd').split()) comment = request.POST.get('comment') - dept = DEPT.objects.filter(id=dept_id) - if dept: + + try: + if CmdGroup.objects.filter(name=name): + error = '%s 命令组已存在' + raise ServerError(error) + + if not dept: + error = u"部门不能为空" + raise ServerError(error) + except ServerError, e: + pass + else: dept = dept[0] CmdGroup.objects.create(name=name, dept=dept, cmd=cmd, comment=comment) - else: - error = u"部门不能为空" - msg = u'命令组添加成功' + msg = u'命令组添加成功' + return HttpResponseRedirect('/jperm/cmd_list/') return render_to_response('jperm/sudo_cmd_add.html', locals(), context_instance=RequestContext(request)) @@ -555,8 +557,16 @@ def cmd_add_adm(request): cmd = ','.join(request.POST.get('cmd').split()) comment = request.POST.get('comment') - CmdGroup.objects.create(name=name, dept=dept, cmd=cmd, comment=comment) - msg = u'命令组添加成功' + try: + if CmdGroup.objects.filter(name=name): + error = '%s 命令组已存在' + raise ServerError(error) + except ServerError, e: + pass + else: + CmdGroup.objects.create(name=name, dept=dept, cmd=cmd, comment=comment) + return HttpResponseRedirect('/jperm/cmd_list/') + return HttpResponseRedirect('/jperm/cmd_list/') return render_to_response('jperm/sudo_cmd_add.html', locals(), context_instance=RequestContext(request)) @@ -568,10 +578,12 @@ def cmd_edit(request): cmd_group_id = request.GET.get('id') cmd_group = CmdGroup.objects.filter(id=cmd_group_id) + dept_all = DEPT.objects.all() if cmd_group: cmd_group = cmd_group[0] cmd_group_id = cmd_group.id + dept_id = cmd_group.dept.id name = cmd_group.name cmd = '\n'.join(cmd_group.cmd.split(',')) comment = cmd_group.comment @@ -579,12 +591,23 @@ def cmd_edit(request): if request.method == 'POST': cmd_group_id = request.POST.get('cmd_group_id') name = request.POST.get('name') + dept_id = request.POST.get('dept_id') cmd = ','.join(request.POST.get('cmd').split()) comment = request.POST.get('comment') - cmd_group = CmdGroup.objects.filter(id=cmd_group_id) - if cmd_group: - cmd_group.update(name=name, cmd=cmd, comment=comment) + + dept = DEPT.objects.filter(id=dept_id) + try: + if not dept: + error = '没有该部门' + raise ServerError(error) + + if not cmd_group: + error = '没有该命令组' + except ServerError, e: + pass + else: + cmd_group.update(name=name, cmd=cmd, dept=dept[0], comment=comment) return HttpResponseRedirect('/jperm/cmd_list/') return render_to_response('jperm/sudo_cmd_add.html', locals(), context_instance=RequestContext(request)) @@ -624,10 +647,22 @@ def cmd_del(request): @require_admin def cmd_detail(request): - cmd_id = request.GET.get('id') - cmd_group = CmdGroup.objects.filter(id=cmd_id) - if cmd_group: - cmd_group = cmd_group[0] + cmd_ids = request.GET.get('id').split(',') + cmds = [] + if len(cmd_ids) == 1: + cmd_group = CmdGroup.objects.filter(id=cmd_ids[0]) + if cmd_group: + cmd_group = cmd_group[0] + cmds.extend(cmd_group.cmd.split(',')) + cmd_group_name = cmd_group.name + else: + cmd_groups = [] + for cmd_id in cmd_ids: + cmd_groups.extend(CmdGroup.objects.filter(id=cmd_id)) + for cmd_group in cmd_groups: + cmds.extend(cmd_group.cmd.split(',')) + + cmds_str = ', '.join(cmds) return render_to_response('jperm/sudo_cmd_detail.html', locals(), context_instance=RequestContext(request)) @@ -660,7 +695,7 @@ def perm_apply(request): time_now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') a = Apply.objects.create(applyer=applyer, dept=dept, bisgroup=group, date_add=datetime.datetime.now(), asset=hosts, status=0, comment=comment) uuid = a.uuid - url = "http://%s:%s/jperm/apply_exec/?uuid=%s" % (send_ip, send_port, uuid) + url = "http://%s:%s/jperm/apply_exec/?uuid=%s" % (SEND_IP, SEND_PORT, uuid) mail_msg = """ Hi,%s: 有新的权限申请, 详情如下: @@ -674,7 +709,7 @@ def perm_apply(request): %s """ % (da.username, applyer, group_lis, hosts_lis, time_now, comment, url) - send_mail(mail_title, mail_msg, 'jkfunshion@fun.tv', [mail_address], fail_silently=False) + send_mail(mail_title, mail_msg, MAIL_FROM, [mail_address], fail_silently=False) smg = "提交成功,已发邮件通知部门管理员。" return render_to_response('jperm/perm_apply.html', locals(), context_instance=RequestContext(request)) return render_to_response('jperm/perm_apply.html', locals(), context_instance=RequestContext(request)) diff --git a/jumpserver.conf b/jumpserver.conf index 291aaf3ef..58a6475e7 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -1,8 +1,10 @@ #coding: utf8 [base] -ip = 192.168.173.129 +ip = 192.168.20.209 port = 80 +key = 88aaaf7ffe3c6c04 + [db] @@ -25,13 +27,9 @@ root_pw = secret234 web_socket_host = 192.168.173.129:3000 -[web] -key = 88aaaf7ffe3c6c04 - - [mail] -email_host = mail.funshion.com +email_host = smtp.exmail.qq.com email_port = 25 -email_host_user = jkfunshion -email_host_password = jkmail% +email_host_user = noreply@jumpserver.org +email_host_password = jumpserver123 email_use_tls = False diff --git a/jumpserver/api.py b/jumpserver/api.py index 6f642188c..db2b93e3b 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -19,6 +19,7 @@ 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.mail import send_mail BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) @@ -27,18 +28,12 @@ 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') -KEY = CONF.get('web', 'key') +KEY = CONF.get('base', 'key') LOGIN_NAME = getpass.getuser() LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') - - -# def user_perm_group_api(username): -# user = User.objects.get(username=username) -# if user: -# perm_list = [] -# user_group_all = user.group.all() -# for user_group in user_group_all: -# perm_list.extend(user_group.perm_set.all()) +SEND_IP = CONF.get('base', 'ip') +SEND_PORT = CONF.get('base', 'port') +MAIL_FROM = CONF.get('mail', 'email_host_user') class LDAPMgmt(): @@ -201,6 +196,9 @@ def require_login(func): 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) @@ -209,6 +207,9 @@ def require_super_user(func): 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) @@ -276,7 +277,8 @@ def view_splitter(request, su=None, adm=None): return su(request) elif is_group_admin(request): return adm(request) - raise Http404 + else: + return HttpResponseRedirect('/login/') def user_perm_group_api(username): @@ -384,39 +386,38 @@ def validate(request, user_group=None, user=None, asset_group=None, asset=None, if user_group: dept_user_groups = dept.usergroup_set.all() - user_groups = [] - for user_group_id in user_group: - user_groups.extend(UserGroup.objects.filter(id=user_group_id)) - if not set(user_groups).issubset(set(dept_user_groups)): + user_group_ids = [] + for group in dept_user_groups: + user_group_ids.append(str(group.id)) + + if not set(user_group).issubset(set(user_group_ids)): return False if user: dept_users = dept.user_set.all() - users = [] - for user_id in user: - users.extend(User.objects.filter(id=user_id)) + user_ids = [] + for user in dept_users: + user_ids.append(str(user.id)) - if not set(users).issubset(set(dept_users)): + if not set(user).issubset(set(user_ids)): return False if asset_group: dept_asset_groups = dept.bisgroup_set.all() - asset_groups = [] - for group_id in asset_group: - asset_groups.extend(BisGroup.objects.filter(id=int(group_id))) + asset_group_ids = [] + for group in dept_asset_groups: + asset_group_ids.append(group.id) - if not set(asset_groups).issubset(set(dept_asset_groups)): + if not set(asset_group).issubset(set(asset_group_ids)): return False if asset: dept_assets = dept.asset_set.all() - assets, eassets = [], [] - for asset_id in dept_assets: - eassets.append(int(asset_id.id)) - for i in asset: - assets.append(int(i)) + asset_ids = [] + for asset in dept_assets: + asset_ids.append(str(asset.id)) - if not set(assets).issubset(eassets): + if not set(asset).issubset(set(asset_ids)): return False return True diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 8ca36df99..a427f3b5e 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -163,6 +163,18 @@ def ugrp_perm_agrp_count(user_group_id): 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) @@ -175,6 +187,21 @@ def ugrp_perm_asset_count(user_group_id): 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) @@ -304,8 +331,52 @@ def sudo_cmd_list(cmd_group_id): @register.filter(name='sudo_cmd_count') -def sudo_cmd_count(cmd_group_id): - cmd_group = CmdGroup.objects.filter(id=cmd_group_id) - if cmd_group: - cmd_group = cmd_group[0] - return len(cmd_group.cmd.split(',')) +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' + + diff --git a/jumpserver/views.py b/jumpserver/views.py index 609be01ab..ec012bcb6 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -50,7 +50,7 @@ def index_cu(request): user = user[0] login_types = {'L': 'LDAP', 'M': 'MAP'} user_id = request.session.get('user_id') - username = User.objects.get(id=user_id).name + username = User.objects.get(id=user_id).username posts = user_perm_asset_api(username) host_count = len(posts) new_posts = [] @@ -248,11 +248,12 @@ def login(request): else: username = request.POST.get('username') password = request.POST.get('password') - user = User.objects.filter(username=username) - if user: - user = user[0] + user_filter = User.objects.filter(username=username) + if user_filter: + user = user_filter[0] if md5_crypt(password) == user.password: request.session['user_id'] = user.id + user_filter.update(last_login=datetime.datetime.now()) if user.role == 'SU': request.session['role_id'] = 2 elif user.role == 'DA': diff --git a/juser/views.py b/juser/views.py index ad3bf4405..c470013b1 100644 --- a/juser/views.py +++ b/juser/views.py @@ -68,9 +68,11 @@ def db_add_user(**kwargs): group = UserGroup.objects.filter(id=group_id) group_select.extend(group) user.group = group_select + return user def db_update_user(**kwargs): + print kwargs groups_post = kwargs.pop('groups') user_id = kwargs.pop('user_id') user = User.objects.filter(id=user_id) @@ -152,18 +154,8 @@ def ldap_add_user(username, ldap_pwd): 'userPassword': ['{crypt}x'], 'gidNumber': [str(user.id)]} - # sudo_dn = 'cn=%s,ou=Sudoers,%s' % (username, LDAP_BASE_DN) - # sudo_attr = {'objectClass': ['top', 'sudoRole'], - # 'cn': ['%s' % str(username)], - # 'sudoCommand': ['/bin/pwd'], - # 'sudoHost': ['192.168.1.1'], - # 'sudoOption': ['!authenticate'], - # 'sudoRunAsUser': ['root'], - # 'sudoUser': ['%s' % str(username)]} - ldap_conn.add(user_dn, user_attr) ldap_conn.add(group_dn, group_attr) - # ldap_conn.add(sudo_dn, sudo_attr) def ldap_del_user(username): @@ -354,11 +346,16 @@ def group_add(request): error = u'组名 或 部门 不能为空' raise AddError(error) + if UserGroup.objects.filter(name=group_name): + error = u'组名已存在' + raise AddError(error) + dept = DEPT.objects.filter(id=dept_id) if dept: dept = dept[0] else: - AddError(u'部门不存在') + error = u'部门不存在' + raise AddError(error) db_add_group(name=group_name, users=users_selected, dept=dept, comment=comment) except AddError: @@ -596,13 +593,13 @@ def user_add(request): if request.method == 'POST': username = request.POST.get('username', '') - password = request.POST.get('password', '') + password = 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 = request.POST.get('ssh_key_pwd', '') + ssh_key_pwd = gen_rand_pwd(16) is_active = True if request.POST.get('is_active', '1') == '1' else False ldap_pwd = gen_rand_pwd(16) @@ -626,19 +623,30 @@ def user_add(request): pass else: try: - db_add_user(username=username, - password=md5_crypt(password), - name=name, email=email, dept=dept, - groups=groups, role=role_post, - ssh_key_pwd=CRYPTOR.encrypt(ssh_key_pwd), - ldap_pwd=CRYPTOR.encrypt(ldap_pwd), - is_active=is_active, - date_joined=datetime.datetime.now()) + user = db_add_user(username=username, + password=md5_crypt(password), + name=name, email=email, dept=dept, + groups=groups, role=role_post, + ssh_key_pwd=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) - msg = u'添加用户 %s 成功!' % username + 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) @@ -649,6 +657,9 @@ def user_add(request): ldap_del_user(username) except Exception: pass + else: + 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)) @@ -662,11 +673,11 @@ def user_add_adm(request): if request.method == 'POST': username = request.POST.get('username', '') - password = request.POST.get('password', '') + password = gen_rand_pwd(16) name = request.POST.get('name', '') email = request.POST.get('email', '') groups = request.POST.getlist('groups', []) - ssh_key_pwd = request.POST.get('ssh_key_pwd', '') + ssh_key_pwd = gen_rand_pwd(16) is_active = True if request.POST.get('is_active', '1') == '1' else False ldap_pwd = gen_rand_pwd(16) @@ -687,7 +698,7 @@ def user_add_adm(request): password=md5_crypt(password), name=name, email=email, dept=dept, groups=groups, role='CU', - ssh_key_pwd=CRYPTOR.encrypt(ssh_key_pwd), + ssh_key_pwd=md5_crypt(ssh_key_pwd), ldap_pwd=CRYPTOR.encrypt(ldap_pwd), is_active=is_active, date_joined=datetime.datetime.now()) @@ -695,7 +706,6 @@ def user_add_adm(request): server_add_user(username, password, ssh_key_pwd) if LDAP_ENABLE: ldap_add_user(username, ldap_pwd) - msg = u'添加用户 %s 成功!' % username except Exception, e: error = u'添加用户 %s 失败 %s ' % (username, e) @@ -706,6 +716,23 @@ def user_add_adm(request): 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) + print MAIL_FROM + 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)) @@ -843,7 +870,7 @@ def user_edit(request): groups_str = ' '.join([str(group.id) for group in user.group.all()]) else: - user_id = request.GET.get('user_id', '') + user_id = request.POST.get('user_id', '') password = request.POST.get('password', '') name = request.POST.get('name', '') email = request.POST.get('email', '') @@ -858,7 +885,7 @@ def user_edit(request): if dept: dept = dept[0] else: - dept = DEPT.objects.get(id='1') + dept = DEPT.objects.get(id='2') if user_id: user = User.objects.filter(id=user_id) diff --git a/log_handler.py b/log_handler.py index f64445cd4..a3942a288 100755 --- a/log_handler.py +++ b/log_handler.py @@ -1,5 +1,5 @@ #!/usr/bin/python -#coding: utf-8 +# coding: utf-8 import os import re diff --git a/service.sh b/service.sh new file mode 100644 index 000000000..fbac87af4 --- /dev/null +++ b/service.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Date: 2015-04-12 +# Version: 2.0.0 +# Site: http://www.jumpserver.org +# Author: jumpserver group + +. /etc/init.d/functions +export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/node/bin + +base_dir=$(dirname $0) + +case $1 in +start) + daemon $base_dir/manage.py runserver 0.0.0.0:80 & + daemon $base_dir/log_handler.py & + cd $base_dir/websocket/; daemon node index.js & + ;; + +stop) + pkill -15 python + pkill -15 node + ;; + +esac diff --git a/static/js/base.js b/static/js/base.js index 4b0adfe76..6a88472bc 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -59,10 +59,13 @@ function GetTableDataBox() { return returnData; } -function move(from, to) { +function move(from, to, from_o, to_o) { $("#" + from + " option").each(function () { if ($(this).prop("selected") == true) { $("#" + to).append(this); + if( typeof from_o !== 'undefined'){ + $("#"+to_o).append($("#"+from_o +" option[value='"+this.value+"']")); + } } }); } @@ -76,12 +79,12 @@ function move_left(from, to) { }); } -function move_all(from, to) { - $("#" + from).children().each(function () { - $("#" + to).append(this); - }); -} - +//function move_all(from, to) { +// $("#" + from).children().each(function () { +// $("#" + to).append(this); +// }); +//} +// function selectAll(){ var checklist = document.getElementsByName ("selected"); @@ -100,12 +103,12 @@ function selectAll(){ } - -function move_all(from, to){ - $("#"+from).children().each(function(){ - $("#"+to).append(this); - }); -} +// +//function move_all(from, to){ +// $("#"+from).children().each(function(){ +// $("#"+to).append(this); +// }); +//} //function commit_select(form_array){ // $('#{0} option'.format(form_array)).each(function(){ diff --git a/templates/jperm/dept_perm_edit.html b/templates/jperm/dept_perm_edit.html index 10c2d7fae..7d9c8247f 100644 --- a/templates/jperm/dept_perm_edit.html +++ b/templates/jperm/dept_perm_edit.html @@ -16,8 +16,6 @@ function search_ip(text, noselect, total){ }) } - -
ID | @@ -31,10 +34,11 @@{{ cmd_group.name }} | {{ cmd_group.dept.name }} | ||||
命令: | - {{ cmd_group.cmd }} + {{ cmds_str }} |