mirror of https://github.com/jumpserver/jumpserver
487 lines
13 KiB
Python
487 lines
13 KiB
Python
# coding: utf-8
|
||
|
||
import os, sys, time, re
|
||
from Crypto.Cipher import AES
|
||
import crypt
|
||
import pwd
|
||
from binascii import b2a_hex, a2b_hex
|
||
import hashlib
|
||
import datetime
|
||
import random
|
||
import subprocess
|
||
import json
|
||
import logging
|
||
|
||
from settings import *
|
||
from django.core.paginator import Paginator, EmptyPage, InvalidPage
|
||
from django.http import HttpResponse, Http404
|
||
from django.template import RequestContext
|
||
from juser.models import User, UserGroup
|
||
from jlog.models import Log, TtyLog
|
||
from jasset.models import Asset, AssetGroup
|
||
from jperm.models import PermRule, PermRole
|
||
from jumpserver.models import Setting
|
||
from django.http import HttpResponseRedirect
|
||
from django.shortcuts import render_to_response
|
||
from django.core.mail import send_mail
|
||
|
||
|
||
def set_log(level):
|
||
"""
|
||
return a log file object
|
||
根据提示设置log打印
|
||
"""
|
||
log_file = os.path.join(LOG_DIR, 'jumpserver.log')
|
||
if not os.path.isfile(log_file):
|
||
os.mknod(log_file)
|
||
os.chmod(log_file, 0777)
|
||
log_level_total = {'debug': logging.DEBUG, 'info': logging.INFO, 'warning': logging.WARN, 'error': logging.ERROR,
|
||
'critical': logging.CRITICAL}
|
||
logger_f = logging.getLogger('jumpserver')
|
||
logger_f.setLevel(logging.DEBUG)
|
||
fh = logging.FileHandler(log_file)
|
||
fh.setLevel(log_level_total.get(level, logging.DEBUG))
|
||
formatter = logging.Formatter('%(asctime)s - %(filename)s - %(levelname)s - %(message)s')
|
||
fh.setFormatter(formatter)
|
||
logger_f.addHandler(fh)
|
||
return logger_f
|
||
|
||
|
||
def get_asset_info(asset):
|
||
"""
|
||
获取资产的相关账号端口信息
|
||
"""
|
||
default = get_object(Setting, name='default')
|
||
info = {'hostname': asset.hostname, 'ip': asset.ip}
|
||
if asset.use_default_auth:
|
||
if default:
|
||
info['port'] = int(default.field2)
|
||
info['username'] = default.field1
|
||
try:
|
||
info['password'] = CRYPTOR.decrypt(default.field3)
|
||
except ServerError:
|
||
pass
|
||
info['ssh_key'] = default.field4
|
||
else:
|
||
info['port'] = int(asset.port)
|
||
info['username'] = asset.username
|
||
info['password'] = CRYPTOR.decrypt(asset.password)
|
||
|
||
return info
|
||
|
||
|
||
def get_role(user, asset):
|
||
"""
|
||
获取用户在这个资产上的授权角色列表
|
||
"""
|
||
roles = []
|
||
rules = PermRule.objects.filter(user=user, asset=asset)
|
||
for rule in rules:
|
||
roles.extend(list(rule.role.all()))
|
||
return roles
|
||
|
||
|
||
def get_role_key(user, role):
|
||
"""
|
||
由于role的key的权限是所有人可以读的, ansible执行命令等要求为600,所以拷贝一份到特殊目录
|
||
:param user:
|
||
:param role:
|
||
:return: self key path
|
||
"""
|
||
user_role_key_dir = os.path.join(KEY_DIR, 'user')
|
||
user_role_key_path = os.path.join(user_role_key_dir, '%s_%s.pem' % (user.username, role.name))
|
||
mkdir(user_role_key_dir, mode=0777)
|
||
if not os.path.isfile(user_role_key_path):
|
||
with open(os.path.join(role.key_path, 'id_rsa')) as fk:
|
||
with open(user_role_key_path, 'w') as fu:
|
||
fu.write(fk.read())
|
||
logger.debug("创建新的用户角色key %s" % user_role_key_path)
|
||
chown(user_role_key_path, user.username)
|
||
os.chmod(user_role_key_path, 0600)
|
||
return user_role_key_path
|
||
|
||
|
||
def chown(path, user, group=''):
|
||
if not group:
|
||
group = user
|
||
try:
|
||
uid = pwd.getpwnam(user).pw_uid
|
||
gid = pwd.getpwnam(group).pw_gid
|
||
os.chown(path, uid, gid)
|
||
except KeyError:
|
||
pass
|
||
|
||
|
||
def page_list_return(total, current=1):
|
||
"""
|
||
page
|
||
分页,返回本次分页的最小页数到最大页数列表
|
||
"""
|
||
min_page = current - 2 if current - 4 > 0 else 1
|
||
max_page = min_page + 4 if min_page + 4 < total else total
|
||
|
||
return range(min_page, max_page + 1)
|
||
|
||
|
||
def pages(post_objects, request):
|
||
"""
|
||
page public function , return page's object tuple
|
||
分页公用函数,返回分页的对象元组
|
||
"""
|
||
paginator = Paginator(post_objects, 20)
|
||
try:
|
||
current_page = int(request.GET.get('page', '1'))
|
||
except ValueError:
|
||
current_page = 1
|
||
|
||
page_range = page_list_return(len(paginator.page_range), current_page)
|
||
|
||
try:
|
||
page_objects = paginator.page(current_page)
|
||
except (EmptyPage, InvalidPage):
|
||
page_objects = paginator.page(paginator.num_pages)
|
||
|
||
if current_page >= 5:
|
||
show_first = 1
|
||
else:
|
||
show_first = 0
|
||
|
||
if current_page <= (len(paginator.page_range) - 3):
|
||
show_end = 1
|
||
else:
|
||
show_end = 0
|
||
|
||
# 所有对象, 分页器, 本页对象, 所有页码, 本页页码,是否显示第一页,是否显示最后一页
|
||
return post_objects, paginator, page_objects, page_range, current_page, show_first, show_end
|
||
|
||
|
||
class PyCrypt(object):
|
||
"""
|
||
This class used to encrypt and decrypt password.
|
||
加密类
|
||
"""
|
||
|
||
def __init__(self, key):
|
||
self.key = key
|
||
self.mode = AES.MODE_CBC
|
||
|
||
@staticmethod
|
||
def gen_rand_pass(length, especial=False):
|
||
"""
|
||
random password
|
||
随机生成密码
|
||
"""
|
||
salt_key = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
|
||
symbol = '!@$%^&*()_'
|
||
salt_list = []
|
||
if especial:
|
||
for i in range(length - 4):
|
||
salt_list.append(random.choice(salt_key))
|
||
for i in range(4):
|
||
salt_list.append(random.choice(symbol))
|
||
else:
|
||
for i in range(length):
|
||
salt_list.append(random.choice(salt_key))
|
||
salt = ''.join(salt_list)
|
||
return salt
|
||
|
||
@staticmethod
|
||
def md5_crypt(string):
|
||
"""
|
||
md5 encrypt method
|
||
md5非对称加密方法
|
||
"""
|
||
return hashlib.new("md5", string).hexdigest()
|
||
|
||
@staticmethod
|
||
def gen_sha512(salt, password):
|
||
"""
|
||
generate sha512 format password
|
||
生成sha512加密密码
|
||
"""
|
||
return crypt.crypt(password, '$6$%s$' % salt)
|
||
|
||
def encrypt(self, passwd=None, length=32):
|
||
"""
|
||
encrypt gen password
|
||
对称加密之加密生成密码
|
||
"""
|
||
if not passwd:
|
||
passwd = self.gen_rand_pass()
|
||
|
||
cryptor = AES.new(self.key, self.mode, b'8122ca7d906ad5e1')
|
||
try:
|
||
count = len(passwd)
|
||
except TypeError:
|
||
raise ServerError('Encrypt password error, TYpe error.')
|
||
|
||
add = (length - (count % length))
|
||
passwd += ('\0' * add)
|
||
cipher_text = cryptor.encrypt(passwd)
|
||
return b2a_hex(cipher_text)
|
||
|
||
def decrypt(self, text):
|
||
"""
|
||
decrypt pass base the same key
|
||
对称加密之解密,同一个加密随机数
|
||
"""
|
||
cryptor = AES.new(self.key, self.mode, b'8122ca7d906ad5e1')
|
||
try:
|
||
plain_text = cryptor.decrypt(a2b_hex(text))
|
||
except TypeError:
|
||
raise ServerError('Decrypt password error, TYpe error.')
|
||
return plain_text.rstrip('\0')
|
||
|
||
|
||
class ServerError(Exception):
|
||
"""
|
||
self define exception
|
||
自定义异常
|
||
"""
|
||
pass
|
||
|
||
|
||
def get_object(model, **kwargs):
|
||
"""
|
||
use this function for query
|
||
使用改封装函数查询数据库
|
||
"""
|
||
for value in kwargs.values():
|
||
if not value:
|
||
return None
|
||
|
||
the_object = model.objects.filter(**kwargs)
|
||
if len(the_object) == 1:
|
||
the_object = the_object[0]
|
||
else:
|
||
the_object = None
|
||
return the_object
|
||
|
||
|
||
def require_role(role='user'):
|
||
"""
|
||
decorator for require user role in ["super", "admin", "user"]
|
||
要求用户是某种角色 ["super", "admin", "user"]的装饰器
|
||
"""
|
||
|
||
def _deco(func):
|
||
def __deco(request, *args, **kwargs):
|
||
request.session['pre_url'] = request.path
|
||
if not request.user.is_authenticated():
|
||
return HttpResponseRedirect('/login/')
|
||
if role == 'admin':
|
||
# if request.session.get('role_id', 0) < 1:
|
||
if request.user.role == 'CU':
|
||
return HttpResponseRedirect('/')
|
||
elif role == 'super':
|
||
# if request.session.get('role_id', 0) < 2:
|
||
if request.user.role in ['CU', 'GA']:
|
||
return HttpResponseRedirect('/')
|
||
return func(request, *args, **kwargs)
|
||
|
||
return __deco
|
||
|
||
return _deco
|
||
|
||
|
||
def is_role_request(request, role='user'):
|
||
"""
|
||
require this request of user is right
|
||
要求请求角色正确
|
||
"""
|
||
role_all = {'user': 'CU', 'admin': 'GA', 'super': 'SU'}
|
||
if request.user.role == role_all.get(role, 'CU'):
|
||
return True
|
||
else:
|
||
return False
|
||
|
||
|
||
def get_session_user_dept(request):
|
||
"""
|
||
get department of the user in session
|
||
获取session中用户的部门
|
||
"""
|
||
# user_id = request.session.get('user_id', 0)
|
||
# print '#' * 20
|
||
# print user_id
|
||
# user = User.objects.filter(id=user_id)
|
||
# if user:
|
||
# user = user[0]
|
||
# return user, None
|
||
return request.user, None
|
||
|
||
|
||
@require_role
|
||
def get_session_user_info(request):
|
||
"""
|
||
get the user info of the user in session, for example id, username etc.
|
||
获取用户的信息
|
||
"""
|
||
# user_id = request.session.get('user_id', 0)
|
||
# user = get_object(User, id=user_id)
|
||
# if user:
|
||
# return [user.id, user.username, user]
|
||
return [request.user.id, request.user.username, request.user]
|
||
|
||
|
||
def get_user_dept(request):
|
||
"""
|
||
get the user dept id
|
||
获取用户的部门id
|
||
"""
|
||
user_id = request.user.id
|
||
if user_id:
|
||
user_dept = User.objects.get(id=user_id).dept
|
||
return user_dept.id
|
||
|
||
|
||
def api_user(request):
|
||
hosts = Log.objects.filter(is_finished=0).count()
|
||
users = Log.objects.filter(is_finished=0).values('user').distinct().count()
|
||
ret = {'users': users, 'hosts': hosts}
|
||
json_data = json.dumps(ret)
|
||
return HttpResponse(json_data)
|
||
|
||
|
||
def view_splitter(request, su=None, adm=None):
|
||
"""
|
||
for different user use different view
|
||
视图分页器
|
||
"""
|
||
if is_role_request(request, 'super'):
|
||
return su(request)
|
||
elif is_role_request(request, 'admin'):
|
||
return adm(request)
|
||
else:
|
||
return HttpResponseRedirect('/login/')
|
||
|
||
|
||
def validate(request, user_group=None, user=None, asset_group=None, asset=None, edept=None):
|
||
"""
|
||
validate the user request
|
||
判定用户请求是否合法
|
||
"""
|
||
dept = get_session_user_dept(request)[1]
|
||
if edept:
|
||
if dept.id != int(edept[0]):
|
||
return False
|
||
|
||
if user_group:
|
||
dept_user_groups = dept.usergroup_set.all()
|
||
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()
|
||
user_ids = []
|
||
for dept_user in dept_users:
|
||
user_ids.append(str(dept_user.id))
|
||
|
||
if not set(user).issubset(set(user_ids)):
|
||
return False
|
||
|
||
if asset_group:
|
||
dept_asset_groups = dept.bisgroup_set.all()
|
||
asset_group_ids = []
|
||
for group in dept_asset_groups:
|
||
asset_group_ids.append(str(group.id))
|
||
|
||
if not set(asset_group).issubset(set(asset_group_ids)):
|
||
return False
|
||
|
||
if asset:
|
||
dept_assets = dept.asset_set.all()
|
||
asset_ids = []
|
||
for dept_asset in dept_assets:
|
||
asset_ids.append(str(dept_asset.id))
|
||
|
||
if not set(asset).issubset(set(asset_ids)):
|
||
return False
|
||
|
||
return True
|
||
|
||
|
||
def verify(request, user_group=None, user=None, asset_group=None, asset=None, edept=None):
|
||
dept = get_session_user_dept(request)[1]
|
||
if edept:
|
||
if dept.id != int(edept[0]):
|
||
return False
|
||
|
||
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)):
|
||
return False
|
||
|
||
if user:
|
||
dept_users = dept.user_set.all()
|
||
users = []
|
||
for user_id in user:
|
||
users.extend(User.objects.filter(id=user_id))
|
||
|
||
if not set(users).issubset(set(dept_users)):
|
||
return False
|
||
|
||
if asset_group:
|
||
dept_asset_groups = dept.bisgroup_set.all()
|
||
asset_group_ids = []
|
||
for group in dept_asset_groups:
|
||
asset_group_ids.append(str(group.id))
|
||
|
||
if not set(asset_group).issubset(set(asset_group_ids)):
|
||
return False
|
||
|
||
if asset:
|
||
dept_assets = dept.asset_set.all()
|
||
asset_ids = []
|
||
for a in dept_assets:
|
||
asset_ids.append(str(a.id))
|
||
print asset, asset_ids
|
||
if not set(asset).issubset(set(asset_ids)):
|
||
return False
|
||
|
||
return True
|
||
|
||
|
||
def bash(cmd):
|
||
"""
|
||
run a bash shell command
|
||
执行bash命令
|
||
"""
|
||
return subprocess.call(cmd, shell=True)
|
||
|
||
|
||
def mkdir(dir_name, username='', mode=0755):
|
||
"""
|
||
insure the dir exist and mode ok
|
||
目录存在,如果不存在就建立,并且权限正确
|
||
"""
|
||
if not os.path.isdir(dir_name):
|
||
os.makedirs(dir_name)
|
||
os.chmod(dir_name, mode)
|
||
if username:
|
||
chown(dir_name, username)
|
||
|
||
|
||
def http_success(request, msg):
|
||
return render_to_response('success.html', locals())
|
||
|
||
|
||
def http_error(request, emg):
|
||
message = emg
|
||
return render_to_response('error.html', locals())
|
||
|
||
|
||
def my_render(template, data, request):
|
||
return render_to_response(template, data, context_instance=RequestContext(request))
|
||
|
||
|
||
CRYPTOR = PyCrypt(KEY)
|
||
logger = set_log(LOG_LEVEL)
|