From 79c79432f56602aa8ca2fd3c1a11ea5531e35e65 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Tue, 15 Sep 2015 23:38:06 +0800 Subject: [PATCH 01/48] usermodle --- jumpserver/settings.py | 4 +- jumpserver/urls.py | 4 +- jumpserver/views.py | 124 ++++++++++++++++++++++++++++++++++------ juser/models.py | 19 +++++- templates/index_cu.html | 4 +- 5 files changed, 129 insertions(+), 26 deletions(-) diff --git a/jumpserver/settings.py b/jumpserver/settings.py index aeeaec053..7d77b4ff4 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -22,7 +22,7 @@ DB_PORT = config.getint('db', 'port') DB_USER = config.get('db', 'user') DB_PASSWORD = config.get('db', 'password') DB_DATABASE = config.get('db', 'database') - +AUTH_USER_MODEL = 'juser.CustomUser' # mail config EMAIL_HOST = config.get('mail', 'email_host') EMAIL_PORT = config.get('mail', 'email_port') @@ -98,7 +98,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.static', 'django.core.context_processors.tz', 'django.contrib.messages.context_processors.messages', - 'jumpserver.context_processors.name_proc' + 'jumpserver.context_processors.name_proc', ) TEMPLATE_DIRS = ( diff --git a/jumpserver/urls.py b/jumpserver/urls.py index 617d0351e..1f47918a1 100644 --- a/jumpserver/urls.py +++ b/jumpserver/urls.py @@ -8,8 +8,8 @@ urlpatterns = patterns('', (r'^skin_config/$', 'jumpserver.views.skin_config'), (r'^install/$', 'jumpserver.views.install'), (r'^base/$', 'jumpserver.views.base'), - (r'^login/$', 'jumpserver.views.login'), - (r'^logout/$', 'jumpserver.views.logout'), + (r'^login/$', 'jumpserver.views.Login'), + (r'^logout/$', 'jumpserver.views.Logout'), (r'^file/upload/$', 'jumpserver.views.upload'), (r'^file/download/$', 'jumpserver.views.download'), (r'^error/$', 'jumpserver.views.httperror'), diff --git a/jumpserver/views.py b/jumpserver/views.py index 37784f9c7..a45f904f1 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -12,7 +12,8 @@ from django.http import HttpResponse # from jperm.models import Apply import paramiko from jumpserver.api import * - +from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.decorators import login_required def getDaysByNum(num): @@ -46,13 +47,105 @@ def get_data(data, items, option): dic[name] = li return dic +class CustomUser(object): + def __init__(self,request): + self.requset = request + def __unicode__(self): + return self.requset.user.username + + def get_asset_group(self): + """ + Get user host_groups. + 获取用户有权限的主机组 + """ + host_group_list = [] + perm_list = [] + user_group_all = self.requset.user.group.all() + for user_group in user_group_all: + perm_list.extend(user_group.perm_set.all()) + + for perm in perm_list: + host_group_list.append(perm.asset_group) + + return host_group_list + + def get_asset_group_info(self, printable=False): + """ + Get or print asset group info + 获取或打印用户授权资产组 + """ + asset_groups_info = {} + asset_groups = self.get_asset_group() + + for asset_group in asset_groups: + asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] + + if printable: + for group_id in asset_groups_info: + if asset_groups_info[group_id][1]: + print "[%3s] %s -- %s" % (group_id, + asset_groups_info[group_id][0], + asset_groups_info[group_id][1]) + else: + print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) + print '' + else: + return asset_groups_info + + def get_asset(self): + """ + Get the assets of under the user control. + 获取主机列表 + """ + assets = [] + asset_groups = self.get_asset_group() + + for asset_group in asset_groups: + assets.extend(asset_group.asset_set.all()) + + return assets + + def get_asset_info(self, printable=False): + """ + Get or print the user asset info + 获取或打印用户资产信息 + """ + from jasset.models import AssetAlias + assets_info = {} + assets = self.get_asset() + + for asset in assets: + asset_alias = AssetAlias.objects.filter(user=self, asset=asset) + if asset_alias and asset_alias[0].alias != '': + assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] + else: + assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] + + if printable: + ips = assets_info.keys() + ips.sort() + for ip in ips: + if assets_info[ip][2]: + print '%-15s -- %s' % (ip, assets_info[ip][2]) + else: + print '%-15s' % ip + print '' + else: + return assets_info + + +# @login_required @require_role(role='user') def index_cu(request): - user_id = request.session.get('user_id') - user = get_object(User, id=user_id) + # user_id = request.session.get('user_id') + # user = get_object(User, id=user_id) + # user = {} + # user.name = request.user.username + # user.username = request.user.username + # user.id = request.user.id login_types = {'L': 'LDAP', 'M': 'MAP'} - username = user.username + user = CustomUser(request) posts = user.get_asset() host_count = len(posts) new_posts = [] @@ -64,7 +157,6 @@ def index_cu(request): new_posts.append(post_five) post_five = [] new_posts.append(post_five) - return render_to_response('index_cu.html', locals(), context_instance=RequestContext(request)) @@ -193,7 +285,7 @@ def is_latest(): pass -def login(request): +def Login(request): """登录界面""" if request.session.get('username'): return HttpResponseRedirect('/') @@ -202,22 +294,20 @@ def login(request): else: username = request.POST.get('username') password = request.POST.get('password') - user_filter = User.objects.filter(username=username) - if user_filter: - user = user_filter[0] - if PyCrypt.md5_crypt(password) == user.password: + user = authenticate(username=username, password=password) + if user is not None: + if user.is_active: + login(request, user) request.session['user_id'] = user.id - user_filter.update(last_login=datetime.datetime.now()) if user.role == 'SU': request.session['role_id'] = 2 elif user.role == 'GA': request.session['role_id'] = 1 else: request.session['role_id'] = 0 - response = HttpResponseRedirect('/', ) - response.set_cookie('username', username, expires=604800) - response.set_cookie('seed', PyCrypt.md5_crypt(password), expires=604800) - return response + # response.set_cookie('username', username, expires=604800) + # response.set_cookie('seed', PyCrypt.md5_crypt(password), expires=604800) + return HttpResponseRedirect('/', ) else: error = '密码错误,请重新输入。' else: @@ -225,8 +315,8 @@ def login(request): return render_to_response('login.html', {'error': error}) -def logout(request): - request.session.delete() +def Logout(request): + logout(request) return HttpResponseRedirect('/login/') # diff --git a/juser/models.py b/juser/models.py index 67325ef7f..58417650c 100644 --- a/juser/models.py +++ b/juser/models.py @@ -1,4 +1,4 @@ -#coding: utf-8 +# coding: utf-8 from django.db import models @@ -19,6 +19,21 @@ class UserGroup(models.Model): self.save() +from django.contrib.auth.models import AbstractUser + +class CustomUser(AbstractUser): + USER_ROLE_CHOICES = ( + ('SU', 'SuperUser'), + ('GA', 'GroupAdmin'), + ('CU', 'CommonUser'), + ) + name = models.CharField(max_length=80) + uuid = models.CharField(max_length=100) + role = models.CharField(max_length=2, choices=USER_ROLE_CHOICES, default='CU') + group = models.ManyToManyField(UserGroup) + ssh_key_pwd = models.CharField(max_length=200) + + class User(models.Model): USER_ROLE_CHOICES = ( ('SU', 'SuperUser'), @@ -137,5 +152,3 @@ class AdminGroup(models.Model): def __unicode__(self): return '%s: %s' % (self.user.username, self.group.name) - - diff --git a/templates/index_cu.html b/templates/index_cu.html index b52f2b4dc..3edb68a75 100644 --- a/templates/index_cu.html +++ b/templates/index_cu.html @@ -72,7 +72,7 @@
- {{ user.name }} + {{ user.username }}
@@ -109,7 +109,7 @@ 角色 - {{ user.id | get_role }} + {{ user.role }} Email From 7a94724dbcac502e5a0243e69c6fc209b6a19f52 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Thu, 24 Sep 2015 09:43:28 +0800 Subject: [PATCH 02/48] initial_data_test --- docs/initial_data.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/initial_data.yaml diff --git a/docs/initial_data.yaml b/docs/initial_data.yaml new file mode 100644 index 000000000..38a1cf887 --- /dev/null +++ b/docs/initial_data.yaml @@ -0,0 +1,10 @@ +- model: IDC.person + pk: 1 + fields: + first_name: John + last_name: Lennon +- model: myapp.person + pk: 2 + fields: + first_name: Paul + last_name: McCartney \ No newline at end of file From 11b3cee346b18fdbbd9c3dccfff2717beadd1622 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Sat, 3 Oct 2015 22:50:05 +0800 Subject: [PATCH 03/48] gitignore --- .gitignore | 3 ++- static/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 static/.DS_Store diff --git a/.gitignore b/.gitignore index b749de2d9..48fa35e3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ *.py[cod] .idea test.py - +.DS_Store +db.sqlite3 # C extensions *.so diff --git a/static/.DS_Store b/static/.DS_Store deleted file mode 100644 index 1f949c21808d87add5ebb949967a44aac6745b10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKUrXaa5Z|r6Gu4+IcZ%T4z6yQl2}iuQ81+#oL@J`vOGvn`grqd7c{Z^ z*AV>@eiC1u+1(bUcW;MCnF+JM+1Z(0_Ls1eWsGsJAAM)cW{g>&h?N>Ne-RuWz(RkvOx$39S^HyzP5g+@@!m;1U z27VC~{oD(BhpH!|I5c)MC^}<%FUyjN9fU0z58C#(tt>49nZ~_WsYm-Vjk1B4_0uRn z^7VZCGAcT4d#TxrPa(A#q;X5;GL(rd{#Z49+V=8rxbFO{i^ixfhR()%T{w<28rf%- zwYv6WbN8S-I={HQy1u!+yMNI9bNE!1d^I?RM=%r>r+zO@vh+9f$FbuWLSldzAO>cF z0du%ni!-qs+5|B`4E#?9@O%)Uh_1m>qdGdE!QV$5uOgy=jduw|VbC>LYJ?FGu2TVZ zDmPaQuG7IUOq^@5)Tq-LS1ZFjW@T<}C|s=$exbq{cQsN=3=jiv8JN>u8|(khzwiHV zlc+}w5Cb#C0IzQQ+a7Gm)YhrZVXYOQzd%tiuGBb50YjByh{aO80;&Z30u4adV5t#2 QAoL@kXrP7|cvl8q08m3=$N&HU From 5a5928483f719f72f8d91e70bf822957a772aff9 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Sun, 4 Oct 2015 00:53:01 +0800 Subject: [PATCH 04/48] lots --- docs/initial_data.yaml | 30 +++++++++--- jasset/models.py | 2 +- jlog/views.py | 7 +-- jumpserver/api.py | 37 +++++--------- jumpserver/settings.py | 49 +++++++++++++----- jumpserver/views.py | 2 +- juser/models.py | 109 +---------------------------------------- juser/user_api.py | 1 + juser/views.py | 20 +++++--- manage.py | 0 10 files changed, 90 insertions(+), 167 deletions(-) mode change 100644 => 100755 manage.py diff --git a/docs/initial_data.yaml b/docs/initial_data.yaml index 38a1cf887..665a7744a 100644 --- a/docs/initial_data.yaml +++ b/docs/initial_data.yaml @@ -1,10 +1,28 @@ -- model: IDC.person +- model: juser.user + pk: 5000 + fields: + username: admin + name: admin + password: pbkdf2_sha256$20000$jBIDGPB2j5JT$orxqGgzzjzykColYm1BswPjgHOiERjZkcgkuVIkD2Hc= + email: admin@jumpserver.org + role: SU + is_active: 1 +- model: juser.user + pk: 5001 + fields: + username: group_admin + name: group_admin + password: pbkdf2_sha256$20000$ttObUWd15q10$NJoyZf2OZz9oiw2g4j2TkTh9zGgyVDRFdUkhn8X0nB0= + email: group_admin@jumpserver.org + role: DA + is_active: 1 +- model: juser.usergroup pk: 1 fields: - first_name: John - last_name: Lennon -- model: myapp.person + name: ALL + comment: ALL +- model: juser.usergroup pk: 2 fields: - first_name: Paul - last_name: McCartney \ No newline at end of file + name: 默认 + comment: 默认 diff --git a/jasset/models.py b/jasset/models.py index 8f51f8989..0d11ba753 100644 --- a/jasset/models.py +++ b/jasset/models.py @@ -65,7 +65,7 @@ class Asset(models.Model): username = models.CharField(max_length=20, blank=True, null=True) password = models.CharField(max_length=80, blank=True, null=True) use_default_auth = models.BooleanField(default=True) - date_added = models.DateTimeField(auto_now=True, default=datetime.datetime.now(), null=True) + date_added = models.DateTimeField(auto_now_add=True) is_active = models.BooleanField(default=True) comment = models.CharField(max_length=100, blank=True, null=True) diff --git a/jlog/views.py b/jlog/views.py index 0eb74f815..88d325eea 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -6,10 +6,8 @@ from django.shortcuts import render_to_response from jumpserver.api import * from jasset.views import httperror from django.http import HttpResponseNotFound - -CONF = ConfigParser() -CONF.read('%s/jumpserver.conf' % BASE_DIR) - +from models import Log +from jumpserver.settings import web_socket_host def get_user_info(request, offset): """ 获取用户信息及环境 """ @@ -58,7 +56,6 @@ def log_list(request, offset): """ 显示日志 """ header_title, path1, path2 = u'查看日志', u'查看日志', u'在线用户' keyword = request.GET.get('keyword', '') - web_socket_host = CONF.get('websocket', 'web_socket_host') posts = get_user_log(get_user_info(request, offset)) contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) diff --git a/jumpserver/api.py b/jumpserver/api.py index 59411abe4..9daf8b62e 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -1,8 +1,6 @@ # coding: utf-8 import os, sys, time -from ConfigParser import ConfigParser -import getpass from Crypto.Cipher import AES import crypt from binascii import b2a_hex, a2b_hex @@ -11,14 +9,15 @@ import datetime import random import subprocess import paramiko -import struct, fcntl, signal,socket, select, fnmatch +import struct, fcntl, signal, socket, select, fnmatch +from settings import JLOG_FILE, KEY, URL, log_dir, log_level 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 jasset.models import Asset, AssetGroup -from jlog.models import Log +# from jlog.models import Log from jasset.models import AssetAlias from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.http import HttpResponseRedirect @@ -36,22 +35,6 @@ except ImportError: sys.exit() -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') -JLOG_FILE = os.path.join(LOG_DIR, 'jumpserver.log') -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') -URL = CONF.get('base', 'url') -MAIL_ENABLE = CONF.get('mail', 'mail_enable') -MAIL_FROM = CONF.get('mail', 'email_host_user') -log_dir = os.path.join(BASE_DIR, 'logs') - - def set_log(level): """ return a log file object @@ -146,7 +129,7 @@ def page_list_return(total, current=1): 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) + return range(min_page, max_page + 1) def pages(post_objects, request): @@ -186,6 +169,7 @@ class Jtty(object): A virtual tty class 一个虚拟终端类,实现连接ssh和记录日志 """ + def __init__(self, user, asset): self.chan = None self.username = user.username @@ -404,7 +388,7 @@ class PyCrypt(object): symbol = '!@$%^&*()_' salt_list = [] if especial: - for i in range(length-4): + for i in range(length - 4): salt_list.append(random.choice(salt_key)) for i in range(4): salt_list.append(random.choice(symbol)) @@ -489,6 +473,7 @@ 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): if role == 'user': @@ -501,7 +486,9 @@ def require_role(role='user'): if request.session.get('role_id', 0) < 2: return HttpResponseRedirect('/') return func(request, *args, **kwargs) + return __deco + return _deco @@ -584,7 +571,7 @@ def validate(request, user_group=None, user=None, asset_group=None, asset=None, if edept: if dept.id != int(edept[0]): return False - + if user_group: dept_user_groups = dept.usergroup_set.all() user_group_ids = [] @@ -710,6 +697,4 @@ CRYPTOR = PyCrypt(KEY) # ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) # else: # ldap_conn = None - -log_level = CONF.get('base', 'log') -logger = set_log(log_level) \ No newline at end of file +logger = set_log(log_level) diff --git a/jumpserver/settings.py b/jumpserver/settings.py index 7d77b4ff4..3d0977dbd 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/1.7/ref/settings/ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os import ConfigParser +import getpass config = ConfigParser.ConfigParser() @@ -22,7 +23,7 @@ DB_PORT = config.getint('db', 'port') DB_USER = config.get('db', 'user') DB_PASSWORD = config.get('db', 'password') DB_DATABASE = config.get('db', 'database') -AUTH_USER_MODEL = 'juser.CustomUser' +AUTH_USER_MODEL = 'juser.User' # mail config EMAIL_HOST = config.get('mail', 'email_host') EMAIL_PORT = config.get('mail', 'email_port') @@ -30,6 +31,24 @@ EMAIL_HOST_USER = config.get('mail', 'email_host_user') EMAIL_HOST_PASSWORD = config.get('mail', 'email_host_password') EMAIL_USE_TLS = config.getboolean('mail', 'email_use_tls') +# ======== Log ========== +LOG = False +LOG_DIR = os.path.join(BASE_DIR, 'logs') +JLOG_FILE = os.path.join(LOG_DIR, 'jumpserver.log') +SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys') +# SERVER_KEY_DIR = os.path.join(SSH_KEY_DIR, 'server') +KEY = config.get('base', 'key') +LOGIN_NAME = getpass.getuser() +# LDAP_ENABLE = CONF.getint('ldap', 'ldap_enable') +URL = config.get('base', 'url') +MAIL_ENABLE = config.get('mail', 'mail_enable') +MAIL_FROM = config.get('mail', 'email_host_user') +log_dir = os.path.join(BASE_DIR, 'logs') + +log_level = config.get('base', 'log') + +web_socket_host = config.get('websocket', 'web_socket_host') + # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ @@ -64,9 +83,9 @@ INSTALLED_APPS = ( MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', - #'django.middleware.csrf.CsrfViewMiddleware', + # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - #'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + # 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ) @@ -79,17 +98,23 @@ WSGI_APPLICATION = 'jumpserver.wsgi.application' # Database # https://docs.djangoproject.com/en/1.7/ref/settings/#databases +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.mysql', +# 'NAME': DB_DATABASE, +# 'USER': DB_USER, +# 'PASSWORD': DB_PASSWORD, +# 'HOST': DB_HOST, +# 'PORT': DB_PORT, +# } +# } + DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': DB_DATABASE, - 'USER': DB_USER, - 'PASSWORD': DB_PASSWORD, - 'HOST': DB_HOST, - 'PORT': DB_PORT, + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } - TEMPLATE_CONTEXT_PROCESSORS = ( 'django.contrib.auth.context_processors.auth', 'django.core.context_processors.debug', @@ -105,7 +130,7 @@ TEMPLATE_DIRS = ( os.path.join(BASE_DIR, 'templates'), ) -#STATIC_ROOT = os.path.join(BASE_DIR, 'static') +# STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), @@ -128,5 +153,3 @@ USE_TZ = False # https://docs.djangoproject.com/en/1.7/howto/static-files/ STATIC_URL = '/static/' - - diff --git a/jumpserver/views.py b/jumpserver/views.py index a45f904f1..d628a640f 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -14,7 +14,7 @@ import paramiko from jumpserver.api import * from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required - +from settings import BASE_DIR def getDaysByNum(num): today = datetime.date.today() diff --git a/juser/models.py b/juser/models.py index 58417650c..6143cab78 100644 --- a/juser/models.py +++ b/juser/models.py @@ -21,7 +21,7 @@ class UserGroup(models.Model): from django.contrib.auth.models import AbstractUser -class CustomUser(AbstractUser): +class User(AbstractUser): USER_ROLE_CHOICES = ( ('SU', 'SuperUser'), ('GA', 'GroupAdmin'), @@ -34,113 +34,6 @@ class CustomUser(AbstractUser): ssh_key_pwd = models.CharField(max_length=200) -class User(models.Model): - USER_ROLE_CHOICES = ( - ('SU', 'SuperUser'), - ('GA', 'GroupAdmin'), - ('CU', 'CommonUser'), - ) - username = models.CharField(max_length=80, unique=True) - password = models.CharField(max_length=100) - name = models.CharField(max_length=80) - email = models.EmailField(max_length=75) - role = models.CharField(max_length=2, choices=USER_ROLE_CHOICES, default='CU') - uuid = models.CharField(max_length=100) - group = models.ManyToManyField(UserGroup) - ssh_key_pwd = models.CharField(max_length=200) - is_active = models.BooleanField(default=True) - last_login = models.DateTimeField(null=True) - date_joined = models.DateTimeField(null=True) - - def __unicode__(self): - return self.username - - def get_asset_group(self): - """ - Get user host_groups. - 获取用户有权限的主机组 - """ - host_group_list = [] - perm_list = [] - user_group_all = self.group.all() - for user_group in user_group_all: - perm_list.extend(user_group.perm_set.all()) - - for perm in perm_list: - host_group_list.append(perm.asset_group) - - return host_group_list - - def get_asset_group_info(self, printable=False): - """ - Get or print asset group info - 获取或打印用户授权资产组 - """ - asset_groups_info = {} - asset_groups = self.get_asset_group() - - for asset_group in asset_groups: - asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] - - if printable: - for group_id in asset_groups_info: - if asset_groups_info[group_id][1]: - print "[%3s] %s -- %s" % (group_id, - asset_groups_info[group_id][0], - asset_groups_info[group_id][1]) - else: - print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) - print '' - else: - return asset_groups_info - - def get_asset(self): - """ - Get the assets of under the user control. - 获取主机列表 - """ - assets = [] - asset_groups = self.get_asset_group() - - for asset_group in asset_groups: - assets.extend(asset_group.asset_set.all()) - - return assets - - def get_asset_info(self, printable=False): - """ - Get or print the user asset info - 获取或打印用户资产信息 - """ - from jasset.models import AssetAlias - assets_info = {} - assets = self.get_asset() - - for asset in assets: - asset_alias = AssetAlias.objects.filter(user=self, asset=asset) - if asset_alias and asset_alias[0].alias != '': - assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] - else: - assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] - - if printable: - ips = assets_info.keys() - ips.sort() - for ip in ips: - if assets_info[ip][2]: - print '%-15s -- %s' % (ip, assets_info[ip][2]) - else: - print '%-15s' % ip - print '' - else: - return assets_info - - def update(self, **kwargs): - for key, value in kwargs.items(): - self.__setattr__(key, value) - self.save() - - class AdminGroup(models.Model): """ under the user control group diff --git a/juser/user_api.py b/juser/user_api.py index 04a638a0f..566f07f68 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -59,6 +59,7 @@ def db_add_user(**kwargs): admin_groups = kwargs.pop('admin_groups') role = kwargs.get('role', 'CU') user = User(**kwargs) + user.set_password(kwargs.get('password')) user.save() if groups_post: group_select = [] diff --git a/juser/views.py b/juser/views.py index 6cfd91ef1..d8c81cc8f 100644 --- a/juser/views.py +++ b/juser/views.py @@ -9,7 +9,7 @@ import uuid as uuid_r from django.db.models import Q from django.template import RequestContext from django.db.models import ObjectDoesNotExist - +from jumpserver.settings import MAIL_FROM, MAIL_ENABLE from juser.user_api import * @@ -240,8 +240,8 @@ def user_add(request): if '' in [username, password, ssh_key_pwd, name, role]: error = u'带*内容不能为空' raise ServerError - user_test = get_object(User, username=username) - if user_test: + check_user_is_exist = User.objects.filter(username=username) + if check_user_is_exist: error = u'用户 %s 已存在' % username raise ServerError @@ -250,10 +250,10 @@ def user_add(request): else: try: user = db_add_user(username=username, name=name, - password=CRYPTOR.md5_crypt(password), + password=password, email=email, role=role, uuid=uuid, groups=groups, admin_groups=admin_groups, - ssh_key_pwd=CRYPTOR.md5_crypt(ssh_key_pwd), + ssh_key_pwd=ssh_key_pwd, is_active=is_active, date_joined=datetime.datetime.now()) server_add_user(username, password, ssh_key_pwd, ssh_key_login_need) @@ -417,8 +417,14 @@ def user_detail(request): @require_role(role='admin') def user_del(request): - user_ids = request.GET.get('id', '') - user_id_list = user_ids.split(',') + if request.method == "GET": + user_ids = request.GET.get('id', '') + user_id_list = user_ids.split(',') + elif request.method == "POST": + user_ids = request.POST.get('id', '') + user_id_list = user_ids.split(',') + else: + return HttpResponse('错误请求') for user_id in user_id_list: User.objects.filter(id=user_id).delete() diff --git a/manage.py b/manage.py old mode 100644 new mode 100755 From 02218e624a0fd9144cdbaca86a1d9545650f6b29 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Sun, 4 Oct 2015 19:07:42 +0800 Subject: [PATCH 05/48] lots --- jasset/models.py | 4 ++-- jumpserver/settings.py | 2 +- jumpserver/views.py | 1 + juser/user_api.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/jasset/models.py b/jasset/models.py index 0d11ba753..e108d507c 100644 --- a/jasset/models.py +++ b/jasset/models.py @@ -59,8 +59,8 @@ class AssetGroup(models.Model): class Asset(models.Model): - ip = models.IPAddressField(unique=True) - port = models.IntegerField(max_length=6) + ip = models.GenericIPAddressField(unique=True) + port = models.IntegerField() group = models.ManyToManyField(AssetGroup) username = models.CharField(max_length=20, blank=True, null=True) password = models.CharField(max_length=80, blank=True, null=True) diff --git a/jumpserver/settings.py b/jumpserver/settings.py index 3d0977dbd..6eb10c252 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -77,7 +77,7 @@ INSTALLED_APPS = ( 'juser', 'jasset', 'jperm', - # 'jlog', + 'jlog', ) MIDDLEWARE_CLASSES = ( diff --git a/jumpserver/views.py b/jumpserver/views.py index d628a640f..eed1d4f4f 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -15,6 +15,7 @@ from jumpserver.api import * from django.contrib.auth import authenticate, login, logout from django.contrib.auth.decorators import login_required from settings import BASE_DIR +from jlog.models import Log def getDaysByNum(num): today = datetime.date.today() diff --git a/juser/user_api.py b/juser/user_api.py index 566f07f68..b1320d32c 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -4,7 +4,7 @@ from Crypto.PublicKey import RSA from juser.models import AdminGroup from jumpserver.api import * - +from jumpserver.settings import BASE_DIR def group_add_user(group, user_id=None, username=None): """ From cee8b2d78786e33acb2f90534a7f94a1510f1819 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Wed, 7 Oct 2015 12:01:14 +0800 Subject: [PATCH 06/48] rollback jumpserver/views.py 54 : user = get_object() part and delete useless 'class CustomUser' --- jumpserver/views.py | 98 ++------------------------------------------- juser/models.py | 1 + 2 files changed, 4 insertions(+), 95 deletions(-) diff --git a/jumpserver/views.py b/jumpserver/views.py index eed1d4f4f..b9e547a0c 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -48,105 +48,13 @@ def get_data(data, items, option): dic[name] = li return dic -class CustomUser(object): - def __init__(self,request): - self.requset = request - def __unicode__(self): - return self.requset.user.username - - def get_asset_group(self): - """ - Get user host_groups. - 获取用户有权限的主机组 - """ - host_group_list = [] - perm_list = [] - user_group_all = self.requset.user.group.all() - for user_group in user_group_all: - perm_list.extend(user_group.perm_set.all()) - - for perm in perm_list: - host_group_list.append(perm.asset_group) - - return host_group_list - - def get_asset_group_info(self, printable=False): - """ - Get or print asset group info - 获取或打印用户授权资产组 - """ - asset_groups_info = {} - asset_groups = self.get_asset_group() - - for asset_group in asset_groups: - asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] - - if printable: - for group_id in asset_groups_info: - if asset_groups_info[group_id][1]: - print "[%3s] %s -- %s" % (group_id, - asset_groups_info[group_id][0], - asset_groups_info[group_id][1]) - else: - print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) - print '' - else: - return asset_groups_info - - def get_asset(self): - """ - Get the assets of under the user control. - 获取主机列表 - """ - assets = [] - asset_groups = self.get_asset_group() - - for asset_group in asset_groups: - assets.extend(asset_group.asset_set.all()) - - return assets - - def get_asset_info(self, printable=False): - """ - Get or print the user asset info - 获取或打印用户资产信息 - """ - from jasset.models import AssetAlias - assets_info = {} - assets = self.get_asset() - - for asset in assets: - asset_alias = AssetAlias.objects.filter(user=self, asset=asset) - if asset_alias and asset_alias[0].alias != '': - assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] - else: - assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] - - if printable: - ips = assets_info.keys() - ips.sort() - for ip in ips: - if assets_info[ip][2]: - print '%-15s -- %s' % (ip, assets_info[ip][2]) - else: - print '%-15s' % ip - print '' - else: - return assets_info - - -# @login_required @require_role(role='user') def index_cu(request): - # user_id = request.session.get('user_id') - # user = get_object(User, id=user_id) - # user = {} - # user.name = request.user.username - # user.username = request.user.username - # user.id = request.user.id + user_id = request.session.get('user_id') + user = get_object(User, id=user_id) login_types = {'L': 'LDAP', 'M': 'MAP'} - user = CustomUser(request) + username = user.username posts = user.get_asset() host_count = len(posts) new_posts = [] diff --git a/juser/models.py b/juser/models.py index 6143cab78..41dd705ad 100644 --- a/juser/models.py +++ b/juser/models.py @@ -21,6 +21,7 @@ class UserGroup(models.Model): from django.contrib.auth.models import AbstractUser + class User(AbstractUser): USER_ROLE_CHOICES = ( ('SU', 'SuperUser'), From c9e192fae46f46a80baf64b4ff478985c39e34f0 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Wed, 7 Oct 2015 12:17:20 +0800 Subject: [PATCH 07/48] recovery User's function --- juser/models.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 3 deletions(-) diff --git a/juser/models.py b/juser/models.py index 41dd705ad..2d38e3412 100644 --- a/juser/models.py +++ b/juser/models.py @@ -1,6 +1,7 @@ # coding: utf-8 from django.db import models +from django.contrib.auth.models import AbstractUser class UserGroup(models.Model): @@ -19,9 +20,6 @@ class UserGroup(models.Model): self.save() -from django.contrib.auth.models import AbstractUser - - class User(AbstractUser): USER_ROLE_CHOICES = ( ('SU', 'SuperUser'), @@ -34,6 +32,86 @@ class User(AbstractUser): group = models.ManyToManyField(UserGroup) ssh_key_pwd = models.CharField(max_length=200) + def __unicode__(self): + return self.username + + def get_asset_group(self): + """ + Get user host_groups. + 获取用户有权限的主机组 + """ + host_group_list = [] + perm_list = [] + user_group_all = self.group.all() + for user_group in user_group_all: + perm_list.extend(user_group.perm_set.all()) + for perm in perm_list: + host_group_list.append(perm.asset_group) + return host_group_list + + def get_asset_group_info(self, printable=False): + """ + Get or print asset group info + 获取或打印用户授权资产组 + """ + asset_groups_info = {} + asset_groups = self.get_asset_group() + for asset_group in asset_groups: + asset_groups_info[asset_group.id] = [asset_group.name, asset_group.comment] + if printable: + for group_id in asset_groups_info: + if asset_groups_info[group_id][1]: + print "[%3s] %s -- %s" % (group_id, + asset_groups_info[group_id][0], + asset_groups_info[group_id][1]) + else: + print "[%3s] %s" % (group_id, asset_groups_info[group_id][0]) + print '' + else: + return asset_groups_info + + def get_asset(self): + """ + Get the assets of under the user control. + 获取主机列表 + """ + assets = [] + asset_groups = self.get_asset_group() + for asset_group in asset_groups: + assets.extend(asset_group.asset_set.all()) + return assets + + def get_asset_info(self, printable=False): + """ + Get or print the user asset info + 获取或打印用户资产信息 + """ + from jasset.models import AssetAlias + assets_info = {} + assets = self.get_asset() + for asset in assets: + asset_alias = AssetAlias.objects.filter(user=self, asset=asset) + if asset_alias and asset_alias[0].alias != '': + assets_info[asset.ip] = [asset.id, asset.ip, str(asset_alias[0].alias)] + else: + assets_info[asset.ip] = [asset.id, asset.ip, str(asset.comment)] + if printable: + ips = assets_info.keys() + ips.sort() + for ip in ips: + if assets_info[ip][2]: + print '%-15s -- %s' % (ip, assets_info[ip][2]) + else: + print '%-15s' % ip + print '' + else: + return assets_info + + def update(self, **kwargs): + for key, value in kwargs.items(): + self.__setattr__(key, value) + self.save() + class AdminGroup(models.Model): """ From 85dcc8ba632e62e3518b38ff518ac3989ea856bc Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Wed, 7 Oct 2015 12:20:59 +0800 Subject: [PATCH 08/48] WARNINGS: jlog.Log.pid: (fields.W122) 'max_length' is ignored when used with IntegerField HINT: Remove 'max_length' from field --- jlog/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jlog/models.py b/jlog/models.py index baaffb5a7..cf13a5354 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -8,7 +8,7 @@ class Log(models.Model): dept_name = models.CharField(max_length=20) log_path = models.CharField(max_length=100) start_time = models.DateTimeField(null=True) - pid = models.IntegerField(max_length=10) + pid = models.IntegerField() is_finished = models.BooleanField(default=False) handle_finished = models.BooleanField(default=False) end_time = models.DateTimeField(null=True) From ce904726244aadfcd70c54de3b0538c0ac8f231c Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Wed, 7 Oct 2015 12:31:40 +0800 Subject: [PATCH 09/48] juser/user_list change user password fix --- juser/user_api.py | 4 ++-- juser/views.py | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/juser/user_api.py b/juser/user_api.py index b1320d32c..f90cbbaf9 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -84,10 +84,10 @@ def db_update_user(**kwargs): groups_post = kwargs.pop('groups') admin_groups_post = kwargs.pop('admin_groups') user_id = kwargs.pop('user_id') - user = User.objects.filter(id=user_id) + user = User.objects.get(id=user_id) if user: user.update(**kwargs) - user = user[0] + user.set_password(kwargs.pop('password')) user.save() else: return None diff --git a/juser/views.py b/juser/views.py index d8c81cc8f..b91f9de17 100644 --- a/juser/views.py +++ b/juser/views.py @@ -532,11 +532,11 @@ def user_edit(request): else: return HttpResponseRedirect('/juser/user_list/') - if password != user.password: - password_decode = password - password = CRYPTOR.md5_crypt(password) - else: - password_decode = None + # if password != user.password: + # password_decode = password + # password = CRYPTOR.md5_crypt(password) + # else: + # password_decode = None db_update_user(user_id=user_id, password=password, From 9d5c4dbb33c91497e88b0b791ee31641531e778c Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Wed, 7 Oct 2015 12:36:20 +0800 Subject: [PATCH 10/48] juser/user_list change user password fix --- juser/views.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/juser/views.py b/juser/views.py index b91f9de17..6e7688d96 100644 --- a/juser/views.py +++ b/juser/views.py @@ -2,8 +2,8 @@ # Author: Guanghongwei # Email: ibuler@qq.com -import random -from Crypto.PublicKey import RSA +# import random +# from Crypto.PublicKey import RSA import uuid as uuid_r from django.db.models import Q @@ -650,10 +650,11 @@ def change_info(request): error = '密码须大于6位' if not error: - if password != user.password: - password = CRYPTOR.md5_crypt(password) + # if password != user.password: + # password = CRYPTOR.md5_crypt(password) - user.update(name=name, password=password, email=email) + user.update(name=name, email=email) + user.set_password(password) msg = '修改成功' return render_to_response('juser/change_info.html', locals(), context_instance=RequestContext(request)) From 9366003f7bc574037d2df9a02be799fb3b380d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B9=BF=E5=AE=8F=E4=BC=9F?= Date: Mon, 26 Oct 2015 22:17:16 +0800 Subject: [PATCH 11/48] jlog --- connect.py | 71 ------------ jlog/log_api.py | 68 +++++++++++ jlog/models.py | 2 - jlog/urls.py | 6 +- jlog/views.py | 184 ++++++++++++++--------------- jumpserver/api.py | 198 +++++++++++++------------------- jumpserver/settings.py | 2 +- jumpserver/urls.py | 2 +- templates/jlog/base.jinja2 | 111 ++++++++++++++++++ templates/jlog/dynamic.jinja2 | 11 ++ templates/jlog/log_offline.html | 10 +- templates/jlog/log_online.html | 8 -- templates/jlog/static.jinja2 | 18 +++ 13 files changed, 379 insertions(+), 312 deletions(-) create mode 100644 jlog/log_api.py create mode 100644 templates/jlog/base.jinja2 create mode 100644 templates/jlog/dynamic.jinja2 create mode 100644 templates/jlog/static.jinja2 diff --git a/connect.py b/connect.py index 7994205ec..66268d50c 100644 --- a/connect.py +++ b/connect.py @@ -19,7 +19,6 @@ if django.get_version() != '1.6': django.setup() from jumpserver.api import ServerError, User, Asset, Jtty, get_object from jumpserver.api import logger -from jumpserver.api import BisGroup as AssetGroup login_user = get_object(User, username=getpass.getuser()) @@ -98,76 +97,6 @@ def print_prompt(): print textwrap.dedent(msg) -# def remote_exec_cmd(ip, port, username, password, cmd): -# try: -# time.sleep(5) -# ssh = paramiko.SSHClient() -# ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) -# ssh.connect(ip, port, username, password, timeout=5) -# stdin, stdout, stderr = ssh.exec_command("bash -l -c '%s'" % cmd) -# out = stdout.readlines() -# err = stderr.readlines() -# color_print('%s:' % ip, 'blue') -# for i in out: -# color_print(" " * 4 + i.strip(), 'green') -# for j in err: -# color_print(" " * 4 + j.strip(), 'red') -# ssh.close() -# except Exception as e: -# color_print(ip + ':', 'blue') -# color_print(str(e), 'red') - - -# def multi_remote_exec_cmd(hosts, username, cmd): -# pool = Pool(processes=5) -# for host in hosts: -# username, password, ip, port = get_connect_item(username, host) -# pool.apply_async(remote_exec_cmd, (ip, port, username, password, cmd)) -# pool.close() -# pool.join() - - -# def exec_cmd_servers(username): -# color_print("You can choose in the following IP(s), Use glob or ips split by comma. q/Q to PreLayer.", 'green') -# user.get_asset_info(printable=True) -# while True: -# hosts = [] -# inputs = raw_input('\033[1;32mip(s)>: \033[0m') -# if inputs in ['q', 'Q']: -# break -# get_hosts = login_user.get_asset_info().keys() -# -# if ',' in inputs: -# ips_input = inputs.split(',') -# for host in ips_input: -# if host in get_hosts: -# hosts.append(host) -# else: -# for host in get_hosts: -# if fnmatch.fnmatch(host, inputs): -# hosts.append(host.strip()) -# -# if len(hosts) == 0: -# color_print("Check again, Not matched any ip!", 'red') -# continue -# else: -# print "You matched ip: %s" % hosts -# color_print("Input the Command , The command will be Execute on servers, q/Q to quit.", 'green') -# while True: -# cmd = raw_input('\033[1;32mCmd(s): \033[0m') -# if cmd in ['q', 'Q']: -# break -# exec_log_dir = os.path.join(log_dir, 'exec_cmds') -# if not os.path.isdir(exec_log_dir): -# os.mkdir(exec_log_dir) -# os.chmod(exec_log_dir, 0777) -# filename = "%s/%s.log" % (exec_log_dir, time.strftime('%Y%m%d')) -# f = open(filename, 'a') -# f.write("DateTime: %s User: %s Host: %s Cmds: %s\n" % -# (time.strftime('%Y/%m/%d %H:%M:%S'), username, hosts, cmd)) -# multi_remote_exec_cmd(hosts, username, cmd) - - def main(): """ he he diff --git a/jlog/log_api.py b/jlog/log_api.py new file mode 100644 index 000000000..c9d54149a --- /dev/null +++ b/jlog/log_api.py @@ -0,0 +1,68 @@ +# coding: utf-8 + + +from argparse import ArgumentParser, FileType +from contextlib import closing +from codecs import open as copen +from json import dumps +from math import ceil +from os.path import basename, dirname, exists, join +from struct import unpack +from subprocess import Popen +from sys import platform, prefix, stderr +from tempfile import NamedTemporaryFile + +from jinja2 import FileSystemLoader, Template +from jinja2.environment import Environment + +from jumpserver.api import BASE_DIR + + +DEFAULT_TEMPLATE = join(BASE_DIR, 'templates', 'jlog', 'static.jinja2') + + +def escapeString(string): + string = string.encode('unicode_escape').decode('utf-8') + string = string.replace("'", "\\'") + string = '\'' + string + '\'' + return string + + +def getTiming(timef): + timing = None + with closing(timef): + timing = [l.strip().split(' ') for l in timef] + timing = [(int(ceil(float(r[0]) * 1000)), int(r[1])) for r in timing] + return timing + + +def scriptToJSON(scriptf, timing=None): + ret = [] + + with closing(scriptf): + scriptf.readline() # ignore first header line from script file + offset = 0 + for t in timing: + data = escapeString(scriptf.read(t[1])) + offset += t[0] + ret.append((data, offset)) + return dumps(ret) + + +def renderTemplate(script_path, time_file_path, dimensions=(24, 60), templatename=DEFAULT_TEMPLATE): + with copen(script_path, encoding='utf-8', errors='replace') as scriptf: + with open(time_file_path) as timef: + timing = getTiming(timef) + json = scriptToJSON(scriptf, timing) + + fsl = FileSystemLoader(dirname(templatename), 'utf-8') + e = Environment() + e.loader = fsl + + templatename = basename(templatename) + rendered = e.get_template(templatename).render(json=json, + dimensions=dimensions) + + return rendered + + diff --git a/jlog/models.py b/jlog/models.py index baaffb5a7..9398ef45e 100644 --- a/jlog/models.py +++ b/jlog/models.py @@ -5,12 +5,10 @@ class Log(models.Model): user = models.CharField(max_length=20, null=True) host = models.CharField(max_length=20, null=True) remote_ip = models.CharField(max_length=100) - dept_name = models.CharField(max_length=20) log_path = models.CharField(max_length=100) start_time = models.DateTimeField(null=True) pid = models.IntegerField(max_length=10) is_finished = models.BooleanField(default=False) - handle_finished = models.BooleanField(default=False) end_time = models.DateTimeField(null=True) def __unicode__(self): diff --git a/jlog/urls.py b/jlog/urls.py index 0b6810d3c..24d821f12 100644 --- a/jlog/urls.py +++ b/jlog/urls.py @@ -5,7 +5,7 @@ from jlog.views import * urlpatterns = patterns('', url(r'^$', log_list), url(r'^log_list/(\w+)/$', log_list), - url(r'^log_kill/', log_kill), - url(r'^history/$', log_history), - url(r'^search/$', log_search), + # url(r'^log_kill/', log_kill), + # url(r'^history/$', log_history), + # url(r'^search/$', log_search), ) \ No newline at end of file diff --git a/jlog/views.py b/jlog/views.py index 0eb74f815..187802a7b 100644 --- a/jlog/views.py +++ b/jlog/views.py @@ -4,117 +4,103 @@ from django.template import RequestContext from django.shortcuts import render_to_response from jumpserver.api import * -from jasset.views import httperror from django.http import HttpResponseNotFound CONF = ConfigParser() CONF.read('%s/jumpserver.conf' % BASE_DIR) +from jlog.models import Log + +# def get_user_info(request, offset): +# """ 获取用户信息及环境 """ +# env_dic = {'online': 0, 'offline': 1} +# env = env_dic[offset] +# keyword = request.GET.get('keyword', '') +# user_info = get_session_user_info(request) +# user_id, username = user_info[0:2] +# dept_id, dept_name = user_info[3:5] +# ret = [request, keyword, env, username, dept_name] +# +# return ret +# +# +# def get_user_log(ret_list): +# """ 获取不同类型用户日志记录 """ +# request, keyword, env, username, dept_name = ret_list +# post_all = Log.objects.filter(is_finished=env).order_by('-start_time') +# post_keyword_all = Log.objects.filter(Q(user__contains=keyword) | +# Q(host__contains=keyword)) \ +# .filter(is_finished=env).order_by('-start_time') +# +# if keyword: +# posts = post_keyword_all +# else: +# posts = post_all +# +# return posts -def get_user_info(request, offset): - """ 获取用户信息及环境 """ - env_dic = {'online': 0, 'offline': 1} - env = env_dic[offset] - keyword = request.GET.get('keyword', '') - user_info = get_session_user_info(request) - user_id, username = user_info[0:2] - dept_id, dept_name = user_info[3:5] - ret = [request, keyword, env, username, dept_name] - - return ret - - -def get_user_log(ret_list): - """ 获取不同类型用户日志记录 """ - request, keyword, env, username, dept_name = ret_list - post_all = Log.objects.filter(is_finished=env).order_by('-start_time') - post_keyword_all = Log.objects.filter(Q(user__contains=keyword) | - Q(host__contains=keyword)) \ - .filter(is_finished=env).order_by('-start_time') - - if is_super_user(request): - if keyword: - posts = post_keyword_all - else: - posts = post_all - - elif is_group_admin(request): - if keyword: - posts = post_keyword_all.filter(dept_name=dept_name) - else: - posts = post_all.filter(dept_name=dept_name) - - elif is_common_user(request): - if keyword: - posts = post_keyword_all.filter(user=username) - else: - posts = post_all.filter(user=username) - - return posts - - -@require_login def log_list(request, offset): """ 显示日志 """ header_title, path1, path2 = u'查看日志', u'查看日志', u'在线用户' keyword = request.GET.get('keyword', '') web_socket_host = CONF.get('websocket', 'web_socket_host') - posts = get_user_log(get_user_info(request, offset)) + # posts = get_user_log(get_user_info(request, offset)) + if offset == 'online': + posts = Log.objects.filter(is_finished=False).order_by('-start_time') + else: + posts = Log.objects.filter(is_finished=True).order_by('-start_time') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) return render_to_response('jlog/log_%s.html' % offset, locals(), context_instance=RequestContext(request)) - -@require_admin -def log_kill(request): - """ 杀掉connect进程 """ - pid = request.GET.get('id', '') - log = Log.objects.filter(pid=pid) - if log: - log = log[0] - 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失败, 您无权操作!') - try: - os.kill(int(pid), 9) - except OSError: - pass - Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.datetime.now()) - return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request)) - else: - return HttpResponseNotFound(u'没有此进程!') - - -@require_login -def log_history(request): - """ 命令历史记录 """ - log_id = request.GET.get('id', 0) - log = Log.objects.filter(id=int(log_id)) - if log: - log = log[0] - dept_name = log.dept_name - deptname = get_session_user_info(request)[4] - if is_group_admin(request) and dept_name != deptname: - return httperror(request, '查看失败, 您无权查看!') - - elif is_common_user(request): - return httperror(request, '查看失败, 您无权查看!') - - log_his = "%s.his" % log.log_path - if os.path.isfile(log_his): - f = open(log_his) - content = f.read() - return HttpResponse(content) - else: - return httperror(request, '无日志记录, 请查看日志处理脚本是否开启!') - - -@require_login -def log_search(request): - """ 日志搜索 """ - offset = request.GET.get('env', '') - keyword = request.GET.get('keyword', '') - posts = get_user_log(get_user_info(request, offset)) - contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - return render_to_response('jlog/log_search.html', locals(), context_instance=RequestContext(request)) +# +# def log_kill(request): +# """ 杀掉connect进程 """ +# pid = request.GET.get('id', '') +# log = Log.objects.filter(pid=pid) +# if log: +# log = log[0] +# 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失败, 您无权操作!') +# try: +# os.kill(int(pid), 9) +# except OSError: +# pass +# Log.objects.filter(pid=pid).update(is_finished=1, end_time=datetime.datetime.now()) +# return render_to_response('jlog/log_offline.html', locals(), context_instance=RequestContext(request)) +# else: +# return HttpResponseNotFound(u'没有此进程!') +# +# +# def log_history(request): +# """ 命令历史记录 """ +# log_id = request.GET.get('id', 0) +# log = Log.objects.filter(id=int(log_id)) +# if log: +# log = log[0] +# dept_name = log.dept_name +# deptname = get_session_user_info(request)[4] +# if is_group_admin(request) and dept_name != deptname: +# return httperror(request, '查看失败, 您无权查看!') +# +# elif is_common_user(request): +# return httperror(request, '查看失败, 您无权查看!') +# +# log_his = "%s.his" % log.log_path +# if os.path.isfile(log_his): +# f = open(log_his) +# content = f.read() +# return HttpResponse(content) +# else: +# return httperror(request, '无日志记录, 请查看日志处理脚本是否开启!') +# +# +# def log_search(request): +# """ 日志搜索 """ +# offset = request.GET.get('env', '') +# keyword = request.GET.get('keyword', '') +# posts = get_user_log(get_user_info(request, offset)) +# contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) +# return render_to_response('jlog/log_search.html', locals(), context_instance=RequestContext(request)) diff --git a/jumpserver/api.py b/jumpserver/api.py index faabb068c..b4ee56595 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -12,6 +12,7 @@ import random import subprocess import paramiko import struct, fcntl, signal,socket, select, fnmatch +import re from django.core.paginator import Paginator, EmptyPage, InvalidPage from django.http import HttpResponse, Http404 @@ -69,75 +70,6 @@ def set_log(level): return logger_f -# class LDAPMgmt(): -# """ -# LDAP class for add, select, del, update -# LDAP 管理类,增删改查 -# """ -# 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): -# """ -# query -# 查询 -# """ -# 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): -# """ -# add -# 添加 -# """ -# try: -# ldif = modlist.addModlist(attrs) -# self.conn.add_s(dn, ldif) -# except ldap.LDAPError, e: -# print e -# -# def modify(self, dn, attrs): -# """ -# modify -# 更改 -# """ -# 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): -# """ -# delete -# 删除 -# """ -# try: -# self.conn.delete_s(dn) -# except ldap.LDAPError, e: -# print e - - def page_list_return(total, current=1): """ page @@ -181,17 +113,46 @@ def pages(post_objects, request): return post_objects, paginator, page_objects, page_range, current_page, show_first, show_end +def remove_control_char(str_r): + """ + 处理日志特殊字符 + """ + control_char = re.compile(r""" + \x1b[ #%()*+\-.\/]. | + \r | #匹配 回车符(CR) + (?:\x1b\[|\x9b) [ -?]* [@-~] | #匹配 控制顺序描述符(CSI)... Cmd + (?:\x1b\]|\x9d) .*? (?:\x1b\\|[\a\x9c]) | \x07 | #匹配 操作系统指令(OSC)...终止符或振铃符(ST|BEL) + (?:\x1b[P^_]|[\x90\x9e\x9f]) .*? (?:\x1b\\|\x9c) | #匹配 设备控制串或私讯或应用程序命令(DCS|PM|APC)...终止符(ST) + \x1b. #匹配 转义过后的字符 + [\x80-\x9f] #匹配 所有控制字符 + """, re.X) + backspace = re.compile(r"[^\b][\b]") + line_filtered = control_char.sub('', str_r.rstrip()) + while backspace.search(line_filtered): + line_filtered = backspace.sub('', line_filtered) + + return line_filtered + + +def newline_code_in(strings): + for i in ['\r', '\r\n', '\n']: + if i in strings: + #print "new line" + return True + return False + + class Jtty(object): """ A virtual tty class 一个虚拟终端类,实现连接ssh和记录日志 """ - def __init__(self, user, asset): + def __init__(self, username, ip): self.chan = None - self.username = user.username - self.ip = asset.ip - self.user = user - self.asset = asset + self.username = username + self.ip = ip + # self.user = user + # self.asset = asset @staticmethod def get_win_size(): @@ -227,11 +188,8 @@ class Jtty(object): timestamp_start = int(time.time()) date_start = time.strftime('%Y%m%d', time.localtime(timestamp_start)) time_start = time.strftime('%H%M%S', time.localtime(timestamp_start)) - log_filename = '%s_%s_%s.log' % (self.username, self.ip, time_start) today_connect_log_dir = os.path.join(tty_log_dir, date_start) - log_file_path = os.path.join(today_connect_log_dir, log_filename) - dept_name = self.user.dept.name - + log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.ip, time_start)) 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') @@ -242,23 +200,29 @@ class Jtty(object): raise ServerError('Create %s failed, Please modify %s permission.' % (today_connect_log_dir, tty_log_dir)) try: - log_file = open(log_file_path, 'a') + log_file_f = open(log_file_path + '.log', 'a') + log_time_f = open(log_file_path + '.time', 'a') + log_res_f = open(log_file_path + '.res', 'a') except IOError: raise ServerError('Create logfile failed, Please modify %s permission.' % today_connect_log_dir) - log = Log(user=self.username, host=self.ip, remote_ip=ip_list, dept_name=dept_name, + log = Log(user=self.username, host=self.ip, remote_ip=ip_list, log_path=log_file_path, start_time=datetime.datetime.now(), pid=pid) - log_file.write('Start time is %s\n' % datetime.datetime.now()) + log_file_f.write('Start time is %s\n' % datetime.datetime.now()) log.save() - return log_file, log + return log_file_f, log_time_f, log_res_f, log def posix_shell(self): """ Use paramiko channel connect server interactive. 使用paramiko模块的channel,连接后端,进入交互式 """ - log_file, log = self.log_record() + log_file_f, log_time_f, log_res_f, log = self.log_record() old_tty = termios.tcgetattr(sys.stdin) + pre_timestamp = time.time() + input_r = '' + input_mode = False + try: tty.setraw(sys.stdin.fileno()) tty.setcbreak(sys.stdin.fileno()) @@ -277,23 +241,40 @@ class Jtty(object): break sys.stdout.write(x) sys.stdout.flush() - log_file.write(x) - log_file.flush() + log_file_f.write(x) + now_timestamp = time.time() + log_time_f.write('%s %s\n' % (round(now_timestamp-pre_timestamp, 4), len(x))) + pre_timestamp = now_timestamp + log_file_f.flush() + log_time_f.flush() + + if input_mode and not newline_code_in(x): + input_r += x + except socket.timeout: pass if sys.stdin in r: x = os.read(sys.stdin.fileno(), 1) + if not input_mode: + input_mode = True + + if str(x) in ['\r', '\n', '\r\n']: + input_r = remove_control_char(input_r) + log_res_f.write('%s: %s\n' % (datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), input_r)) + log_res_f.flush() + input_r = '' + input_mode = False + if len(x) == 0: break self.chan.send(x) finally: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - log_file.write('End time is %s' % datetime.datetime.now()) - log_file.close() + log_file_f.write('End time is %s' % datetime.datetime.now()) + log_file_f.close() log.is_finished = True - log.handle_finished = False log.end_time = datetime.datetime.now() log.save() @@ -302,27 +283,15 @@ class Jtty(object): get args for connect: ip, port, username, passwd 获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码 """ - if not self.asset.is_active: - raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) + # if not self.asset.is_active: + # raise ServerError('该主机被禁用 Host %s is not active.' % self.ip) + # + # if not self.user.is_active: + # raise ServerError('该用户被禁用 User %s is not active.' % self.username) - if not self.user.is_active: - raise ServerError('该用户被禁用 User %s is not active.' % self.username) - - login_type_dict = { - 'L': self.user.ldap_pwd, - } - - if self.asset.login_type in login_type_dict: - password = CRYPTOR.decrypt(login_type_dict[self.asset.login_type]) - return self.username, password, self.ip, int(self.asset.port) - - elif self.asset.login_type == 'M': - username = self.asset.username - password = CRYPTOR.decrypt(self.asset.password) - return username, password, self.ip, int(self.asset.port) - - else: - raise ServerError('不支持的服务器登录方式 Login type is not in ["L", "M"]') + # password = CRYPTOR.decrypt(self.]) + # return self.username, password, self.ip, int(self.asset.port) + return 'root', 'redhat', '127.0.0.1', 22 def get_connection(self): """ @@ -337,7 +306,7 @@ class Jtty(object): ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: - ssh.connect(ip, port=port, username=username, password=password, compress=True) + ssh.connect(ip, port=port, username=username, password=password) except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: raise ServerError('认证错误 Authentication Error.') except socket.error: @@ -351,7 +320,7 @@ class Jtty(object): 连接服务器 """ ps1 = "PS1='[\u@%s \W]\$ '\n" % self.ip - login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.asset.ip + login_msg = "clear;echo -e '\\033[32mLogin %s done. Enjoy it.\\033[0m'\n" % self.ip # 发起ssh连接请求 Make a ssh connection ssh = self.get_connection() @@ -706,14 +675,5 @@ def my_render(template, data, request): CRYPTOR = PyCrypt(KEY) -# 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') -# ldap_conn = LDAPMgmt(LDAP_HOST_URL, LDAP_BASE_DN, LDAP_ROOT_DN, LDAP_ROOT_PW) -# else: -# ldap_conn = None - log_level = CONF.get('base', 'log') logger = set_log(log_level) \ No newline at end of file diff --git a/jumpserver/settings.py b/jumpserver/settings.py index 0a5a69bee..279320c92 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -62,7 +62,7 @@ INSTALLED_APPS = ( 'juser', 'jasset', 'jperm', - # 'jlog', + 'jlog', ) MIDDLEWARE_CLASSES = ( diff --git a/jumpserver/urls.py b/jumpserver/urls.py index 6c483ea5b..c6d6b4421 100644 --- a/jumpserver/urls.py +++ b/jumpserver/urls.py @@ -16,7 +16,7 @@ urlpatterns = patterns('', (r'^error/$', 'jumpserver.views.httperror'), (r'^juser/', include('juser.urls')), (r'^jasset/', include('jasset.urls')), - # (r'^jlog/', include('jlog.urls')), + (r'^jlog/', include('jlog.urls')), (r'^jperm/', include('jperm.urls')), (r'^node_auth/', 'jumpserver.views.node_auth'), diff --git a/templates/jlog/base.jinja2 b/templates/jlog/base.jinja2 new file mode 100644 index 000000000..926163d1d --- /dev/null +++ b/templates/jlog/base.jinja2 @@ -0,0 +1,111 @@ + + {% block head %}{% endblock %} + + + + + + + + + -5x +5x + + + diff --git a/templates/jlog/dynamic.jinja2 b/templates/jlog/dynamic.jinja2 new file mode 100644 index 000000000..8ddf2a8ac --- /dev/null +++ b/templates/jlog/dynamic.jinja2 @@ -0,0 +1,11 @@ +{% extends "base.jinja2" %} +{% block head %} + + + +{% endblock %} diff --git a/templates/jlog/log_offline.html b/templates/jlog/log_offline.html index 12bfb5af5..b949a0e18 100644 --- a/templates/jlog/log_offline.html +++ b/templates/jlog/log_offline.html @@ -39,12 +39,6 @@ - @@ -77,12 +71,12 @@ 用户名 - 所属部门 登录主机 来源IP {% ifnotequal session_role_id 0 %} 命令统计 {% endifnotequal %} + 回放录像 登录时间 结束时间 @@ -92,12 +86,12 @@ {% for post in contacts.object_list %} {{ post.user }} - {{ post.dept_name }} {{ post.host }} {{ post.remote_ip }} {% ifnotequal session_role_id 0 %} 命令统计 {% endifnotequal %} + 回放 {{ post.start_time|date:"Y-m-d H:i:s"}} {{ post.end_time|date:"Y-m-d H:i:s" }} diff --git a/templates/jlog/log_online.html b/templates/jlog/log_online.html index 7e2858e57..4731c5ff1 100644 --- a/templates/jlog/log_online.html +++ b/templates/jlog/log_online.html @@ -39,12 +39,6 @@ - @@ -77,7 +71,6 @@ 用户名 - 所属部门 登录主机 来源IP {% ifnotequal session_role_id 0 %} @@ -92,7 +85,6 @@ {% for post in contacts.object_list %} {{ post.user }} - {{ post.dept_name }} {{ post.host }} {{ post.remote_ip }} {% ifnotequal session_role_id 0 %} diff --git a/templates/jlog/static.jinja2 b/templates/jlog/static.jinja2 new file mode 100644 index 000000000..6e41b7ee5 --- /dev/null +++ b/templates/jlog/static.jinja2 @@ -0,0 +1,18 @@ +{% extends "base.jinja2" %} +{% block head %} + + + +{% endblock %} From 8e9f22537f98ae5dd4afbd054a1d94f408ff05a7 Mon Sep 17 00:00:00 2001 From: liuzheng712 Date: Tue, 27 Oct 2015 17:40:53 +0800 Subject: [PATCH 12/48] reuse asset_list for host_list --- templates/jasset/asset_list.html | 2 ++ templates/nav.html | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/templates/jasset/asset_list.html b/templates/jasset/asset_list.html index 6b1812b41..14607f3a4 100644 --- a/templates/jasset/asset_list.html +++ b/templates/jasset/asset_list.html @@ -24,7 +24,9 @@
+ {% if session_role_id > 0 %} 添加 + {% endif %}