diff --git a/connect.py b/connect.py index 89dc43b16..e1d4829b8 100755 --- a/connect.py +++ b/connect.py @@ -239,24 +239,6 @@ class Tty(object): today_connect_log_dir = os.path.join(tty_log_dir, date_start) log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.asset_name, time_start)) -<<<<<<< HEAD -def log_record(username, host): - """Logging user command and output.""" - connect_log_dir = os.path.join(LOG_DIR, 'connect') - timestamp_start = int(time.time()) - today = time.strftime('%Y%m%d', time.localtime(timestamp_start)) - time_now = time.strftime('%H%M%S', time.localtime(timestamp_start)) - today_connect_log_dir = os.path.join(connect_log_dir, today) - log_filename = '%s_%s_%s.log' % (username, host, time_now) - log_file_path = os.path.join(today_connect_log_dir, log_filename) - dept_name = User.objects.get(username=username).dept.name - pid = os.getpid() - pts = os.popen("ps axu | grep %s | grep -v grep | awk '{ print $7 }'" % pid).read().strip() - ip_list = os.popen("who | grep %s | awk '{ print $5 }'" % pts).read().strip('()\n') - - if not os.path.isdir(today_connect_log_dir): -======= ->>>>>>> dev try: mkdir(os.path.dirname(today_connect_log_dir), mode=0777) mkdir(today_connect_log_dir, mode=0777) diff --git a/jlog/views.py b/jlog/views.py index b8cc089d5..ff0eaf1c0 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -84,13 +84,6 @@ def log_kill(request): log = Log.objects.filter(pid=pid) if log: log = log[0] -<<<<<<< HEAD - dept_name = log.dept_name - deptname = get_session_user_info(request)[4] - if is_group_admin(request) and dept_name != deptname: - return httperror(request, u'Kill失败, 您无权操作!') -======= ->>>>>>> dev try: os.kill(int(pid), 9) except OSError: diff --git a/jperm/views.py b/jperm/views.py index e42faec06..cb9a7d46e 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -182,107 +182,6 @@ def perm_rule_edit(request): asset_groups_select = request.POST.getlist('asset_group', []) roles_select = request.POST.getlist('role', []) -<<<<<<< HEAD -@require_super_user -def perm_edit(request): - if request.method == 'GET': - header_title, path1, path2 = u'编辑授权', u'授权管理', u'授权编辑' - user_group_id = request.GET.get('id', '') - user_group = UserGroup.objects.filter(id=user_group_id) - if user_group: - user_group = user_group[0] - asset_groups_all = BisGroup.objects.all() - asset_groups_select = [perm.asset_group for perm in user_group.perm_set.all()] - asset_groups = [asset_group for asset_group in asset_groups_all if asset_group not in asset_groups_select] - else: - user_group_id = request.POST.get('user_group_id') - asset_group_id_list = request.POST.getlist('asset_groups_select') - perm_group_update(user_group_id, asset_group_id_list) - - return HttpResponseRedirect('/jperm/perm_list/') - return render_to_response('jperm/perm_edit.html', locals(), context_instance=RequestContext(request)) - - -@require_admin -def perm_edit_adm(request): - if request.method == 'GET': - header_title, path1, path2 = u'编辑授权', u'授权管理', u'授权编辑' - user_group_id = request.GET.get('id', '') - user_group = UserGroup.objects.filter(id=user_group_id) - user, dept = get_session_user_dept(request) - if user_group: - user_group = user_group[0] - asset_groups_all = dept.bisgroup_set.all() - asset_groups_select = [perm.asset_group for perm in user_group.perm_set.all()] - asset_groups = [asset_group for asset_group in asset_groups_all if asset_group not in asset_groups_select] - else: - user_group_id = request.POST.get('user_group_id') - asset_group_id_list = request.POST.getlist('asset_groups_select') - print user_group_id, asset_group_id_list - if not validate(request, user_group=[user_group_id], asset_group=asset_group_id_list): - return HttpResponseRedirect('/') - perm_group_update(user_group_id, asset_group_id_list) - - return HttpResponseRedirect('/jperm/perm_list/') - return render_to_response('jperm/perm_edit.html', locals(), context_instance=RequestContext(request)) - - -@require_admin -def perm_detail(request): - header_title, path1, path2 = u'授权管理', u'小组管理', u'授权详情' - group_id = request.GET.get('id') - user_group = UserGroup.objects.filter(id=group_id) - if user_group: - user_group = user_group[0] - users = user_group.user_set.all() - group_user_num = len(users) - perms = user_group.perm_set.all() - asset_groups = [perm.asset_group for perm in perms] - return render_to_response('jperm/perm_detail.html', locals(), context_instance=RequestContext(request)) - - -@require_admin -def perm_del(request): - perm_id = request.GET.get('id') - perm = Perm.objects.filter(id=perm_id) - if perm: - perm = perm[0] - perm.delete() - return HttpResponseRedirect('/jperm/perm_list/') - - -@require_admin -def perm_asset_detail(request): - header_title, path1, path2 = u'用户授权主机', u'权限管理', u'用户主机详情' - user_id = request.GET.get('id') - user = User.objects.filter(id=user_id) - if user: - user = user[0] - assets_list = user_perm_asset_api(user.username) - return render_to_response('jperm/perm_asset_detail.html', locals(), context_instance=RequestContext(request)) - - -def unicode2str(unicode_list): - return [str(i) for i in unicode_list] - - -def sudo_ldap_add(user_group, user_runas, asset_groups_select, - cmd_groups_select): - if LDAP_ENABLE: - ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) - else: - return - - assets = [] - cmds = [] - user_runas = user_runas.split(',') - if len(asset_groups_select) == 1 and asset_groups_select[0].name == 'ALL': - asset_all = True - else: - asset_all = False - for asset_group in asset_groups_select: - assets.extend(asset_group.asset_set.all()) -======= try: if not rule_name or not roles_select: raise ServerError(u'系统用户和关联系统用户不能为空') @@ -318,7 +217,6 @@ def sudo_ldap_add(user_group, user_runas, asset_groups_select, rule.comment = rule_comment rule.save() msg = u"更新授权规则:%s成功" % rule.name ->>>>>>> dev except ServerError, e: error = e @@ -389,66 +287,6 @@ def perm_role_add(request): raise ServerError('已经存在该用户 %s' % name) default = get_object(Setting, name='default') -<<<<<<< HEAD - -@require_admin -def cmd_add_adm(request): - header_title, path1, path2 = u'sudo命令添加', u'授权管理', u'命令组添加' - user, dept = get_session_user_dept(request) - - if request.method == 'POST': - name = request.POST.get('name') - cmd = ','.join(request.POST.get('cmd').split('\n')) - comment = request.POST.get('comment') - - 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)) - - -@require_admin -def cmd_edit(request): - header_title, path1, path2 = u'sudo命令修改', u'授权管理管理', u'命令组修改' - - 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 - - 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('\n')) - comment = request.POST.get('comment') - cmd_group = CmdGroup.objects.filter(id=cmd_group_id) - - dept = DEPT.objects.filter(id=dept_id) - try: - if not dept: - error = '没有该部门' - raise ServerError(error) - - if not cmd_group: - error = '没有该命令组' -======= if password: encrypt_pass = CRYPTOR.encrypt(password) else: @@ -465,7 +303,6 @@ def cmd_edit(request): role.sudo = sudos_obj msg = u"添加系统用户: %s" % name return HttpResponseRedirect(reverse('role_list')) ->>>>>>> dev except ServerError, e: error = e diff --git a/jumpserver.conf b/jumpserver.conf index fe8099605..e5f15d738 100644 --- a/jumpserver.conf +++ b/jumpserver.conf @@ -17,13 +17,7 @@ web_socket_host = 192.168.244.129:3000 mail_enable = 1 email_host = smtp.qq.com email_port = 25 -<<<<<<< HEAD -email_host_user = 1152704203@qq.com -email_host_password = Hudie117... -email_use_tls = False -======= email_host_user = ibuler@qq.com email_host_password = Hudie117...qq email_use_tls = True ->>>>>>> dev diff --git a/jumpserver/api.py b/jumpserver/api.py index 747cbce00..646aeeeb2 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -25,91 +25,6 @@ from jumpserver.models import Setting from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.core.mail import send_mail -<<<<<<< HEAD -import json - - -BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) -CONF = ConfigParser() -CONF.read(os.path.join(BASE_DIR, 'jumpserver.conf')) -LOG_DIR = os.path.join(BASE_DIR, 'logs') -SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys') -SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') -KEY = CONF.get('base', 'key') -LOGIN_NAME = getpass.getuser() -LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') -SEND_IP = CONF.get('base', 'ip') -SEND_PORT = CONF.get('base', 'port') -MAIL_FROM = CONF.get('mail', 'email_host_user') - - -class LDAPMgmt(): - def __init__(self, - host_url, - base_dn, - root_cn, - root_pw): - self.ldap_host = host_url - self.ldap_base_dn = base_dn - self.conn = ldap.initialize(host_url) - self.conn.set_option(ldap.OPT_REFERRALS, 0) - self.conn.protocol_version = ldap.VERSION3 - self.conn.simple_bind_s(root_cn, root_pw) - - def list(self, filter, scope=ldap.SCOPE_SUBTREE, attr=None): - 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 decrypt(self, text): - cryptor = AES.new(self.key, self.mode, b'0000000000000000') - try: - plain_text = cryptor.decrypt(a2b_hex(text)) - except TypeError: - raise ServerError('Decrypt password error, TYpe error.') - return plain_text.rstrip('\0') - - -if LDAP_ENABLE: - LDAP_HOST_URL = CONF.get('ldap', 'host_url') - 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() -======= from django.core.urlresolvers import reverse @@ -194,7 +109,6 @@ def chown(path, user, group=''): os.chown(path, uid, gid) except KeyError: pass ->>>>>>> dev def page_list_return(total, current=1): diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index ea27b12e1..a5d70d801 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -273,23 +273,11 @@ def get_push_info(push_id, arg): else: return [] -<<<<<<< HEAD -@register.filter(name='sudo_cmd_count') -def sudo_cmd_count(cmd_group_id): - cmd_group = CmdGroup.objects.filter(id=cmd_group_id) - cmds = [] - if cmd_group: - cmd_group = cmd_group[0] - return len(set(cmd_group.cmd.split(','))) - else: - return 0 -======= @register.filter(name='get_cpu_core') def get_cpu_core(cpu_info): cpu_core = cpu_info.split('* ')[1] if cpu_info and '*' in cpu_info else cpu_info return cpu_core ->>>>>>> dev @register.filter(name='get_disk_info') diff --git a/juser/views.py b/juser/views.py index d3059a460..ed2c11f2a 100644 --- a/juser/views.py +++ b/juser/views.py @@ -14,330 +14,7 @@ from jperm.perm_api import get_group_user_perm MAIL_FROM = EMAIL_HOST_USER -<<<<<<< HEAD -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 - - -class AddError(Exception): - pass - - -def gen_sha512(salt, password): - return crypt.crypt(password, '$6$%s$' % salt) - - -def group_add_user(group, user_id=None, username=None): - try: - if user_id: - user = User.objects.get(id=user_id) - else: - user = User.objects.get(username=username) - except ObjectDoesNotExist: - raise AddError('用户获取失败') - else: - group.user_set.add(user) - - -def db_add_group(**kwargs): - name = kwargs.get('name') - group = UserGroup.objects.filter(name=name) - users = kwargs.pop('users') - if group: - raise AddError(u'用户组 %s 已经存在' % name) - group = UserGroup(**kwargs) - group.save() - for user_id in users: - group_add_user(group, user_id) - - -def db_add_user(**kwargs): - groups_post = kwargs.pop('groups') - user = User(**kwargs) - user.save() - if groups_post: - group_select = [] - for group_id in groups_post: - group = UserGroup.objects.filter(id=group_id) - group_select.extend(group) - user.group = group_select - return user - - -def db_update_user(**kwargs): - groups_post = kwargs.pop('groups') - user_id = kwargs.pop('user_id') - user = User.objects.filter(id=user_id) - if user: - user.update(**kwargs) - user = User.objects.get(id=user_id) - user.save() - - if groups_post: - group_select = [] - for group_id in groups_post: - group = UserGroup.objects.filter(id=group_id) - group_select.extend(group) - user.group = group_select - - -def db_del_user(username): - try: - user = User.objects.get(username=username) - user.delete() - except ObjectDoesNotExist: - pass - - -def gen_ssh_key(username, password=None, length=2048): - private_key_dir = os.path.join(BASE_DIR, 'keys/jumpserver/') - private_key_file = os.path.join(private_key_dir, username+".pem") - public_key_dir = '/home/%s/.ssh/' % username - public_key_file = os.path.join(public_key_dir, 'authorized_keys') - is_dir(private_key_dir) - is_dir(public_key_dir, username, mode=0700) - - key = RSA.generate(length) - with open(private_key_file, 'w') as pri_f: - pri_f.write(key.exportKey('PEM', password)) - os.chmod(private_key_file, 0600) - - pub_key = key.publickey() - with open(public_key_file, 'w') as pub_f: - pub_f.write(pub_key.exportKey('OpenSSH')) - os.chmod(public_key_file, 0600) - bash('chown %s:%s %s' % (username, username, public_key_file)) - - -def server_add_user(username, password, ssh_key_pwd): - bash("useradd '%s'; echo '%s' | passwd --stdin '%s'" % (username, password, username)) - gen_ssh_key(username, ssh_key_pwd) - - -def server_del_user(username): - bash('userdel -r %s' % username) - - -def ldap_add_user(username, ldap_pwd): - if LDAP_ENABLE: - ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) - else: - return - user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) - password_sha512 = gen_sha512(gen_rand_pwd(6), ldap_pwd) - user = User.objects.filter(username=username) - if user: - user = user[0] - else: - raise AddError(u'用户 %s 不存在' % username) - - user_attr = {'uid': [str(username)], - 'cn': [str(username)], - 'objectClass': ['account', 'posixAccount', 'top', 'shadowAccount'], - 'userPassword': ['{crypt}%s' % password_sha512], - 'shadowLastChange': ['16328'], - 'shadowMin': ['0'], - 'shadowMax': ['99999'], - 'shadowWarning': ['7'], - 'loginShell': ['/bin/bash'], - 'uidNumber': [str(user.id)], - 'gidNumber': [str(user.id)], - 'homeDirectory': [str('/home/%s' % username)]} - - group_dn = "cn=%s,ou=Group,%s" % (username, LDAP_BASE_DN) - group_attr = {'objectClass': ['posixGroup', 'top'], - 'cn': [str(username)], - 'userPassword': ['{crypt}x'], - 'gidNumber': [str(user.id)]} - - ldap_conn.add(user_dn, user_attr) - ldap_conn.add(group_dn, group_attr) - - -def ldap_del_user(username): - if LDAP_ENABLE: - ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) - else: - return - user_dn = "uid=%s,ou=People,%s" % (username, LDAP_BASE_DN) - group_dn = "cn=%s,ou=Group,%s" % (username, LDAP_BASE_DN) - sudo_dn = 'cn=%s,ou=Sudoers,%s' % (username, LDAP_BASE_DN) - - ldap_conn.delete(user_dn) - ldap_conn.delete(group_dn) - ldap_conn.delete(sudo_dn) - - -@require_super_user -def dept_add(request): - header_title, path1, path2 = '添加部门', '用户管理', '添加部门' - if request.method == 'POST': - name = request.POST.get('name', '') - comment = request.POST.get('comment', '') - - try: - if not name: - raise AddError('部门名称不能为空') - if DEPT.objects.filter(name=name): - raise AddError(u'部门名称 %s 已存在' % name) - except AddError, e: - error = e - else: - DEPT(name=name, comment=comment).save() - msg = u'添加部门 %s 成功' % name - - return render_to_response('juser/dept_add.html', locals(), context_instance=RequestContext(request)) - - -@require_super_user -def dept_list(request): - header_title, path1, path2 = '查看部门', '用户管理', '查看部门' - keyword = request.GET.get('search') - if keyword: - contact_list = DEPT.objects.filter(Q(name__icontains=keyword) | Q(comment__icontains=keyword)).order_by('name') - else: - contact_list = DEPT.objects.all().order_by('id') - - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(contact_list, request) - - return render_to_response('juser/dept_list.html', locals(), context_instance=RequestContext(request)) - - -@require_admin -def dept_list_adm(request): - header_title, path1, path2 = '查看部门', '用户管理', '查看部门' - user, dept = get_session_user_dept(request) - contact_list = [dept] - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(contact_list, request) - - return render_to_response('juser/dept_list.html', locals(), context_instance=RequestContext(request)) - - -def chg_role(request): - role = {'SU': 2, 'DA': 1, 'CU': 0} - user, dept = get_session_user_dept(request) - if request.session['role_id'] > 0: - request.session['role_id'] = 0 - elif request.session['role_id'] == 0: - request.session['role_id'] = role.get(user.role, 0) - return HttpResponseRedirect('/') - - -@require_super_user -def dept_detail(request): - dept_id = request.GET.get('id', None) - if not dept_id: - return HttpResponseRedirect('/juser/dept_list/') - dept = DEPT.objects.filter(id=dept_id) - if dept: - dept = dept[0] - users = dept.user_set.all() - return render_to_response('juser/dept_detail.html', locals(), context_instance=RequestContext(request)) - - -@require_super_user -def dept_del(request): - dept_id = request.GET.get('id', None) - if not dept_id or dept_id in ['1', '2']: - return HttpResponseRedirect('/juser/dept_list/') - dept = DEPT.objects.filter(id=dept_id) - if dept: - dept = dept[0] - dept.delete() - return HttpResponseRedirect('/juser/dept_list/') - - -def dept_member(dept_id): - dept = DEPT.objects.filter(id=dept_id) - if dept: - dept = dept[0] - return dept.user_set.all() - - -def dept_member_update(dept, users_id_list): - old_users = dept.user_set.all() - new_users = [] - for user_id in users_id_list: - new_users.extend(User.objects.filter(id=user_id)) - - remove_user = [user for user in old_users if user not in new_users] - add_user = [user for user in new_users if user not in old_users] - - for user in add_user: - user.dept = dept - user.save() - - dept_default = DEPT.objects.get(id=2) - for user in remove_user: - user.dept = dept_default - user.save() - - -@require_super_user -def dept_del_ajax(request): - dept_ids = request.POST.get('dept_ids') - for dept_id in dept_ids.split(','): - if int(dept_id) > 2: - DEPT.objects.filter(id=dept_id).delete() - return HttpResponse("删除成功") - - -@require_super_user -def dept_edit(request): - header_title, path1, path2 = '部门编辑', '用户管理', '部门编辑' - if request.method == 'GET': - dept_id = request.GET.get('id', '') - if dept_id: - dept = DEPT.objects.filter(id=dept_id) - if dept: - dept = dept[0] - users = dept_member(dept_id) - users_all = User.objects.all() - users_other = [user for user in users_all if user not in users] - else: - error = 'id 错误' - else: - error = u'部门不存在' - else: - dept_id = request.POST.get('id', '') - name = request.POST.get('name', '') - users = request.POST.getlist('users_selected', []) - comment = request.POST.get('comment', '') - - dept = DEPT.objects.filter(id=dept_id) - if dept: - dept.update(name=name, comment=comment) - dept_member_update(dept[0], users) - else: - error = '部门不存在' - return HttpResponseRedirect('/juser/dept_list/') - return render_to_response('juser/dept_edit.html', locals(), context_instance=RequestContext(request)) - - -def dept_user_ajax(request): - dept_id = request.GET.get('id', '4') - if dept_id not in ['1', '2']: - dept = DEPT.objects.filter(id=dept_id) - if dept: - dept = dept[0] - users = dept.user_set.all() - else: - users = User.objects.all() - - return render_to_response('juser/dept_user_ajax.html', locals()) - - - -@require_super_user -======= @require_role(role='super') ->>>>>>> dev def group_add(request): """ group add view for route diff --git a/log_handler.py b/log_handler.py deleted file mode 100644 index 5e5e4f698..000000000 --- a/log_handler.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 - -import os -import re -import time -import psutil -from datetime import datetime - -os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' - -import django -#django.setup() -from jlog.models import Log - - -def log_hanler(id): - log = Log.objects.get(id=id) - pattern = re.compile(r'([\[.*@.*\][\$#].*)|(.*mysql>.*)') - if log: - filename = log.log_path - if os.path.isfile(filename): - f_his = filename + '.his' - f1 = open(filename) - f2 = open(f_his, 'a') - lines = f1.readlines() - for line in lines[7:]: - match = pattern.match(line) - if match: - newline = re.sub('\[[A-Z]', '', line) - f2.write(newline) - f1.close() - f2.close() - log.log_finished = True - log.save() - - -def set_finish(id): - log = Log.objects.filter(id=id) - if log: - log.update(is_finished=1, end_time=datetime.now()) - - -def kill_pid(pid): - try: - os.kill(pid, 9) - except OSError: - pass - - -def get_pids(): - pids1, pids2 = [], [] - pids1_obj = Log.objects.filter(is_finished=0) - pids2_obj = Log.objects.filter(is_finished=1, log_finished=0) - for pid_obj in pids1_obj: - pids1.append((pid_obj.id, pid_obj.pid, pid_obj.log_path, pid_obj.is_finished, pid_obj.log_finished, pid_obj.start_time)) - for pid_obj in pids2_obj: - pids2.append(pid_obj.id) - - return pids1, pids2 - - -def run(): - pids1, pids2 = get_pids() - for pid_id in pids2: - log_hanler(pid_id) - - for pid_id, pid, log_path, is_finished, log_finished, start_time in pids1: - try: - file_time = int(os.stat(log_path).st_ctime) - now_time = int(time.time()) - if now_time - file_time > 10800: - if psutil.pid_exists(pid): - kill_pid(pid) - set_finish(pid_id) - log_hanler(pid_id) - except OSError: - pass - -#function erorcheck is added by Lin to resolve CPU filled issue 2015.08.24 - -def errorcheck(): - f=os.popen("ps auxr | grep -v auxr|sed -n 2p") - a=f.read().strip() - f.close() - pid=0 - try: - if a is not None: - b=a.split(" ") - if b[-1]=='/opt/jumpserver/connect.py' and b[-2]=='python' and b[-8]=='R' and int(b[-3].split(':')[0])>100: - for i,j in enumerate(b): - if i==0 or j=='': - continue - else: - pid=int(j) - break - kill_pid(pid) - except : - pass - -if __name__ == '__main__': - while True: - run() - time.sleep(5) - errorcheck() diff --git a/static/js/layer/layer.min.js b/static/js/layer/layer.min.js deleted file mode 100644 index 988b1aa11..000000000 --- a/static/js/layer/layer.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/**************************************** - - @Name:layer v1.8.5 弹层组件压缩版 - @Author:贤心 - @Date:2014-08-13 - @Blog:http://sentsin.com - @Copyright:Sentsin Xu(贤心) - @官网:http://sentsin.com/jquery/layer - - */ - -;!function(a,b){ -"use strict"; -var c,d, -e="", //组件存放目录,为空表示自动获取 -f={getPath:function(){var a=document.scripts,b=a[a.length-1].src;return e?e:b.substring(0,b.lastIndexOf("/")+1)},type:["dialog","page","iframe","loading","tips"]};a.layer={v:"1.8.5",ie6:!!a.ActiveXObject&&!a.XMLHttpRequest,index:0,path:f.getPath(),use:function(a,b){var d=c("head")[0],a=a.replace(/\s/g,""),e=/\.css$/.test(a),f=document.createElement(e?"link":"script"),g=a.replace(/\.|\//g,"");e&&(f.type="text/css",f.rel="stylesheet"),f[e?"href":"src"]=/^http:\/\//.test(a)?a:layer.path+a,f.id=g,c("#"+g)[0]||d.appendChild(f),b&&(document.all?c(f).ready(b):c(f).load(b))},alert:function(a,b,d,e){var f="function"==typeof d,g={dialog:{msg:a,type:b,yes:f?d:e},area:["auto","auto"]};return f||(g.title=d),c.layer(g)},confirm:function(a,b,d,e){var f="function"==typeof d,g={dialog:{msg:a,type:4,btns:2,yes:b,no:f?d:e}};return f||(g.title=d),c.layer(g)},msg:function(a,d,e,f){var g={title:!1,closeBtn:!1,time:d===b?2:d,dialog:{msg:""===a||a===b?" ":a},end:f};return"object"==typeof e?(g.dialog.type=e.type,g.shade=e.shade,g.shift=e.rate):"function"==typeof e?g.end=e:g.dialog.type=e,c.layer(g)},load:function(a,b){return"string"==typeof a?layer.msg(a,b||0,16):c.layer({time:a,loading:{type:b},bgcolor:b?"#fff":"",shade:b?[.1,"#000"]:[0],border:3!==b&&b?[6,.3,"#000"]:[0],type:3,title:["",!1],closeBtn:[0,!1]})},tips:function(a,b,d,e,f,g){var h={type:4,shade:!1,success:function(a){this.closeBtn||a.find(".xubox_tips").css({"padding-right":10})},bgcolor:"",tips:{msg:a,follow:b}};return h.time="object"==typeof d?d.time:0|d,d=d||{},h.closeBtn=d.closeBtn||!1,h.maxWidth=d.maxWidth||e,h.tips.guide=d.guide||f,h.tips.style=d.style||g,h.tips.more=d.more,c.layer(h)}};var g=["xubox_layer","xubox_iframe",".xubox_title",".xubox_text",".xubox_page",".xubox_main"],h=function(a){var b=this,d=b.config;layer.index++,b.index=layer.index,b.config=c.extend({},d,a),b.config.dialog=c.extend({},d.dialog,a.dialog),b.config.page=c.extend({},d.page,a.page),b.config.iframe=c.extend({},d.iframe,a.iframe),b.config.loading=c.extend({},d.loading,a.loading),b.config.tips=c.extend({},d.tips,a.tips),b.creat()};h.pt=h.prototype,h.pt.config={type:0,shade:[.3,"#000"],fix:!0,move:".xubox_title",title:"信息",offset:["","50%"],area:["310px","auto"],closeBtn:[0,!0],time:0,bgcolor:"#fff",border:[6,.3,"#000"],zIndex:19891014,maxWidth:400,dialog:{btns:1,btn:["确定","取消"],type:8,msg:"",yes:function(a){layer.close(a)},no:function(a){layer.close(a)}},page:{dom:"#xulayer",html:"",url:""},iframe:{src:"",scrolling:"auto"},loading:{type:0},tips:{msg:"",follow:"",guide:0,isGuide:!0,style:["background-color:#FF9900; color:#fff;","#FF9900"]},success:function(){},close:function(a){layer.close(a)},end:function(){}},h.pt.space=function(a){var b=this,a=a||"",c=b.index,d=b.config,e=d.dialog,f=-1===e.type?"":'',h=['
'+f+''+e.msg+"
",'
'+a+"
",'','','
'+d.tips.msg+'
'],i="",j="",k=d.zIndex+c,l="z-index:"+k+"; background-color:"+d.shade[1]+"; opacity:"+d.shade[0]+"; filter:alpha(opacity="+100*d.shade[0]+");";d.shade[0]&&(i='
'),d.zIndex=k;var m="",n="",o="z-index:"+(k-1)+"; background-color: "+d.border[2]+"; opacity:"+d.border[1]+"; filter:alpha(opacity="+100*d.border[1]+"); top:-"+d.border[0]+"px; left:-"+d.border[0]+"px;";d.border[0]&&(j='
'),!d.maxmin||1!==d.type&&2!==d.type||/^\d+%$/.test(d.area[0])&&/^\d+%$/.test(d.area[1])||(n=''),d.closeBtn[1]&&(n+='');var p="object"==typeof d.title;return d.title&&(m='
'+(p?d.title[0]:d.title)+"
"),[i,'
'+h[d.type]+m+''+n+'
'+j+"
"]},h.pt.creat=function(){var a=this,b="",d=a.config,e=d.dialog,f=a.index,h=d.page,i=c("body"),j=function(d){var d=d||"";b=a.space(d),i.append(c(b[0]))};switch(d.type){case 0:d.title||(d.area=["auto","auto"]),c(".xubox_dialog")[0]&&layer.close(c(".xubox_dialog").parents("."+g[0]).attr("times"));break;case 1:if(""!==h.html)j('
'+h.html+"
"),i.append(c(b[1]));else if(""!==h.url)j('
'+h.html+"
"),i.append(c(b[1])),c.get(h.url,function(a){c("#xuboxPageHtml"+f).html(a.toString()),h.ok&&h.ok(a)});else{if(0!=c(h.dom).parents(g[4]).length)return;j(),c(h.dom).show().wrap(c(b[1]))}break;case 3:d.title=!1,d.area=["auto","auto"],d.closeBtn=["",!1],c(".xubox_loading")[0]&&layer.closeLoad();break;case 4:d.title=!1,d.area=["auto","auto"],d.fix=!1,d.border=[0],d.tips.more||layer.closeTips()}1!==d.type&&(j(),i.append(c(b[1])));var k=a.layerE=c("#"+g[0]+f);if(k.css({width:d.area[0],height:d.area[1]}),d.fix||k.css({position:"absolute"}),d.title&&(3!==d.type||4!==d.type)){var l=0===d.type?e:d,m=k.find(".xubox_botton");switch(l.btn=d.btn||e.btn,l.btns){case 0:m.html("").hide();break;case 1:m.html(''+l.btn[0]+"");break;case 2:m.html(''+l.btn[0]+''+l.btn[1]+"")}}"auto"===k.css("left")?(k.hide(),setTimeout(function(){k.show(),a.set(f)},500)):a.set(f),d.time<=0||a.autoclose(),a.callback()},f.fade=function(a,b,c){a.css({opacity:0}).animate({opacity:c},b)},h.pt.offset=function(){var a=this,b=a.config,c=a.layerE,e=c.outerHeight();a.offsetTop=""===b.offset[0]&&ee.maxWidth&&i.width(e.maxWidth),o.tipColor=e.tips.style[1],m[0]=i.outerWidth(),o.autoLeft=function(){o.left+m[0]-d.width()>0?(o.tipLeft=o.left+o.width-m[0],p.css({right:12,left:"auto"})):o.tipLeft=o.left},o.where=[function(){o.autoLeft(),o.tipTop=o.top-m[1]-10,p.removeClass("layerTipsB").addClass("layerTipsT").css({"border-right-color":o.tipColor})},function(){o.tipLeft=o.left+o.width+10,o.tipTop=o.top,p.removeClass("layerTipsL").addClass("layerTipsR").css({"border-bottom-color":o.tipColor})},function(){o.autoLeft(),o.tipTop=o.top+o.height+10,p.removeClass("layerTipsT").addClass("layerTipsB").css({"border-right-color":o.tipColor})},function(){o.tipLeft=o.left-m[0]+10,o.tipTop=o.top,p.removeClass("layerTipsR").addClass("layerTipsL").css({"border-bottom-color":o.tipColor})}],o.where[e.tips.guide](),0===e.tips.guide?o.top-(d.scrollTop()+m[1]+16)<0&&o.where[2]():1===e.tips.guide?d.width()-(o.left+o.width+m[0]+16)>0||o.where[3]():2===e.tips.guide?o.top-d.scrollTop()+o.height+m[1]+16-d.height()>0&&o.where[0]():3===e.tips.guide?m[0]+16-o.left>0&&o.where[1]():4===e.tips.guide,i.css({left:o.tipLeft,top:o.tipTop})}e.fadeIn&&(f.fade(i,e.fadeIn,1),f.fade(c("#xubox_shade"+a),e.fadeIn,e.shade[0])),e.fix&&""===e.offset[0]&&!e.shift&&d.on("resize",function(){i.css({top:(d.height()-i.outerHeight())/2})}),b.move()},h.pt.shift=function(a,b,c){var e=this,f=e.config,g=e.layerE,h=0,i=d.width(),j=d.height()+(f.fix?0:d.scrollTop());h="50%"==f.offset[1]||""==f.offset[1]?g.outerWidth()/2:g.outerWidth();var k={t:{top:e.offsetTop},b:{top:j-g.outerHeight()-f.border[0]},cl:h+f.border[0],ct:-g.outerHeight(),cr:i-h-f.border[0]};switch(a){case"left-top":g.css({left:k.cl,top:k.ct}).animate(k.t,b);break;case"top":g.css({top:k.ct}).animate(k.t,b);break;case"right-top":g.css({left:k.cr,top:k.ct}).animate(k.t,b);break;case"right-bottom":g.css({left:k.cr,top:j}).animate(c?k.t:k.b,b);break;case"bottom":g.css({top:j}).animate(c?k.t:k.b,b);break;case"left-bottom":g.css({left:k.cl,top:j}).animate(c?k.t:k.b,b);break;case"left":g.css({left:-g.outerWidth()}).animate({left:e.offsetLeft},b)}},h.pt.autoArea=function(a){var b,d=this,a=a||d.index,e=d.config,f=e.page,h=c("#"+g[0]+a),i=h.find(g[2]),j=h.find(g[5]),k=e.title?i.innerHeight():0,l=0;switch("auto"===e.area[0]&&j.outerWidth()>=e.maxWidth&&h.css({width:e.maxWidth}),e.type){case 0:var m=h.find(".xubox_botton>a");b=h.find(g[3]).outerHeight()+20,m.length>0&&(l=m.outerHeight()+20);break;case 1:var n=h.find(g[4]);b=c(f.dom).outerHeight(),"auto"===e.area[0]&&h.css({width:n.outerWidth()}),(""!==f.html||""!==f.url)&&(b=n.outerHeight());break;case 2:h.find("iframe").css({width:h.outerWidth(),height:h.outerHeight()-(e.title?i.innerHeight():0)});break;case 3:var o=h.find(".xubox_loading");b=o.outerHeight(),j.css({width:o.width()})}"auto"===e.area[1]&&j.css({height:k+b+l}),c("#xubox_border"+a).css({width:h.outerWidth()+2*e.border[0],height:h.outerHeight()+2*e.border[0]}),layer.ie6&&"auto"!==e.area[0]&&j.css({width:h.outerWidth()}),h.css("50%"!==e.offset[1]&&""!=e.offset[1]||4===e.type?{marginLeft:0}:{marginLeft:-h.outerWidth()/2})},h.pt.move=function(){var a=this,b=a.config,e={setY:0,moveLayer:function(){if(0==parseInt(e.layerE.css("margin-left")))var a=parseInt(e.move.css("left"));else var a=parseInt(e.move.css("left"))+-parseInt(e.layerE.css("margin-left"));"fixed"!==e.layerE.css("position")&&(a-=e.layerE.parent().offset().left,e.setY=0),e.layerE.css({left:a,top:parseInt(e.move.css("top"))-e.setY})}},f=a.layerE.find(b.move);b.move&&f.attr("move","ok"),f.css(b.move?{cursor:"move"}:{cursor:"auto"}),c(b.move).on("mousedown",function(a){if(a.preventDefault(),"ok"===c(this).attr("move")){e.ismove=!0,e.layerE=c(this).parents("."+g[0]);var f=e.layerE.offset().left,h=e.layerE.offset().top,i=e.layerE.width()-6,j=e.layerE.height()-6;c("#xubox_moves")[0]||c("body").append('
'),e.move=c("#xubox_moves"),b.moveType&&e.move.css({opacity:0}),e.moveX=a.pageX-e.move.position().left,e.moveY=a.pageY-e.move.position().top,"fixed"!==e.layerE.css("position")||(e.setY=d.scrollTop())}}),c(document).mousemove(function(a){if(e.ismove){var c=a.pageX-e.moveX,f=a.pageY-e.moveY;if(a.preventDefault(),!b.moveOut){e.setY=d.scrollTop();var g=d.width()-e.move.outerWidth()-b.border[0],h=b.border[0]+e.setY;cg&&(c=g),h>f&&(f=h),f>d.height()-e.move.outerHeight()-b.border[0]+e.setY&&(f=d.height()-e.move.outerHeight()-b.border[0]+e.setY)}e.move.css({left:c,top:f}),b.moveType&&e.moveLayer(),c=null,f=null,g=null,h=null}}).mouseup(function(){try{e.ismove&&(e.moveLayer(),e.move.remove()),e.ismove=!1}catch(a){e.ismove=!1}b.moveEnd&&b.moveEnd()})},h.pt.autoclose=function(){var a=this,b=a.config.time,c=function(){b--,0===b&&(layer.close(a.index),clearInterval(a.autotime))};a.autotime=setInterval(c,1e3)},f.config={end:{}},h.pt.callback=function(){var a=this,b=a.layerE,d=a.config,e=d.dialog;a.openLayer(),a.config.success(b),layer.ie6&&a.IE6(b),b.find(".xubox_close").on("click",function(){d.close(a.index),layer.close(a.index)}),b.find(".xubox_yes").on("click",function(){d.yes?d.yes(a.index):e.yes(a.index)}),b.find(".xubox_no").on("click",function(){d.no?d.no(a.index):e.no(a.index),layer.close(a.index)}),a.config.shadeClose&&c("#xubox_shade"+a.index).on("click",function(){layer.close(a.index)}),b.find(".xubox_min").on("click",function(){layer.min(a.index,d),d.min&&d.min(b)}),b.find(".xubox_max").on("click",function(){c(this).hasClass("xubox_maxmin")?(layer.restore(a.index),d.restore&&d.restore(b)):(layer.full(a.index,d),d.full&&d.full(b))}),f.config.end[a.index]=d.end},f.reselect=function(){c.each(c("select"),function(){var a=c(this);a.parents("."+g[0])[0]||1==a.attr("layer")&&c("."+g[0]).length<1&&a.removeAttr("layer").show(),a=null})},h.pt.IE6=function(a){var b=this,e=a.offset().top;if(b.config.fix)var f=function(){a.css({top:d.scrollTop()+e})};else var f=function(){a.css({top:e})};f(),d.scroll(f),c.each(c("select"),function(){var a=c(this);a.parents("."+g[0])[0]||"none"==a.css("display")||a.attr({layer:"1"}).hide(),a=null})},h.pt.openLayer=function(){{var a=this;a.layerE}layer.autoArea=function(b){return a.autoArea(b)},layer.shift=function(b,c,d){a.shift(b,c,d)},layer.setMove=function(){return a.move()},layer.zIndex=a.config.zIndex,layer.setTop=function(a){var b=function(){layer.zIndex++,a.css("z-index",layer.zIndex+1)};return layer.zIndex=parseInt(a[0].style.zIndex),a.on("mousedown",b),layer.zIndex}},f.isauto=function(a,b,c){"auto"===b.area[0]&&(b.area[0]=a.outerWidth()),"auto"===b.area[1]&&(b.area[1]=a.outerHeight()),a.attr({area:b.area+","+c}),a.find(".xubox_max").addClass("xubox_maxmin")},f.rescollbar=function(a){g.html.attr("layer-full")==a&&(g.html[0].style.removeProperty?g.html[0].style.removeProperty("overflow"):g.html[0].style.removeAttribute("overflow"),g.html.removeAttr("layer-full"))},layer.getIndex=function(a){return c(a).parents("."+g[0]).attr("times")},layer.getChildFrame=function(a,b){return b=b||c("."+g[1]).parents("."+g[0]).attr("times"),c("#"+g[0]+b).find("."+g[1]).contents().find(a)},layer.getFrameIndex=function(a){return c(a?"#"+a:"."+g[1]).parents("."+g[0]).attr("times")},layer.iframeAuto=function(a){a=a||c("."+g[1]).parents("."+g[0]).attr("times");var b=layer.getChildFrame("body",a).outerHeight(),d=c("#"+g[0]+a),e=d.find(g[2]),f=0;e&&(f=e.height()),d.css({height:b+f});var h=-parseInt(c("#xubox_border"+a).css("top"));c("#xubox_border"+a).css({height:b+2*h+f}),c("#"+g[1]+a).css({height:b})},layer.iframeSrc=function(a,b){c("#"+g[0]+a).find("iframe").attr("src",b)},layer.area=function(a,b){var d=[c("#"+g[0]+a),c("#xubox_border"+a)],e=d[0].attr("type"),h=d[0].find(g[5]),i=d[0].find(g[2]);if(e===f.type[1]||e===f.type[2]){if(d[0].css(b),h.css({width:b.width,height:b.height}),e===f.type[2]){var j=d[0].find("iframe");j.css({width:b.width,height:i?b.height-i.innerHeight():b.height})}"0px"!==d[0].css("margin-left")&&(b.hasOwnProperty("top")&&d[0].css({top:b.top-(d[1][0]?parseFloat(d[1].css("top")):0)}),b.hasOwnProperty("left")&&d[0].css({left:b.left+d[0].outerWidth()/2-(d[1][0]?parseFloat(d[1].css("left")):0)}),d[0].css({marginLeft:-d[0].outerWidth()/2})),d[1][0]&&d[1].css({width:parseFloat(b.width)-2*parseFloat(d[1].css("left")),height:parseFloat(b.height)-2*parseFloat(d[1].css("top"))})}},layer.min=function(a,b){var d=c("#"+g[0]+a),e=[d.position().top,d.position().left+parseFloat(d.css("margin-left"))];f.isauto(d,b,e),layer.area(a,{width:180,height:35}),d.find(".xubox_min").hide(),"page"===d.attr("type")&&d.find(g[4]).hide(),f.rescollbar(a)},layer.restore=function(a){{var b=c("#"+g[0]+a),d=b.attr("area").split(",");b.attr("type")}layer.area(a,{width:parseFloat(d[0]),height:parseFloat(d[1]),top:parseFloat(d[2]),left:parseFloat(d[3])}),b.find(".xubox_max").removeClass("xubox_maxmin"),b.find(".xubox_min").show(),"page"===b.attr("type")&&b.find(g[4]).show(),f.rescollbar(a)},layer.full=function(a,b){var e,h=c("#"+g[0]+a),i=2*b.border[0]||6,j=[h.position().top,h.position().left+parseFloat(h.css("margin-left"))];f.isauto(h,b,j),g.html.attr("layer-full")||g.html.css("overflow","hidden").attr("layer-full",a),clearTimeout(e),e=setTimeout(function(){layer.area(a,{top:"fixed"===h.css("position")?0:d.scrollTop(),left:"fixed"===h.css("position")?0:d.scrollLeft(),width:d.width()-i,height:d.height()-i})},100)},layer.title=function(a,b){var d=c("#"+g[0]+(b||layer.index)).find(".xubox_title>em");d.html(a)},layer.close=function(a){var b=c("#"+g[0]+a),d=b.attr("type"),e=c("#xubox_moves, #xubox_shade"+a);if(b[0]){if(d==f.type[1])if(b.find(".xuboxPageHtml")[0])b[0].innerHTML="",b.remove();else{b.find(".xubox_setwin,.xubox_close,.xubox_botton,.xubox_title,.xubox_border").remove();for(var h=0;3>h;h++)b.find(".layer_pageContent").unwrap().hide()}else b[0].innerHTML="",b.remove();e.remove(),layer.ie6&&f.reselect(),f.rescollbar(a),"function"==typeof f.config.end[a]&&f.config.end[a](),delete f.config.end[a]}},layer.closeLoad=function(){layer.close(c(".xubox_loading").parents("."+g[0]).attr("times"))},layer.closeTips=function(){layer.closeAll("tips")},layer.closeAll=function(a){c.each(c("."+g[0]),function(){var b=c(this),d=a?b.attr("type")===a:1;d&&layer.close(b.attr("times")),d=null})},f.run=function(){c=jQuery,d=c(a),g.html=c("html"),layer.use("skin/layer.css"),c.layer=function(a){var b=new h(a);return b.index},(new Image).src=layer.path+"skin/default/xubox_ico0.png"},"function"==typeof define?define(function(){return f.run(),layer}):f.run()}(window); diff --git a/templates/jperm/sudo_detail.html b/templates/jperm/sudo_detail.html deleted file mode 100644 index 1f547d8c2..000000000 --- a/templates/jperm/sudo_detail.html +++ /dev/null @@ -1,170 +0,0 @@ -{% extends 'base.html' %} -{% load mytags %} - -{% block content %} - {% include 'nav_cat_bar.html' %} -
-
-
-
-
-
授权主机/组
- -
-
-

用户

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

{{ user_group.comment }}

-

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

-

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

授权主机/组

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

{{ group.comment }}

-

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

-

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

{{ group.comment }}

-

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

-

-
-
-
- {% endfor %} - {% if not cmd_groups %} - (暂无) - {% endif %} -
-
-
- -
-
- -{% endblock %} \ No newline at end of file