diff --git a/connect.py b/connect.py index 61bace914..3d52e727b 100755 --- a/connect.py +++ b/connect.py @@ -34,12 +34,12 @@ except ImportError: time.sleep(3) sys.exit() -CURRENT_DIR = os.path.dirname(__file__) +BASE_DIR = os.path.dirname(__file__) CONF = ConfigParser() -CONF.read(os.path.join(CURRENT_DIR, 'jumpserver.conf')) -LOG_DIR = os.path.join(CURRENT_DIR, 'logs') +CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) +LOG_DIR = os.path.join(BASE_DIR, 'logs') # Web generate user ssh_key dir. -SSH_KEY_DIR = os.path.join(CURRENT_DIR, 'keys') +SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys') # User upload the server key to this dir. SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') # The key of decryptor. diff --git a/jasset/views.py b/jasset/views.py index 7419519a8..8472d3d3f 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -4,6 +4,7 @@ from django.shortcuts import render_to_response from django.core.paginator import Paginator, EmptyPage from models import IDC, Asset, BisGroup +from models import IDC, Asset, UserGroup from connect import PyCrypt, KEY @@ -17,6 +18,9 @@ def jadd_host(request): cryptor = PyCrypt(KEY) eidc = IDC.objects.all() egroup = BisGroup.objects.all() + egroup = UserGroup.objects.all() + is_actived = {'active': 1, 'no_active': 0} + login_typed = {'LDAP': 'L', 'SSH_KEY': 'S', 'PASSWORD': 'P', 'MAP': 'M'} if request.method == 'POST': j_ip = request.POST.get('j_ip') @@ -30,6 +34,7 @@ def jadd_host(request): j_idc = IDC.objects.get(name=j_idc) for group in j_group: c = BisGroup.objects.get(name=group) + c = UserGroup.objects.get(name=group) groups.append(c) if Asset.objects.filter(ip=str(j_ip)): @@ -130,4 +135,4 @@ def jadd_group(request): def jlist_group(request): header_title, path1, path2 = '添加业务组 | Add Group', '资产管理', '查看业务组' posts = BisGroup.objects.all().order_by('id') - return render_to_response('jasset/jlist_group.html', locals(), context_instance=RequestContext(request)) \ No newline at end of file + return render_to_response('jasset/jlist_group.html', locals(), context_instance=RequestContext(request)) diff --git a/jumpserver.conf b/jumpserver.conf index 1523fb3ba..18364c398 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -12,6 +12,11 @@ host_url = ldap://192.168.8.60:389 base_dn = dc=fengxing,dc=org root_dn = cn=admin,dc=fengxing,dc=org root_pw = 123456 +ldap_enable = 0 +host_url = ldap://127.0.0.1:389 +base_dn = dc=jumpserver,dc=org +root_dn = cn=admin,dc=jumpserver,dc=org +root_pw = secret234 [web] key = 88aaaf7ffe3c6c04 diff --git a/jumpserver/settings.py b/jumpserver/settings.py index 67e9bc776..5b991157f 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -46,6 +46,7 @@ INSTALLED_APPS = ( 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'jumpserver', 'juser', 'jasset', 'jpermission', diff --git a/jumpserver/templatetags/__init__.py b/jumpserver/templatetags/__init__.py new file mode 100644 index 000000000..bfd53d39f --- /dev/null +++ b/jumpserver/templatetags/__init__.py @@ -0,0 +1 @@ +__author__ = 'Hudie' diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py new file mode 100644 index 000000000..3ef015a5c --- /dev/null +++ b/jumpserver/templatetags/mytags.py @@ -0,0 +1,17 @@ +import time +from django import template + +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): + return str(value) diff --git a/jumpserver/views.py b/jumpserver/views.py index baeebf2d5..ef9c9560c 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -10,3 +10,5 @@ def base(request): def skin_config(request): return render_to_response('skin_config.html') + + diff --git a/juser/views.py b/juser/views.py index 5c44bdc16..09e262b2c 100644 --- a/juser/views.py +++ b/juser/views.py @@ -1,20 +1,125 @@ # coding: utf-8 +# Author: Guanghongwei +# Email: ibuler@qq.com + +import time +import os +import hashlib +import random +import subprocess +import ldap +from ldap import modlist +from Crypto.PublicKey import RSA +import crypt from django.shortcuts import render_to_response +from django.core.exceptions import ObjectDoesNotExist from juser.models import UserGroup, User +from connect import PyCrypt, KEY +from connect import BASE_DIR +from connect import CONF + + +CRYPTOR = PyCrypt(KEY) +LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') +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') + + +def md5_crypt(string): + return hashlib.new("md5", string).hexdigest() + + +def gen_rand_pwd(num): + """生成随机密码""" + seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + salt_list = [] + for i in range(num): + salt_list.append(random.choice(seed)) + salt = ''.join(salt_list) + return salt + + +def bash(cmd): + """执行bash命令""" + return subprocess.call(cmd, shell=True) + + +def is_dir(dir_name, mode=0755): + if not os.path.isdir(dir_name): + os.makedirs(dir_name) + os.chmod(dir_name, mode) class AddError(Exception): pass +# class LDAPMgmt(): +# def __init__(self, +# host_url=LDAP_HOST_URL, +# base_dn=LDAP_BASE_DN, +# root_cn=LDAP_ROOT_DN, +# root_pw=LDAP_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): +# 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): +# try: +# ldif = modlist.addModlist(attrs) +# self.conn.add_s(dn, ldif) +# except ldap.LDAPError, e: +# print e +# +# def modify(self, dn, attrs): +# 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): +# try: +# self.conn.delete_s(dn) +# except ldap.LDAPError, e: +# print e + + +def gen_sha512(salt, password): + return crypt.crypt(password, '$6$%s$' % salt) + + def group_add(request): error = '' msg = '' + header_title, path1, path2 = '添加属组 | Add Group', 'juser', 'group_add' + if request.method == 'POST': - group_name = request.POST.get('j_group_name', None) - comment = request.POST.get('j_comment', None) + group_name = request.POST.get('group_name', None) + comment = request.POST.get('comment', None) try: if not group_name: @@ -37,44 +142,177 @@ def group_add(request): else: msg = u'添加组 %s 成功' % group_name - return render_to_response('juser/group_add.html', - {'header_title': u'添加属组 | Add Group', - 'path1': 'juser', 'path2': 'group_add', - 'error': error, 'msg': msg}) + return render_to_response('juser/group_add.html', locals()) def group_list(request): + header_title, path1, path2 = '查看属组 | Add Group', 'juser', 'group_add' groups = UserGroup.objects.all() - return render_to_response('juser/group_list.html', - {'header_title': u'查看属组 | Add Group', - 'path1': 'juser', 'path2': 'group_add', - 'groups': groups}) + return render_to_response('juser/group_list.html', locals()) def user_list(request): pass +def db_add_user(**kwargs): + groups_post = kwargs.pop('groups') + user = User(**kwargs) + group_select = [] + for group_id in groups_post: + group = UserGroup.objects.filter(id=group_id) + group_select.extend(group) + user.save() + user.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) + 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, 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_pwd1): + bash('useradd %s; echo %s | passwd --stdin %s' % (username, password, username)) + gen_ssh_key(username, ssh_key_pwd1) + + +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.get(username=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)]} + + 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 = LDAPMgmt() + + 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): + 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 = LDAPMgmt() + ldap_conn.delete(user_dn) + ldap_conn.delete(group_dn) + ldap_conn.delete(sudo_dn) + + def user_add(request): error = '' msg = '' - - user_role = {'SU': 'SuperUser', 'GA': 'GroupAdmin', 'CU': 'CommonUser'} - groups = UserGroup.objects.all() + header_title, path1, path2 = '添加用户 | Add User', 'juser', 'user_add' + user_role = {'SU': u'超级管理员', 'GA': u'组管理员', 'CU': u'普通用户'} + all_group = UserGroup.objects.all() if request.method == 'POST': - username = request.POST.get('j_username', None) - password = request.POST.get('j_password', None) - name = request.POST.get('j_name', None) - email = request.POST.get('j_email', '') - groups = request.POST.getlist('j_group', None) - role = request.POST.get('j_role', None) - ssh_pwd = request.POST.get('j_ssh_pwd', None) - is_active = request.POST.get('j_is_active', None) + username = request.POST.get('username', None) + password = request.POST.get('password', None) + name = request.POST.get('name', None) + email = request.POST.get('email', '') + groups = request.POST.getlist('groups', None) + groups_str = ' '.join(groups) + role_post = request.POST.get('role', None) + ssh_pwd = request.POST.get('ssh_pwd', None) + ssh_key_pwd1 = request.POST.get('ssh_key_pwd1', None) + is_active = request.POST.get('is_active', '1') + ldap_pwd = gen_rand_pwd(16) - return render_to_response('juser/user_add.html', - {'header_title': u'添加用户 | Add User', - 'path1': 'juser', 'path2': 'user_add', - 'roles': user_role, 'groups': groups}) + try: + if None in [username, password, ssh_key_pwd1, name, groups, role_post, is_active]: + error = u'带*内容不能为空' + raise AddError + user = User.objects.filter(username=username) + if user: + error = u'用户 %s 已存在' % username + raise AddError + + except AddError: + pass + else: + time_now = time.time() + try: + db_add_user(username=username, + password=md5_crypt(password), + name=name, email=email, + groups=groups, role=role_post, + ssh_pwd=CRYPTOR.encrypt(ssh_pwd), + ssh_key_pwd1=CRYPTOR.encrypt(ssh_key_pwd1), + ldap_pwd=CRYPTOR.encrypt(ldap_pwd), + is_active=is_active, + date_joined=time_now) + + server_add_user(username, password, ssh_key_pwd1) + if LDAP_ENABLE: + ldap_add_user(username, ldap_pwd) + msg = u'添加用户 %s 成功!' % username + + 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 + + return render_to_response('juser/user_add.html', locals()) diff --git a/static/css/style.css b/static/css/style.css index 9c9fc28d3..8550c5a87 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,10 +1,5 @@ -/*@import url("//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&lang=en");*/ -/* - * - * INSPINIA - Responsive Admin Theme - * Copyright 2014 Webapplayers.com - * -*/ +@import url("//fonts.useso.com/css?family=Open+Sans:300,400,600,700&lang=en"); + h1, h2, h3, diff --git a/static/css/vaildator/images/loading.gif b/static/css/vaildator/images/loading.gif new file mode 100755 index 000000000..6e5bace6e Binary files /dev/null and b/static/css/vaildator/images/loading.gif differ diff --git a/static/css/vaildator/images/validator_default.png b/static/css/vaildator/images/validator_default.png new file mode 100755 index 000000000..5a9564095 Binary files /dev/null and b/static/css/vaildator/images/validator_default.png differ diff --git a/static/css/vaildator/images/validator_simple.png b/static/css/vaildator/images/validator_simple.png new file mode 100755 index 000000000..2470db588 Binary files /dev/null and b/static/css/vaildator/images/validator_simple.png differ diff --git a/static/css/vaildator/jquery.validator.css b/static/css/vaildator/jquery.validator.css new file mode 100755 index 000000000..9de60962f --- /dev/null +++ b/static/css/vaildator/jquery.validator.css @@ -0,0 +1,64 @@ +/*! nice Validator 0.7.3 + * (c) 2012-2014 Jony Zhang , MIT Licensed + * http://niceue.com/validator/ + */ +.n-inline-block,.nice-validator input,.nice-validator select,.nice-validator textarea,.msg-wrap,.n-icon,.n-msg{display:inline-block;*display:inline;*zoom:1} +.msg-box{position:relative;*zoom:1} +.msg-wrap{position:relative;white-space:nowrap} +.msg-wrap,.n-icon,.n-msg{vertical-align:top} +.n-arrow{position:absolute;overflow:hidden;} +.n-arrow b,.n-arrow i{position:absolute;left:0;top:0;border:0;margin:0;padding:0;overflow:hidden;font-weight:400;font-style:normal;font-size:12px;font-family:serif;line-height:14px;_line-height:15px} +.n-arrow i{text-shadow:none} +.n-icon{width:16px;height:16px;overflow:hidden;background-repeat:no-repeat} +.n-msg{display:inline-block;line-height:15px;margin-left:2px;*margin-top:-1px;_margin-top:0;font-size:12px;font-family:simsun} +.n-error{color:#c33} +.n-ok{color:#390} +.n-tip,.n-loading{color:#808080} +.n-error .n-icon{background-position:0 0} +.n-ok .n-icon{background-position:-16px 0} +.n-tip .n-icon{background-position:-32px 0} +.n-loading .n-icon{background:url("images/loading.gif") 0 center no-repeat !important} +.n-top,.n-right,.n-bottom,.n-left{display:inline-block;line-height:0;vertical-align:top;outline:0} +.n-top .n-arrow,.n-bottom .n-arrow{height:6px;width:12px;left:8px} +.n-left .n-arrow,.n-right .n-arrow{width:6px;height:12px;top:6px} +.n-top{vertical-align:top;} +.n-top .msg-wrap{margin-bottom:6px} +.n-top .n-arrow{bottom:-6px;} +.n-top .n-arrow b{top:-6px} +.n-top .n-arrow i{top:-7px} +.n-bottom{vertical-align:bottom;} +.n-bottom .msg-wrap{margin-top:6px} +.n-bottom .n-arrow{top:-6px;} +.n-bottom .n-arrow b{top:-1px} +.n-bottom .n-arrow i{top:0} +.n-left .msg-wrap{right:100%;margin-right:6px} +.n-left .n-arrow{right:-6px;} +.n-left .n-arrow b{left:-6px} +.n-left .n-arrow i{left:-7px} +.n-right .msg-wrap{margin-left:6px} +.n-right .n-arrow{left:-6px;} +.n-right .n-arrow b{left:1px} +.n-right .n-arrow i{left:2px} +.n-default .n-left,.n-default .n-right{margin-top:5px} +.n-default .n-top .msg-wrap{bottom:100%} +.n-default .n-bottom .msg-wrap{top:100%} +.n-default .msg-wrap{position:absolute;z-index:1;} +.n-default .msg-wrap .n-icon{background-image:url("images/validator_default.png")} +.n-default .n-tip .n-icon{display:none} +.n-simple .msg-wrap{position:absolute;z-index:1;} +.n-simple .msg-wrap .n-icon{background-image:url("images/validator_simple.png")} +.n-simple .n-top .msg-wrap{bottom:100%} +.n-simple .n-bottom .msg-wrap{top:100%} +.n-simple .n-left,.n-simple .n-right{margin-top:5px} +.n-simple .n-bottom .msg-wrap{margin-top:3px} +.n-simple .n-tip .n-icon{display:none} +.n-yellow .msg-wrap{position:absolute;z-index:1;padding:4px 6px;font-size:12px;border:1px solid transparent;background-color:#fffcef;border-color:#ffbb76;color:#db7c22;box-shadow:0 1px 3px #ccc;border-radius:2px;} +.n-yellow .msg-wrap .n-arrow b{color:#ffbb76;text-shadow:0 0 2px #ccc} +.n-yellow .msg-wrap .n-arrow i{color:#fffcef} +.n-yellow .msg-wrap .n-icon{background-image:url("images/validator_simple.png")} +.n-yellow .n-top .msg-wrap{bottom:100%} +.n-yellow .n-bottom .msg-wrap{top:100%} +.n-yellow .n-tip,.n-yellow .n-ok,.n-yellow .n-loading{background-color:#f8fdff;border-color:#ddd;color:#333;box-shadow:0 1px 3px #ccc;} +.n-yellow .n-tip .n-arrow b,.n-yellow .n-ok .n-arrow b,.n-yellow .n-loading .n-arrow b{color:#ddd;text-shadow:0 0 2px #ccc} +.n-yellow .n-tip .n-arrow i,.n-yellow .n-ok .n-arrow i,.n-yellow .n-loading .n-arrow i{color:#f8fdff} +.n-yellow .n-tip .n-icon{display:none} diff --git a/static/js/validator/images/loading.gif b/static/js/validator/images/loading.gif new file mode 100755 index 000000000..6e5bace6e Binary files /dev/null and b/static/js/validator/images/loading.gif differ diff --git a/static/js/validator/images/validator_default.png b/static/js/validator/images/validator_default.png new file mode 100755 index 000000000..5a9564095 Binary files /dev/null and b/static/js/validator/images/validator_default.png differ diff --git a/static/js/validator/images/validator_simple.png b/static/js/validator/images/validator_simple.png new file mode 100755 index 000000000..2470db588 Binary files /dev/null and b/static/js/validator/images/validator_simple.png differ diff --git a/static/js/validator/jquery.validator.js b/static/js/validator/jquery.validator.js new file mode 100755 index 000000000..20a83dc9e --- /dev/null +++ b/static/js/validator/jquery.validator.js @@ -0,0 +1,5 @@ +/*! nice Validator 0.7.3 + * (c) 2012-2014 Jony Zhang , MIT Licensed + * http://niceue.com/validator/ + */ +!function(e,t){"use strict";function i(n,s){var r=this;return!r instanceof i?new i(n,s):(r.$el=e(n),r._init(n,s),t)}function n(e,t){var i=t?t===!0?this:t:n.prototype;if(z(e))for(var s in e)i[s]=r(e[s])}function s(e,t){var i=t?t===!0?this:t:s.prototype;if(z(e))for(var n in e){if(!e[n])return;i[n]=e[n]}}function r(t){switch(e.type(t)){case"function":return t;case"array":return function(e){return t[0].test(e.value)||t[1]||!1};case"regexp":return function(e){return t.test(e.value)}}}function a(t){var i="";return e.map(t.split(" "),function(e){i+=","+("#"===e.charAt(0)?e:'[name="'+e+'"]')}),i.substring(1)}function l(t){var i;if(t&&t.tagName){switch(t.tagName){case"INPUT":case"SELECT":case"TEXTAREA":case"BUTTON":case"FIELDSET":i=t.form||e(t).closest("."+k);break;case"FORM":i=t;break;default:i=e(t).closest("."+k)}return e(i).data(h)||e(i)[h]().data(h)}}function u(e){var t,i=e.currentTarget;i.form&&null===K(i.form,q)&&(t=l(i),t?(t._parse(i),t["_"+e.type](e)):K(i,V,null))}function o(i,n){var s=e.trim(K(i,V+"-"+n));if(s)return s=Function("return "+s)(),s?r(s):t}function d(e,t,i,n){var s=t.msg,r=t._r;return z(s)&&(s=s[r]),Q(s)||(s=K(e,A+"-"+r)||K(e,A)||i||(n?Q(n)?n:n[r]:"")),s}function c(e){var t;return e&&(t=H.exec(e)),t?t[1]:""}function f(e){return"INPUT"===e.tagName&&"checkbox"===e.type||"radio"===e.type}function g(e){return Date.parse(e.replace(/\.|\-/g,"/"))}var p,m,h="validator",v="."+h,y=".rule",_=".field",b=".form",k="nice-"+h,w="n-ok",M="n-error",O="n-tip",$="n-loading",x="msg-box",C="aria-required",F="aria-invalid",V="data-rule",A="data-msg",R="data-tip",T="data-ok",S="data-target",E="data-inputstatus",q="novalidate",N=":verifiable",j=/(!?)\s?(\w+)(?:\[\s*(.*?\]?)\s*\]|\(\s*(.*?\)?)\s*\))?\s*(;|\||&)?/g,D=/(\w+)(?:\[\s*(.*?\]?)\s*\]|\(\s*(.*?\)?)\s*\))?/,I=/(?:([^:;\(\[]*):)?(.*)/,U=/[^\x00-\xff]/g,H=/^.*(top|right|bottom|left).*$/,L=/(?:(post|get):)?(.+)/i,P=/<|>/g,W=e.noop,B=e.proxy,X=e.isFunction,J=e.isArray,Q=function(e){return"string"==typeof e},z=function(e){return e&&"[object Object]"===Object.prototype.toString.call(e)},G=!window.XMLHttpRequest,K=function(e,i,n){return n===t?e.getAttribute(i):(null===n?e.removeAttribute(i):e.setAttribute(i,""+n),t)},Y=window.console||{log:W,info:W},Z={debug:0,timely:1,theme:"default",ignore:"",focusInvalid:!0,beforeSubmit:W,validClass:"n-valid",invalidClass:"n-invalid",msgWrapper:"span",msgMaker:function(e){var t,i={error:M,ok:w,tip:O,loading:$}[e.type];return t='',t+=e.arrow+e.icon+''+e.msg+"",t+=""},msgIcon:'',msgArrow:"",msgClass:"",defaultMsg:"{0} is not valid.",loadingMsg:"Validating..."},et={"default":{formClass:"n-default",msgClass:"n-right",showOk:""}};e.fn[h]=function(t){var n=this,s=arguments;return n.is(":input")?n:(!n.is("form")&&(n=this.find("form")),!n.length&&(n=this),n.each(function(){var n=e(this).data(h);if(n)if(Q(t)){if("_"===t.charAt(0))return;n[t].apply(n,Array.prototype.slice.call(s,1))}else t&&(n._reset(!0),n._init(this,t));else new i(this,t)}),this)},e.fn.isValid=function(e,t){var i,n,s=l(this[0]),r=X(e);return s?(s.checkOnly=!!t,n=s.options,i=s._multiValidate(this.is(":input")?this:this.find(N),function(t){t||!n.focusInvalid||s.checkOnly||s.$el.find(":input["+F+"]:first").focus(),r&&e.call(null,t),s.checkOnly=!1}),r?this:i):!0},e.expr[":"].verifiable=function(e){var t=e.nodeName.toLowerCase();return("input"===t&&!{submit:1,button:1,reset:1,image:1}[e.type]||"select"===t||"textarea"===t)&&e.disabled===!1},i.prototype={_init:function(i,r){var l,u,o,d=this;if(X(r)&&(r={valid:r}),r=r||{},o=K(i,"data-"+h+"-option"),o=o&&"{"===o.charAt(0)?Function("return "+o)():{},u=et[r.theme||o.theme||Z.theme],l=d.options=e.extend({},Z,u,o,d.options,r),d.rules=new n(l.rules,!0),d.messages=new s(l.messages,!0),d.elements=d.elements||{},d.deferred={},d.errors={},d.fields={},d._initFields(l.fields),J(l.groups)&&e.map(l.groups,function(i){return Q(i.fields)&&X(i.callback)?(i.$elems=d.$el.find(a(i.fields)),e.map(i.fields.split(" "),function(e){d.fields[e]=d.fields[e]||{},d.fields[e].group=i}),t):null}),d.msgOpt={type:"error",pos:c(l.msgClass),wrapper:l.msgWrapper,cls:l.msgClass,style:l.msgStyle,icon:l.msgIcon,arrow:l.msgArrow,show:l.msgShow,hide:l.msgHide},d.isAjaxSubmit=!1,l.valid||!e.trim(K(i,"action")))d.isAjaxSubmit=!0;else{var f=e[e._data?"_data":"data"](i,"events");f&&f.valid&&e.map(f.valid,function(e){return-1!==e.namespace.indexOf("form")?1:null}).length&&(d.isAjaxSubmit=!0)}d.$el.data(h)||(d.$el.data(h,d).addClass(k+" "+l.formClass).on("submit"+v+" validate"+v,B(d,"_submit")).on("reset"+v,B(d,"_reset")).on("showtip"+v,B(d,"_showTip")).on("focusin"+v+" click"+v+" showtip"+v,N,B(d,"_focusin")).on("focusout"+v+" validate"+v,N,B(d,"_focusout")),l.timely>=2&&d.$el.on("keyup"+v+" paste"+v,N,B(d,"_focusout")).on("click"+v,":radio,:checkbox",B(d,"_focusout")).on("change"+v,'select,input[type="file"]',B(d,"_focusout")),d._novalidate=K(i,q),K(i,q,q))},_initFields:function(t){var i=this;z(t)&&e.each(t,function(e,t){if(null===t){var n=i.elements[e];n&&i._resetElement(n,!0),delete i.fields[e]}else i.fields[e]=Q(t)?{rule:t}:t}),i.$el.find(N).each(function(){i._parse(this)})},_parse:function(e){var t,i=this,n=e.name,s=K(e,V);s&&K(e,V,null),(e.id&&"#"+e.id in i.fields||!e.name)&&(n="#"+e.id),n&&(t=i.fields[n]||{},t.key=n,t.old={},t.rule=t.rule||s||"",t.rule&&(t.rule.match(/match|checked/)&&(t.must=!0),-1!==t.rule.indexOf("required")&&(t.required=!0,K(e,C,!0)),("timely"in t&&!t.timely||!i.options.timely)&&K(e,"notimely",!0),Q(t.target)&&K(e,S,t.target),Q(t.tip)&&K(e,R,t.tip),i.fields[n]=i._parseRule(t)))},_parseRule:function(e){var i=I.exec(e.rule),n=this.options;if(i)return e._i=0,i[1]&&(e.display=i[1]),!e.display&&n.display&&(e.display=n.display),i[2]&&(e.rules=[],i[2].replace(j,function(){var i=arguments;i[3]=i[3]||i[4],e.rules.push({not:"!"===i[1],method:i[2],params:i[3]?i[3].split(", "):t,or:"|"===i[5]})})),e},_multiValidate:function(i,n){var s=this,r=s.options;return s.verifying=!0,s.isValid=!0,r.ignore&&(i=i.not(r.ignore)),i.each(function(e,i){var n=s.getField(i);return n&&(s._validate(i,n),!s.isValid&&r.stopOnError)?!1:t}),e.when.apply(null,e.map(s.deferred,function(e){return e})).done(function(){n.call(s,s.isValid),s.verifying=!1}),e.isEmptyObject(s.deferred)?s.isValid:t},_submit:function(t){var i=this,n=i.options,s=t.target,r="submit"===t.type;t.preventDefault(),m&&~(m=!1)||i.submiting||"validate"===t.type&&i.$el[0]!==s||n.beforeSubmit.call(i,s)===!1||(n.debug&&Y.log("\n"+t.type),i._reset(),i.submiting=!0,i._multiValidate(i.$el.find(N),function(t){var a,l=t||2===n.debug?"valid":"invalid";t||(n.focusInvalid&&i.$el.find(":input["+F+'="true"]:first').focus(),a=e.map(i.errors,function(e){return e})),i.submiting=!1,X(n[l])&&n[l].call(i,s,a),i.$el.trigger(l+b,[s,a]),t&&!i.isAjaxSubmit&&r&&(m=!0,p&&p.name&&i.$el.append(''),s.submit())}))},_reset:function(e){var t=this;t.errors={},e&&t.$el.find(N).each(function(e,i){t._resetElement(i)})},_resetElement:function(t,i){var n=this.options;e(t).removeClass(n.validClass+" "+n.invalidClass),this.hideMsg(t),i&&K(t,C,null)},_focusin:function(t){var i,n=this,s=n.options,r=t.target;n.verifying||("showtip"!==t.type&&"error"===K(r,E)&&s.focusCleanup&&(e(r).removeClass(s.invalidClass),n.hideMsg(r)),i=K(r,R),i&&n.showMsg(r,{type:"tip",msg:i}))},_focusout:function(t){var i,n,s=this,r=s.options,a=t.target,l=t.type,u={click:1,change:1,paste:1},o=0;if(!u[l]){if("validate"===l)n=!0;else{if(K(a,"notimely"))return;if(r.timely>=2&&"keyup"!==l)return}if(r.ignore&&e(a).is(r.ignore))return;if("keyup"===l){var d=t.keyCode,c={8:1,9:1,16:1,32:1,46:1};if(9===d&&!a.value)return;if(48>d&&!c[d])return;o=r.timely>=100?r.timely:500}}i=s.getField(a),i&&(o?(i._t&&clearTimeout(i._t),i._t=setTimeout(function(){s._validate(a,i,n)},o)):s._validate(a,i,n))},_showTip:function(e){var t=this;t.$el[0]===e.target&&t.$el.find(N+"["+R+"]").each(function(){t.showMsg(this,{msg:K(this,R),type:"tip"})})},_validatedField:function(t,i,n){var s=this,r=s.options,a=n.isValid=i.isValid=!!n.isValid,l=a?"valid":"invalid";n.key=i.key,n.rule=i._r,a?n.type="ok":(s.submiting&&(s.errors[i.key]=n.msg),s.isValid=!1),i.old.value=t.value,i.old.id=t.id,s.elements[i.key]=n.element=t,s.$el[0].isValid=a?s.isFormValid():a,X(i[l])&&i[l].call(s,t,n),e(t).attr(F,a?null:!0).removeClass(a?r.invalidClass:r.validClass).addClass(n.skip?"":a?r.validClass:r.invalidClass).trigger(l+_,[n,s]),s.$el.triggerHandler("validation",[n,s]),s.checkOnly||(i.msgMaker||r.msgMaker)&&s[n.showOk||n.msg?"showMsg":"hideMsg"](t,n,i)},_validatedRule:function(i,n,s,r){n=n||o.getField(i),r=r||{};var a,l,u,o=this,c=o.options,f=n._r,g=!1;if(null===s)return o._validatedField(i,n,{isValid:!0,skip:!0}),t;if(s===!0||s===t||""===s?g=!0:Q(s)?a=s:z(s)&&(s.error?a=s.error:(a=s.ok,g=!0)),n.rules&&(l=n.rules[n._i],l.not&&(a=t,g="required"===f||!g),l.or))if(g)for(;n._i "+(g||r.msg||g)),u||g&&n._i=+l&&+u>=e)return!0;d=d.concat(a)}else if(l&&!u){if(c&&e>=+l)return!0;d.push(l),o="gte"}else if(!l&&u){if(c&&+u>=e)return!0;d.push(u),o="lte"}}else{if(e===+l)return!0;d.push(l),o="eq"}return r&&(n&&r[o+n]&&(o+=n),d[0]=r[o]),s.renderMsg.apply(null,d)}},renderMsg:function(){var e=arguments,t=e[0],i=e.length;if(t){for(;--i;)t=t.replace("{"+i+"}",e[i]);return t}},_getDisplay:function(e,t){return Q(t)?t:X(t)?t.call(this,e):""},_getMsgOpt:function(t){return e.extend({},this.msgOpt,Q(t)?{msg:t}:t)},_getMsgDOM:function(t,i){var n,s,r,a=e(t);if(a.is(":input")?(r=i.target||K(t,S),r&&(r=X(r)?r.call(this,t):this.$el.find(r),r.length&&(r.is(":input")?t=r.get(0):n=r)),n||(s=!f(t)&&t.id?t.id:t.name,n=this.$el.find(i.wrapper+"."+x+'[for="'+s+'"]'))):n=a,!n.length)if(a=this.$el.find(r||t),n=e("<"+i.wrapper+">").attr({"class":x+(i.cls?" "+i.cls:""),style:i.style||"","for":s}),f(t)){var l=a.parent();n.appendTo(l.is("label")?l.parent():l)}else n[i.pos&&"right"!==i.pos?"insertBefore":"insertAfter"](a);return n},showMsg:function(t,i,n){var s,r=this,a=r.options;if(i=r._getMsgOpt(i),(i.msg||i.showOk)&&(t=e(t).get(0),e(t).is(N)&&(K(t,E,i.type),n=n||r.getField(t),n&&(i.style=n.msgStyle||i.style,i.cls=n.msgClass||i.cls,i.wrapper=n.msgWrapper||i.wrapper,i.target=n.target||a.target)),s=(n||{}).msgMaker||a.msgMaker)){var l=r._getMsgDOM(t,i),u=l[0].className;!H.test(u)&&l.addClass(i.cls),G&&"bottom"===i.pos&&(l[0].style.marginTop=e(t).outerHeight()+"px"),l.html(s.call(r,i))[0].style.display="",X(i.show)&&i.show.call(r,l,i.type)}},hideMsg:function(t,i,n){var s=this,r=s.options;t=e(t).get(0),i=s._getMsgOpt(i),e(t).is(N)&&(K(t,E,null),K(t,F,null),n=n||s.getField(t),n&&(i.wrapper=n.msgWrapper||i.wrapper,i.target=n.target||r.target));var a=s._getMsgDOM(t,i);a.length&&(X(i.hide)?i.hide.call(s,a,i.type):a[0].style.display="none")},mapMsg:function(t){var i=this;e.each(t,function(e,t){var n=i.elements[e]||i.$el.find(':input[name="'+e+'"]')[0];i.showMsg(n,t)})},setMsg:function(e){new s(e,this.messages)},setRule:function(t){new n(t,this.rules),e.map(this.fields,function(e){e.old={}})},getField:function(e){var t,i=this;return t=e.id&&"#"+e.id in i.fields||!e.name?"#"+e.id:e.name,K(e,V)&&i._parse(e),i.fields[t]},setField:function(e,t){var i={};Q(e)?i[e]=t:z(e)&&(i=e),this._initFields(i)},isFormValid:function(){var e=this.fields;for(var t in e)if(!e[t].isValid)return e[t].isValid;return!0},holdSubmit:function(e){this.submiting=e===t||e},cleanUp:function(){this._reset(1)},destroy:function(){this._reset(1),this.$el.off(v).removeData(h),K(this.$el[0],q,this._novalidate)}},e(document).on("focusin",":input["+V+"]",function(e){u(e)}).on("click","input,button",function(e){var t=this,i=t.name;if(t.form)if("submit"===t.type)p=t,null!==K(t,q)&&(m=!0);else if(i&&f(t)){var n=t.form.elements[i];n.length&&(n=n[0]),K(n,V)&&u(e)}}).on("submit validate","form",function(t){if(null===K(this,q)){var i,n=e(this);n.data(h)||(i=n[h]().data(h),e.isEmptyObject(i.fields)?(K(this,q,q),n.off(v).removeData(h)):i._submit(t))}}),new n({required:function(t,i){var n=e.trim(t.value),s=!0;if(i)if(1===i.length){if(!n&&!this.test(t,i[0]))return K(t,C,null),null;K(t,C,!0)}else"not"===i[0]&&e.map(i.slice(1),function(t){n===e.trim(t)&&(s=!1)});return s&&!!n},integer:function(e,t){var i,n="0|",s="[1-9]\\d*",r=t?t[0]:"*";switch(r){case"+":i=s;break;case"-":i="-"+s;break;case"+0":i=n+s;break;case"-0":i=n+"-"+s;break;default:i=n+"-?"+s}return i="^(?:"+i+")$",RegExp(i).test(e.value)||this.messages.integer[r]},match:function(t,i,n){if(i){var s,r,a,l,u,o,d,c=this,f="eq";if(1===i.length?a=i[0]:(f=i[0],a=i[1]),u="#"===a.charAt(0)?a:':input[name="'+a+'"]',o=c.$el.find(u)[0]){if(d=c.getField(o),s=t.value,r=o.value,n._match||(c.$el.on("valid"+_+v,u,function(){e(t).trigger("validate")}),n._match=d._match=1),!n.required&&""===s&&""===r)return null;if(i[2]&&("date"===i[2]?(s=g(s),r=g(r)):"time"===i[2]&&(s=+s.replace(":",""),r=+r.replace(":",""))),"eq"!==f&&!isNaN(+s)&&isNaN(+r))return!0;switch(l=c.messages.match[f].replace("{1}",c._getDisplay(t,d.display||a)),f){case"lt":return+r>+s||l;case"lte":return+r>=+s||l;case"gte":return+s>=+r||l;case"gt":return+s>+r||l;case"neq":return s!==r||l;default:return s===r||l}}}},range:function(e,t){return this.getRangeMsg(+e.value,t,"range")},checked:function(t,i,n){if(f(t)){var s,r,a=this;return r=a.$el.find('input[name="'+t.name+'"]').filter(function(){var t=this;return!s&&f(t)&&(s=t),!t.disabled&&t.checked&&e(t).is(":visible")}).length,i?a.getRangeMsg(r,i,"checked"):!!r||d(s,n,"")||a.messages.required}},length:function(e,t){var i=e.value,n=(t[1]?i.replace(U,"xx"):i).length;return this.getRangeMsg(n,t,"length",t[1]?"_2":"")},remote:function(t,i){if(i){var n,s=this,r=L.exec(i[0]),a=r[2],l=(r[1]||"POST").toUpperCase(),u={};return u[t.name]=t.value,i[1]&&e.map(i.slice(1),function(t){var i,n=t.split(":");t=e.trim(n[0]),i=e.trim(n[1]||"")||t,u[t]=s.$el.find("#"===i.charAt(0)?i:':input[name="'+i+'"]').val()}),u=e.param(u),"POST"===l&&(n=a.indexOf("?"),-1!==n&&(u+="&"+a.substring(n+1,a.length),a=a.substring(0,n))),e.ajax({url:a,type:l,data:u,cache:!1})}},filter:function(e,t){e.value=e.value.replace(t?RegExp("["+t[0]+"]","gm"):P,"")}}),i.config=function(t){e.each(t,function(e,t){"rules"===e?new n(t):"messages"===e?new s(t):Z[e]=t})},i.setTheme=function(t,i){z(t)?e.each(t,function(e,t){et[e]=t}):Q(t)&&z(i)&&(et[t]=i)},e[h]=i}(jQuery); diff --git a/static/js/validator/zh_CN.js b/static/js/validator/zh_CN.js new file mode 100755 index 000000000..ba320330d --- /dev/null +++ b/static/js/validator/zh_CN.js @@ -0,0 +1,156 @@ +/********************************* + * Themes, rules, and i18n support + * Locale: Chinese; 中文 + *********************************/ +(function(factory) { + if (typeof define === 'function') { + define(function(require, exports, module){ + var $ = require('jquery'); + $._VALIDATOR_URI = module.uri; + require('../src/jquery.validator')($); + factory($); + }); + } else { + factory(jQuery); + } +}(function($) { + /* Global configuration + */ + $.validator.config({ + //stopOnError: false, + //theme: 'yellow_right', + defaultMsg: "{0}格式不正确", + loadingMsg: "正在验证...", + + // Custom rules + rules: { + digits: [/^\d+$/, "请输入数字"] + ,letters: [/^[a-z]+$/i, "{0}只能输入字母"] + ,tel: [/^(?:(?:0\d{2,3}[\- ]?[1-9]\d{6,7})|(?:[48]00[\- ]?[1-9]\d{6}))$/, "电话格式不正确"] + ,mobile: [/^1[3-9]\d{9}$/, "手机号格式不正确"] + ,email: [/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/i, "邮箱格式不正确"] + ,qq: [/^[1-9]\d{4,}$/, "QQ号格式不正确"] + ,date: [/^\d{4}-\d{1,2}-\d{1,2}$/, "请输入正确的日期,例:yyyy-mm-dd"] + ,time: [/^([01]\d|2[0-3])(:[0-5]\d){1,2}$/, "请输入正确的时间,例:14:30或14:30:00"] + ,ID_card: [/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[A-Z])$/, "请输入正确的身份证号码"] + ,url: [/^(https?|ftp):\/\/[^\s]+$/i, "网址格式不正确"] + ,postcode: [/^[1-9]\d{5}$/, "邮政编码格式不正确"] + ,chinese: [/^[\u0391-\uFFE5]+$/, "请输入中文"] + ,username: [/^\w{3,12}$/, "请输入3-12位数字、字母、下划线"] + ,password: [/^[0-9a-zA-Z]{6,16}$/, "密码由6-16位数字、字母组成"] + ,accept: function (element, params){ + if (!params) return true; + var ext = params[0]; + return (ext === '*') || + (new RegExp(".(?:" + (ext || "png|jpg|jpeg|gif") + ")$", "i")).test(element.value) || + this.renderMsg("只接受{1}后缀", ext.replace('|', ',')); + } + + } + }); + + /* Default error messages + */ + $.validator.config({ + messages: { + required: "{0}不能为空", + remote: "{0}已被使用", + integer: { + '*': "请输入整数", + '+': "请输入正整数", + '+0': "请输入正整数或0", + '-': "请输入负整数", + '-0': "请输入负整数或0" + }, + match: { + eq: "{0}与{1}不一致", + neq: "{0}与{1}不能相同", + lt: "{0}必须小于{1}", + gt: "{0}必须大于{1}", + lte: "{0}必须小于或等于{1}", + gte: "{0}必须大于或等于{1}" + }, + range: { + rg: "请输入{1}到{2}的数", + gte: "请输入大于或等于{1}的数", + lte: "请输入小于或等于{1}的数" + }, + checked: { + eq: "请选择{1}项", + rg: "请选择{1}到{2}项", + gte: "请至少选择{1}项", + lte: "请最多选择{1}项" + }, + length: { + eq: "请输入{1}个字符", + rg: "请输入{1}到{2}个字符", + gte: "请至少输入{1}个字符", + lte: "请最多输入{1}个字符", + eq_2: "", + rg_2: "", + gte_2: "", + lte_2: "" + } + } + }); + + /* Themes + */ + var TPL_ARROW = ''; + $.validator.setTheme({ + 'simple_right': { + formClass: 'n-simple', + msgClass: 'n-right' + }, + 'simple_bottom': { + formClass: 'n-simple', + msgClass: 'n-bottom' + }, + 'yellow_top': { + formClass: 'n-yellow', + msgClass: 'n-top', + msgArrow: TPL_ARROW + }, + 'yellow_right': { + formClass: 'n-yellow', + msgClass: 'n-right', + msgArrow: TPL_ARROW + }, + 'yellow_right_effect': { + formClass: 'n-yellow', + msgClass: 'n-right', + msgArrow: TPL_ARROW, + msgShow: function($msgbox, type){ + var $el = $msgbox.children(); + if ($el.is(':animated')) return; + if (type === 'error') { + $el.css({ + left: '20px', + opacity: 0 + }).delay(100).show().stop().animate({ + left: '-4px', + opacity: 1 + }, 150).animate({ + left: '3px' + }, 80).animate({ + left: 0 + }, 80); + } else { + $el.css({ + left: 0, + opacity: 1 + }).fadeIn(200); + } + }, + msgHide: function($msgbox, type){ + var $el = $msgbox.children(); + $el.stop().delay(100).show().animate({ + left: '20px', + opacity: 0 + }, 300, function(){ + $msgbox.hide(); + }); + } + } + }); +})); \ No newline at end of file diff --git a/templates/jasset/jadd.html b/templates/jasset/jadd.html index fe5ecd0de..1ba125809 100644 --- a/templates/jasset/jadd.html +++ b/templates/jasset/jadd.html @@ -31,17 +31,14 @@ -
- addidc -
{% if emg %}
{{ emg }}
{% endif %} -
+
-
+
@@ -131,17 +128,6 @@
- - - - - - - - - - - {% endblock %} \ No newline at end of file diff --git a/templates/juser/group_add.html b/templates/juser/group_add.html index 11bd489f0..da5481b35 100644 --- a/templates/juser/group_add.html +++ b/templates/juser/group_add.html @@ -35,16 +35,16 @@
{{ msg }}
{% endif %}
- +
- +
- +
- +
diff --git a/templates/juser/user_add.html b/templates/juser/user_add.html index 65d6ae890..2524cd3eb 100644 --- a/templates/juser/user_add.html +++ b/templates/juser/user_add.html @@ -1,4 +1,5 @@ {% extends 'base.html' %} +{% load mytags %} {% block content %} {% include 'nav_cat_bar.html' %} @@ -27,18 +28,24 @@
- + + {% if error %} +
{{ error }}
+ {% endif %} + {% if msg %} +
{{ msg }}
+ {% endif %}
- +
- +
- +
- + 登陆web的密码 @@ -46,62 +53,84 @@
- +
- + + + 登陆 Jumpserver 使用的SSH密钥的密码 +
- +
- +
- +
- + {% for group in all_group %} + {% if groups_str %} + {% if group.id|int2str in groups_str %} + + {% else %} + + {% endif %} + {% else %} + {% if forloop.first %} + + {% else %} + + {% endif %} {% endif %} - {% endfor %}
- +
- + {% for r, role_name in user_role.items %} + {% ifequal r role_post %} + + {% else %} + + {% endifequal %} {% endfor %}
- +
- + 如果使用password方式,该密码是用户在后端服务器的密码
+
+ +
+ +
+
+
-
- +
- +
diff --git a/templates/link_css.html b/templates/link_css.html index 2d795dece..057f788be 100644 --- a/templates/link_css.html +++ b/templates/link_css.html @@ -3,4 +3,5 @@ - \ No newline at end of file + + \ No newline at end of file diff --git a/templates/script.html b/templates/script.html index 7d22a71b4..f730bb65e 100644 --- a/templates/script.html +++ b/templates/script.html @@ -29,3 +29,7 @@ + + + +