diff --git a/.gitignore b/.gitignore index 2c3064f75..983fedd49 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ nosetests.xml .mr.developer.cfg .project .pydevproject +*.log logs/* keys/* jumpserver.conf diff --git a/connect.py b/connect.py index 60f0daebb..05d84dd8f 100644 --- a/connect.py +++ b/connect.py @@ -19,10 +19,11 @@ import struct, fcntl, signal, socket, select os.environ['DJANGO_SETTINGS_MODULE'] = 'jumpserver.settings' if django.get_version() != '1.6': django.setup() -from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object -from jumpserver.api import logger, mkdir, Log, TtyLog +from jumpserver.api import ServerError, User, Asset, AssetGroup, get_object, mkdir, get_asset_info, get_role +from jumpserver.api import logger, Log, TtyLog, get_role_key +from jperm.perm_api import gen_resource, get_group_asset_perm, get_group_user_perm from jumpserver.settings import LOG_DIR - +from jperm.ansible_api import Command login_user = get_object(User, username=getpass.getuser()) VIM_FLAG = False @@ -68,23 +69,20 @@ def check_vim_status(command, ssh): return False - - - class Tty(object): """ A virtual tty class 一个虚拟终端类,实现连接ssh和记录日志,基类 """ - def __init__(self, username, asset_name): - self.username = username - self.asset_name = asset_name + def __init__(self, user, asset, role): + self.username = user.username + self.asset_name = asset.hostname self.ip = None self.port = 22 self.channel = None - #self.asset = get_object(Asset, name=asset_name) - #self.user = get_object(User, username=username) - self.role = None + self.asset = asset + self.user = user + self.role = role self.ssh = None self.connect_info = None self.login_type = 'ssh' @@ -252,6 +250,7 @@ class Tty(object): log_file_path = os.path.join(today_connect_log_dir, '%s_%s_%s' % (self.username, self.asset_name, time_start)) try: + mkdir(os.path.dirname(today_connect_log_dir), mode=0777) mkdir(today_connect_log_dir, mode=0777) except OSError: logger.debug('创建目录 %s 失败,请修改%s目录权限' % (today_connect_log_dir, tty_log_dir)) @@ -289,7 +288,10 @@ class Tty(object): # 2. get 映射用户 # 3. get 映射用户的账号,密码或者key # self.connect_info = {'user': '', 'asset': '', 'ip': '', 'port': 0, 'role_name': '', 'role_pass': '', 'role_key': ''} - self.connect_info = {'user': 'a', 'asset': 'b', 'ip': '127.0.0.1', 'port': 22, 'role_name': 'root', 'role_pass': '', 'role_key': '/root/.ssh/id_rsa.bak'} + asset_info = get_asset_info(self.asset) + self.connect_info = {'user': self.user, 'asset': self.asset, 'ip': asset_info.get('ip'), + 'port': int(asset_info.get('port')), 'role_name': self.role.name, + 'role_pass': self.role.password, 'role_key': self.role.key_path} return self.connect_info def get_connection(self): @@ -303,18 +305,24 @@ class Tty(object): ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: - if connect_info.get('role_pass'): - ssh.connect(connect_info.get('ip'), - port=connect_info.get('port'), - username=connect_info.get('role_name'), - password=connect_info.get('role_pass'), - look_for_keys=False) - else: - ssh.connect(connect_info.get('ip'), - port=connect_info.get('port'), - username=connect_info.get('role_name'), - key_filename=connect_info.get('role_key'), - look_for_keys=False) + role_key = get_role_key(self.user, self.role) + if role_key and os.path.isfile(role_key): + try: + ssh.connect(connect_info.get('ip'), + port=connect_info.get('port'), + username=connect_info.get('role_name'), + key_filename=role_key, + look_for_keys=False) + self.ssh = ssh + return ssh + except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: + pass + + ssh.connect(connect_info.get('ip'), + port=connect_info.get('port'), + username=connect_info.get('role_name'), + password=connect_info.get('role_pass'), + look_for_keys=False) except paramiko.ssh_exception.AuthenticationException, paramiko.ssh_exception.SSHException: raise ServerError('认证失败 Authentication Error.') @@ -452,7 +460,7 @@ class SshTty(Tty): #print 'ok'+tmp+'ok' # SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip() # SSH_TTY = '' - channel.send('clear\n') + # channel.send('clear\n') # Make ssh interactive tunnel self.posix_shell() @@ -460,28 +468,123 @@ class SshTty(Tty): channel.close() ssh.close() - def execute(self, cmd): - """ - execute cmd on the asset - 执行命令 - """ - pass +class Nav(object): + def __init__(self, user): + self.user = user + self.search_result = {} + self.user_perm = {} -def print_prompt(): - """ - Print prompt - 打印提示导航 - """ - msg = """\033[1;32m### Welcome Use JumpServer To Login. ### \033[0m - 1) Type \033[32mIP or Part IP, Host Alias or Comments \033[0m To Login. - 2) Type \033[32mP/p\033[0m To Print The Servers You Available. - 3) Type \033[32mG/g\033[0m To Print The Server Groups You Available. - 4) Type \033[32mG/g(1-N)\033[0m To Print The Server Group Hosts You Available. - 5) Type \033[32mE/e\033[0m To Execute Command On Several Servers. - 6) Type \033[32mQ/q\033[0m To Quit. - """ - print textwrap.dedent(msg) + @staticmethod + def print_nav(): + """ + Print prompt + 打印提示导航 + """ + msg = """\n\033[1;32m### Welcome To Use JumpServer, A Open Source System . ### \033[0m + 1) Type \033[32mID\033[0m To Login. + 2) Type \033[32m/\033[0m + \033[32mIP, Host Name, Host Alias or Comments \033[0mTo Search. + 3) Type \033[32mP/p\033[0m To Print The Servers You Available. + 4) Type \033[32mG/g\033[0m To Print The Server Groups You Available. + 5) Type \033[32mG/g\033[0m\033[0m + \033[32mGroup ID\033[0m To Print The Server Group You Available. + 6) Type \033[32mE/e\033[0m To Execute Command On Several Servers. + 7) Type \033[32mQ/q\033[0m To Quit. + """ + + msg = """\n\033[1;32m### 欢迎使用Jumpserver开源跳板机 ### \033[0m + 1) 输入 \033[32mID\033[0m 直接登录. + 2) 输入 \033[32m/\033[0m + \033[32mIP, 主机名, 主机别名 or 备注 \033[0m搜索. + 3) 输入 \033[32mP/p\033[0m 显示您有权限的主机. + 4) 输入 \033[32mG/g\033[0m 显示您有权限的主机组. + 5) 输入 \033[32mG/g\033[0m\033[0m + \033[32m组ID\033[0m 显示该组下主机. + 6) 输入 \033[32mE/e\033[0m 批量执行命令. + 7) 输入 \033[32mQ/q\033[0m 退出. + """ + print textwrap.dedent(msg) + + def search(self, str_r=''): + gid_pattern = re.compile(r'^g\d+$') + if not self.user_perm: + self.user_perm = get_group_user_perm(self.user) + user_asset_all = self.user_perm.get('asset').keys() + user_asset_search = [] + if str_r: + if gid_pattern.match(str_r): + user_asset_search = list(Asset.objects.all()) + else: + for asset in user_asset_all: + if str_r in asset.ip or str_r in str(asset.comment): + user_asset_search.append(asset) + else: + user_asset_search = user_asset_all + + self.search_result = dict(zip(range(len(user_asset_search)), user_asset_search)) + print '\033[32m[%-3s] %-15s %-15s %-5s %-10s %s \033[0m' % ('ID', 'AssetName', 'IP', 'Port', 'Role', 'Comment') + for index, asset in self.search_result.items(): + asset_info = get_asset_info(asset) + role = [str(role.name) for role in self.user_perm.get('asset').get(asset).get('role')] + if asset.comment: + print '[%-3s] %-15s %-15s %-5s %-10s %s' % (index, asset.hostname, asset.ip, asset_info.get('port'), + role, asset.comment) + else: + print '[%-3s] %-15s %-15s %-5s %-10s' % (index, asset.hostname, asset.ip, asset_info.get('port'), role) + print + + @staticmethod + def print_asset_group(): + user_asset_group_all = AssetGroup.objects.all() + + print '\033[32m[%-3s] %-15s %s \033[0m' % ('ID', 'GroupName', 'Comment') + for asset_group in user_asset_group_all: + if asset_group.comment: + print '[%-3s] %-15s %s' % (asset_group.id, asset_group.name, asset_group.comment) + else: + print '[%-3s] %-15s' % (asset_group.id, asset_group.name) + print + + def exec_cmd(self): + self.search() + while True: + print "请输入主机名、IP或ansile支持的pattern, q退出" + try: + pattern = raw_input("\033[1;32mPattern>:\033[0m ").strip() + if pattern == 'q': + break + else: + if not self.user_perm: + self.user_perm = get_group_user_perm(self.user) + res = gen_resource(self.user, perm=self.user_perm) + cmd = Command(res) + logger.debug(res) + for inv in cmd.inventory.get_hosts(pattern=pattern): + print inv.name + confirm_host = raw_input("\033[1;32mIs that [y/n]>:\033[0m ").strip() + if confirm_host == 'y': + while True: + print "请输入执行的命令, 按q退出" + command = raw_input("\033[1;32mCmds>:\033[0m ").strip() + if command == 'q': + break + result = cmd.run(module_name='shell', command=command, pattern=pattern) + for k, v in result.items(): + if k == 'ok': + for host, output in v.items(): + color_print("%s => %s" % (host, 'Ok'), 'green') + print output + print + else: + for host, output in v.items(): + color_print("%s => %s" % (host, k), 'red') + color_print(output, 'red') + print + print "=" * 20 + print + else: + continue + + except EOFError: + print + break def main(): @@ -492,38 +595,60 @@ def main(): if not login_user: # 判断用户是否存在 color_print(u'没有该用户,或许你是以root运行的 No that user.', exits=True) - print_prompt() gid_pattern = re.compile(r'^g\d+$') + nav = Nav(login_user) + nav.print_nav() try: while True: try: - option = raw_input("\033[1;32mOpt or IP>:\033[0m ") + option = raw_input("\033[1;32mOpt or ID>:\033[0m ").strip() except EOFError: - print_prompt() + nav.print_nav() continue except KeyboardInterrupt: sys.exit(0) - if option in ['P', 'p']: - login_user.get_asset_info(printable=True) + if option in ['P', 'p', '\n', '']: + nav.search() continue + if option.startswith('/') or gid_pattern.match(option): + nav.search(option.lstrip('/')) elif option in ['G', 'g']: - login_user.get_asset_group_info(printable=True) - continue - elif gid_pattern.match(option): - gid = option[1:].strip() - asset_group = get_object(AssetGroup, id=gid) - if asset_group and asset_group.is_permed(user=login_user): - asset_group.get_asset_info(printable=True) + nav.print_asset_group() continue elif option in ['E', 'e']: - # exec_cmd_servers(login_name) - pass + nav.exec_cmd() + continue elif option in ['Q', 'q', 'exit']: sys.exit() else: try: - verify_connect(login_user, option) + asset = nav.search_result[int(option)] + roles = get_role(login_user, asset) + if len(roles) > 1: + role_check = dict(zip(range(len(roles)), roles)) + print role_check + for index, role in role_check.items(): + print "[%s] %s" % (index, role.name) + print "输入角色ID, q退出" + try: + role_index = raw_input("\033[1;32mID>:\033[0m ").strip() + if role_index == 'q': + continue + else: + role = role_check[int(role_index)] + except IndexError: + color_print('请输入正确ID', 'red') + continue + elif len(roles) == 1: + role = roles[0] + else: + color_print('没有映射用户', 'red') + continue + ssh_tty = SshTty(login_user, asset, role) + ssh_tty.connect() + except (KeyError, ValueError): + color_print('请输入正确ID', 'red') except ServerError, e: color_print(e, 'red') except IndexError: diff --git a/docs/requirements.txt b/docs/requirements.txt index ea56ab0c6..28d7b9137 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,5 @@ sphinx-me==0.3 django==1.6 -python-ldap==2.4.19 pycrypto==2.6.1 paramiko==1.15.2 ecdsa==0.13 @@ -9,4 +8,9 @@ django-uuidfield==0.5.0 psutil==2.2.1 xlsxwriter==0.7.7 xlrd==0.9.4 -django-bootstrap-form \ No newline at end of file +django-bootstrap-form +tornado +ansible +pyinotify +passlib +argparse \ No newline at end of file diff --git a/jasset/views.py b/jasset/views.py index d6d0f1a3c..9bbff91de 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -145,7 +145,7 @@ def asset_add(request): asset_save = af_post.save(commit=False) if not use_default_auth: password = request.POST.get('password', '') - password_encode = CRYPTOR.encrypt(password) + password_encode = password asset_save.password = password_encode if not ip: asset_save.ip = hostname diff --git a/jperm/ansible_api.py b/jperm/ansible_api.py index 2e89008fc..1b0e5ce38 100644 --- a/jperm/ansible_api.py +++ b/jperm/ansible_api.py @@ -15,11 +15,11 @@ from utils import get_rand_pass import os.path + API_DIR = os.path.dirname(os.path.abspath(__file__)) ANSIBLE_DIR = os.path.join(API_DIR, 'playbooks') - class AnsibleError(StandardError): """ the base AnsibleError which contains error(required), @@ -61,7 +61,7 @@ class MyInventory(object): [{"hostname": "10.10.10.10", "port": "22", "username": "test", "password": "mypass"}, ...] """ self.resource = resource - self.inventory = Inventory() + self.inventory = Inventory(host_list=[]) self.gen_inventory() def add_group(self, hosts, groupname, groupvars=None): @@ -79,14 +79,17 @@ class MyInventory(object): for host in hosts: # set connection variables hostname = host.get("hostname") + hostip = host.get('ip', hostname) hostport = host.get("port") username = host.get("username") password = host.get("password") + ssh_key = host.get("ssh_key") my_host = Host(name=hostname, port=hostport) - my_host.set_variable('ansible_ssh_host', hostname) + my_host.set_variable('ansible_ssh_host', hostip) my_host.set_variable('ansible_ssh_port', hostport) my_host.set_variable('ansible_ssh_user', username) my_host.set_variable('ansible_ssh_pass', password) + my_host.set_variable('ansible_ssh_private_key_file', ssh_key) # set other variables for key, value in host.iteritems(): if key not in ["hostname", "port", "username", "password"]: @@ -101,47 +104,78 @@ class MyInventory(object): add hosts to inventory. """ if isinstance(self.resource, list): - self.add_group(self.resource, 'my_group') + self.add_group(self.resource, 'default_group') elif isinstance(self.resource, dict): for groupname, hosts_and_vars in self.resource.iteritems(): self.add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars")) +class MyRunner(MyInventory): + """ + This is a General object for parallel execute modules. + """ + def __init__(self, *args, **kwargs): + super(MyRunner, self).__init__(*args, **kwargs) + self.results = {} + + def run(self, module_name, module_args='', timeout=10, forks=10, pattern='', + sudo=False, sudo_user='root', sudo_pass=''): + """ + run module from andible ad-hoc. + module_name: ansible module_name + module_args: ansible module args + """ + hoc = Runner(module_name=module_name, + module_args=module_args, + timeout=timeout, + inventory=self.inventory, + pattern=pattern, + forks=forks, + become=sudo, + become_method='sudo', + become_user=sudo_user, + become_pass=sudo_pass + ) + self.results = hoc.run() + return self.results + + class Command(MyInventory): """ this is a command object for parallel execute command. """ def __init__(self, *args, **kwargs): super(Command, self).__init__(*args, **kwargs) - self.results = '' + self.results = {} - def run(self, command, module_name="command", timeout=5, forks=10, group='my_group'): + def run(self, command, module_name="command", timeout=10, forks=10, pattern='*'): """ run command from andible ad-hoc. command : 必须是一个需要执行的命令字符串, 比如 'uname -a' """ + data = {} + if module_name not in ["raw", "command", "shell"]: - raise CommandValueError("module_name", - "module_name must be of the 'raw, command, shell'") + raise CommandValueError("module_name", + "module_name must be of the 'raw, command, shell'") hoc = Runner(module_name=module_name, module_args=command, timeout=timeout, inventory=self.inventory, - subset=group, - forks=forks + pattern=pattern, + forks=forks, ) self.results = hoc.run() if self.stdout: - return {"ok": self.stdout} - else: - msg = [] - if self.stderr: - msg.append(self.stderr) - if self.dark: - msg.append(self.dark) - return {"failed": msg} + data['ok'] = self.stdout + if self.stderr: + data['err'] = self.stderr + if self.dark: + data['dark'] = self.dark + + return data @property def raw_results(self): @@ -172,7 +206,7 @@ class Command(MyInventory): result = {} all = self.results.get("contacted") for key, value in all.iteritems(): - result[key] = value.get("stdout") + result[key] = value.get("stdout") return result @property @@ -183,7 +217,8 @@ class Command(MyInventory): result = {} all = self.results.get("contacted") for key, value in all.iteritems(): - result[key] = { + if value.get("stderr") or value.get("warnings"): + result[key] = { "stderr": value.get("stderr"), "warnings": value.get("warnings"),} return result @@ -203,7 +238,7 @@ class Tasks(Command): def __init__(self, *args, **kwargs): super(Tasks, self).__init__(*args, **kwargs) - def __run(self, module_args, module_name="command", timeout=5, forks=10, group='my_group'): + def __run(self, module_args, module_name="command", timeout=5, forks=10, group='default_group', pattern='*'): """ run command from andible ad-hoc. command : 必须是一个需要执行的命令字符串, 比如 @@ -214,7 +249,8 @@ class Tasks(Command): timeout=timeout, inventory=self.inventory, subset=group, - forks=forks + pattern=pattern, + forks=forks, ) self.results = hoc.run() @@ -368,7 +404,7 @@ class Tasks(Command): result[key] = { "all_ip": setup.get("ansible_all_ipv4_addresses"), - "hostname" : setup.get("ansible_hostname" ), + "hostname" : setup.get("ansible_hostname"), "default_ip": setup.get("ansible_default_ipv4").get("address"), "default_mac": setup.get("ansible_default_ipv4").get("macaddress"), "product_name": setup.get("ansible_product_name"), @@ -385,8 +421,6 @@ class Tasks(Command): return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok", "result": result} - - class CustomAggregateStats(callbacks.AggregateStats): """ Holds stats about per-host activity during playbook runs. @@ -405,7 +439,6 @@ class CustomAggregateStats(callbacks.AggregateStats): self.results.append(runner_results) - def summarize(self, host): """ Return information about a particular host @@ -425,7 +458,6 @@ class MyPlaybook(MyInventory): def __init__(self, *args, **kwargs): super(MyPlaybook, self).__init__(*args, **kwargs) - def run(self, playbook_relational_path, extra_vars=None): """ run ansible playbook, @@ -464,7 +496,6 @@ class App(MyPlaybook): if __name__ == "__main__": - pass # resource = { # "group1": { @@ -472,8 +503,10 @@ if __name__ == "__main__": # "vars" : {"var1": "value1", "var2": "value2"}, # }, # } -# command = Command(resource) -# print command.run("who", group="group1") + + resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902"}] + command = Command(resource) + print command.run("who") # resource = [{"hostname": "192.168.10.148", "port": "22", "username": "root", "password": "xxx"}] # task = Tasks(resource) diff --git a/jperm/models.py b/jperm/models.py index 2bcea14e3..0a01f7593 100644 --- a/jperm/models.py +++ b/jperm/models.py @@ -5,20 +5,6 @@ from jasset.models import Asset, AssetGroup from juser.models import User, UserGroup -class PermLog(models.Model): - datetime = models.DateTimeField(auto_now_add=True) - action = models.CharField(max_length=100, null=True, blank=True, default='') - results = models.CharField(max_length=1000, null=True, blank=True, default='') - is_success = models.BooleanField(default=False) - is_finish = models.BooleanField(default=False) - - -class SysUser(models.Model): - username = models.CharField(max_length=100) - password = models.CharField(max_length=100) - comment = models.CharField(max_length=100, null=True, blank=True, default='') - - class PermRole(models.Model): name = models.CharField(max_length=100, unique=True) comment = models.CharField(max_length=100, null=True, blank=True, default='') @@ -32,14 +18,13 @@ class PermRole(models.Model): class PermRule(models.Model): date_added = models.DateTimeField(auto_now=True) - name = models.CharField(max_length=100) + name = models.CharField(max_length=100, unique=True) comment = models.CharField(max_length=100) asset = models.ManyToManyField(Asset, related_name='perm_rule') asset_group = models.ManyToManyField(AssetGroup, related_name='perm_rule') user = models.ManyToManyField(User, related_name='perm_rule') user_group = models.ManyToManyField(UserGroup, related_name='perm_rule') role = models.ManyToManyField(PermRole, related_name='perm_rule') - ssh_type = models.BooleanField() def __unicode__(self): - return self.name \ No newline at end of file + return self.name diff --git a/jperm/perm_api.py b/jperm/perm_api.py index c04aeda52..f79040c60 100644 --- a/jperm/perm_api.py +++ b/jperm/perm_api.py @@ -1,15 +1,179 @@ # coding: utf-8 - +from django.db.models.query import QuerySet from jumpserver.api import * import uuid import re -from jumpserver.tasks import playbook_run from jumpserver.models import Setting -from jperm.models import PermLog - from jperm.models import PermRole +from jperm.models import PermRule + + +def get_group_user_perm(ob): + """ + ob为用户或用户组 + 获取用户、用户组授权的资产、资产组 + return: + {’asset_group': { + asset_group1: {'asset': [], 'role': [role1, role2], 'rule': [rule1, rule2]}, + asset_group2: {'asset: [], 'role': [role1, role2], 'rule': [rule1, rule2]}, + } + 'asset':{ + asset1: {'role': [role1, role2], 'rule': [rule1, rule2]}, + asset2: {'role': [role1, role2], 'rule': [rule1, rule2]}, + } + ]}, + 'rule':[rule1, rule2,] + } + """ + perm = {} + if isinstance(ob, User): + rule_all = PermRule.objects.filter(user=ob) + elif isinstance(ob, UserGroup): + rule_all = PermRule.objects.filter(user_group=ob) + else: + rule_all = [] + + perm['rule'] = rule_all + perm_asset_group = perm['asset_group'] = {} + perm_asset = perm['asset'] = {} + for rule in rule_all: + asset_groups = rule.asset_group.all() + assets = rule.asset.all() + + # 获取一个规则用户授权的资产 + for asset in assets: + if perm_asset.get(asset): + perm_asset[asset].get('role', set()).update(set(rule.role.all())) + perm_asset[asset].get('rule', set()).add(rule) + else: + perm_asset[asset] = {'role': set(rule.role.all()), 'rule': set([rule])} + + # 获取一个规则用户授权的资产组 + for asset_group in asset_groups: + asset_group_assets = asset_group.asset_set.all() + if perm_asset_group.get(asset_group): + perm_asset_group[asset_group].get('role', set()).update(set(rule.role.all())) + perm_asset_group[asset_group].get('rule', set()).add(rule) + else: + perm_asset_group[asset_group] = {'role': set(rule.role.all()), 'rule': set([rule]), + 'asset': asset_group_assets} + + # 将资产组中的资产添加到资产授权中 + for asset in asset_group_assets: + if perm_asset.get(asset): + perm_asset[asset].get('role', set()).update(perm_asset_group[asset_group].get('role', set())) + perm_asset[asset].get('rule', set()).update(perm_asset_group[asset_group].get('rule', set())) + else: + perm_asset[asset] = {'role': perm_asset_group[asset_group].get('role', set()), + 'rule': perm_asset_group[asset_group].get('rule', set())} + return perm + + +def get_group_asset_perm(ob): + """ + ob为资产或资产组 + 获取资产,资产组授权的用户,用户组 + return: + {’user_group': { + user_group1: {'user': [], 'role': [role1, role2], 'rule': [rule1, rule2]}, + user_group2: {'user: [], 'role': [role1, role2], 'rule': [rule1, rule2]}, + } + 'user':{ + user1: {'role': [role1, role2], 'rule': [rule1, rule2]}, + user2: {'role': [role1, role2], 'rule': [rule1, rule2]}, + } + ]}, + 'rule':[rule1, rule2,] + } + """ + perm = {} + if isinstance(ob, Asset): + rule_all = PermRule.objects.filter(asset=ob) + elif isinstance(ob, AssetGroup): + rule_all = PermRule.objects.filter(asset_group=ob) + else: + rule_all = [] + + perm['rule'] = rule_all + perm_user_group = perm['user_group'] = {} + perm_user = perm['user'] = {} + for rule in rule_all: + user_groups = rule.user_group.all() + users = rule.user.all() + + # 获取一个规则资产的用户 + for user in users: + if perm_user.get(user): + perm_user[user].get('role', set()).update(set(rule.role.all())) + perm_user[user].get('rule', set()).add(rule) + else: + perm_user[user] = {'role': set(rule.role.all()), 'rule': set([rule])} + + # 获取一个规则资产授权的用户组 + for user_group in user_groups: + user_group_users = user_group.user_set.all() + if perm_user_group.get(user_group): + perm_user_group[user_group].get('role', set()).update(set(rule.role.all())) + perm_user_group[user_group].get('rule', set()).add(rule) + else: + perm_user_group[user_group] = {'role': set(rule.role.all()), 'rule': set([rule]), + 'user': user_group_users} + + # 将用户组中的资产添加到用户授权中 + for user in user_group_users: + if perm_user.get(user): + perm_user[user].get('role', set()).update(perm_user_group[user_group].get('role', set())) + perm_user[user].get('rule', set()).update(perm_user_group[user_group].get('rule', set())) + else: + perm_user[user] = {'role': perm_user_group[user_group].get('role', set()), + 'rule': perm_user_group[user_group].get('rule', set())} + return perm + + +def gen_resource(ob, ex='', perm=None): + """ + ob为用户或资产列表或资产queryset, 如果同时输入用户和资产,则获取用户在这些资产上的信息 + 生成MyInventory需要的 resource文件 + """ + res = [] + if isinstance(ob, User) and isinstance(ex, (list, QuerySet)): + if not perm: + perm = get_group_user_perm(ob) + for asset, asset_info in perm.get('asset').items(): + if asset not in ex: + continue + asset_info = get_asset_info(asset) + info = {'hostname': asset.hostname, 'ip': asset.ip, 'port': asset_info.get('port', 22)} + try: + role = sorted(list(perm.get('asset').get(asset).get('role')))[0] + except IndexError: + continue + info['username'] = role.name + info['password'] = role.password + info['ssh_key'] = get_role_key(ob, role) + res.append(info) + elif isinstance(ob, User): + if not perm: + perm = get_group_user_perm(ob) + + for asset, asset_info in perm.get('asset').items(): + asset_info = get_asset_info(asset) + info = {'hostname': asset.hostname, 'ip': asset.ip, 'port': asset_info.get('port', 22)} + try: + role = sorted(list(perm.get('asset').get(asset).get('role')))[0] + except IndexError: + continue + info['username'] = role.name + info['password'] = role.password + info['ssh_key'] = get_role_key(ob, role) + res.append(info) + elif isinstance(ob, (list, QuerySet)): + for asset in ob: + info = get_asset_info(asset) + res.append(info) + return res def get_object_list(model, id_list): @@ -22,267 +186,6 @@ def get_object_list(model, id_list): return object_list -def get_rand_file_path(base_dir=os.path.join(BASE_DIR, 'tmp')): - """获取随机文件路径""" - filename = uuid.uuid1().hex - return os.path.join(base_dir, filename) - - -def get_inventory(host_group): - """生成资产表库存清单""" - path = get_rand_file_path() - f = open(path, 'w') - for group, host_list in host_group.items(): - f.write('[%s]\n' % group) - for ip in host_list: - asset = get_object(Asset, ip=ip) - if asset.use_default: - f.write('%s\n' % ip) - else: - f.write('%s ansible_ssh_port=%s ansible_ssh_user=%s ansible_ssh_pass=%s\n' % - (ip, asset.port, asset.username, CRYPTOR.decrypt(asset.password))) - f.close() - return path - - -def get_playbook(template, var): - """根据playbook模板,生成playbook""" - str_playbook = open(template).read() - for k, v in var.items(): - str_playbook = re.sub(r'%s' % k, v, str_playbook) # 正则来替换传入的字符 - path = get_rand_file_path() - f = open(path, 'w') - f.write(str_playbook) - return path - - -def perm_user_api(perm_info): - """ - 用户授权api,通过调用ansible API完成用户新建等,传入参数必须如下,列表中可以是对象,也可以是用户名和ip - perm_info = {'del': {'users': [], - 'assets': [], - }, - 'new': {'users': [], - 'assets': []}} - """ - log = PermLog(action=perm_info.get('action', '')) - try: - new_users = perm_info.get('new', {}).get('users', []) - new_assets = perm_info.get('new', {}).get('assets', []) - del_users = perm_info.get('del', {}).get('users', []) - del_assets = perm_info.get('del', {}).get('assets', []) - print new_users, new_assets - except IndexError: - raise ServerError("Error: function perm_user_api传入参数错误") - - try: - new_ip = [asset.ip for asset in new_assets if isinstance(asset, Asset)] - del_ip = [asset.ip for asset in del_assets if isinstance(asset, Asset)] - new_username = [user.username for user in new_users] - del_username = [user.username for user in del_users] - except IndexError: - raise ServerError("Error: function perm_user_api传入参数类型错误") - - host_group = {'new': new_ip, 'del': del_ip} - inventory = get_inventory(host_group) - - the_new_users = ','.join(new_username) - the_del_users = ','.join(del_username) - - playbook = get_playbook(os.path.join(BASE_DIR, 'playbook', 'user_perm.yaml'), - {'the_new_group': 'new', 'the_del_group': 'del', - 'the_new_users': the_new_users, 'the_del_users': the_del_users, - 'KEY_DIR': os.path.join(SSH_KEY_DIR, 'sysuser')}) - - print playbook, inventory - - settings = get_object(Setting, name='default') - results = playbook_run(inventory, playbook, settings) - if not results.get('failures', 1) and not results.get('unreachable', ''): - is_success = True - else: - is_success = False - - log.results = results - log.is_finish = True - log.is_success = is_success - log.save() - return results - - -def user_group_permed(user_group): - assets = user_group.asset.all() - asset_groups = user_group.asset_group.all() - - for asset_group in asset_groups: - assets.extend(asset_group.asset.all()) - - return {'assets': assets, 'asset_groups': asset_groups} - - -def user_permed(user): - asset_groups = [] - assets = [] - user_groups = user.group.all() - asset_groups.extend(user.asset_group.all()) - assets.extend(user.asset.all()) - - for user_group in user_groups: - asset_groups.extend(user_group_permed(user_group).get('assets', [])) - assets.extend((user_group_permed(user_group).get('asset_groups', []))) - - return {'assets': assets, 'asset_groups': asset_groups} - - -def _public_perm_api(info): - """ - 公用的用户,用户组,主机,主机组编辑修改新建调用的api,用来完成授权 - info like that: - { - 'type': 'new_user', - 'user': 'a', - 'group': ['A', 'B'] - } - - { - 'type': 'edit_user', - 'user': 'a', - 'group': {'new': ['A'], 'del': []} - } - - { - 'type': 'del_user', - 'user': ['a', 'b'] - } - - { - 'type': 'edit_user_group', - 'group': 'A', - 'user': {'del': ['a', 'b'], 'new': ['c', 'd']} - } - - { - 'type': 'del_user_group', - 'group': ['A'] - } - - { - 'type': 'new_asset', - 'asset': 'a', - 'group': ['A', 'B'] - } - - { - 'type': 'edit_asset', - 'asset': 'a', - 'group': { - 'del': ['A', ['B'], - 'new': ['C', ['D']] - } - } - - { - 'type': 'del_asset', - 'asset': ['a', 'b'] - } - - { - 'type': 'edit_asset_group', - 'group': 'A', - 'asset': {'new': ['a', 'b'], 'del': ['c', 'd']} - } - - { - 'type': 'del_asset_group', - 'group': ['A', 'B'] - } - """ - - if info.get('type') == 'new_user': - new_assets = [] - user = info.get('user') - user_groups = info.get('group') - for user_group in user_groups: - new_assets.extend(user_group_permed(user_group).get('assets', [])) - - perm_info = { - 'action': 'new user: ' + user.name, - 'new': {'users': [user], 'assets': new_assets} - } - elif info.get('type') == 'edit_user': - new_assets = [] - del_assets = [] - user = info.get('user') - new_group = info.get('group').get('new') - del_group = info.get('group').get('del') - - for user_group in new_group: - new_assets.extend(user_group_permed(user_group).get('assets', [])) - - for user_group in del_group: - del_assets.extend((user_group_permed(user_group).get('assets', []))) - - perm_info = { - 'action': 'edit user: ' + user.name, - 'del': {'users': [user], 'assets': del_assets}, - 'new': {'users': [user], 'assets': new_assets} - } - - elif info.get('type') == 'del_user': - user = info.get('user') - del_assets = user_permed(user).get('assets', []) - perm_info = { - 'action': 'del user: ' + user.name, 'del': {'users': [user], 'assets': del_assets}, - } - - elif info.get('type') == 'edit_user_group': - user_group = info.get('group') - new_users = info.get('user').get('new') - del_users = info.get('user').get('del') - assets = user_group_permed(user_group).get('assets', []) - - perm_info = { - 'action': 'edit user group: ' + user_group.name, - 'new': {'users': new_users, 'assets': assets}, - 'del': {'users': del_users, 'assets': assets} - } - - elif info.get('type') == 'del_user_group': - user_group = info.get('group', []) - del_users = user_group.user_set.all() - assets = user_group_permed(user_group).get('assets', []) - - perm_info = { - 'action': "del user group: " + user_group.name, 'del': {'users': del_users, 'assets': assets} - } - else: - return - - try: - results = perm_user_api(perm_info) # 通过API授权或回收 - except ServerError, e: - return e - else: - return results - - -def push_user(user, asset_groups_id): - assets = [] - if not user: - return {'error': '没有该用户'} - for group_id in asset_groups_id: - asset_group = get_object(AssetGroup, id=group_id) - if asset_group: - assets.extend(asset_group.asset_set.all()) - perm_info = { - 'action': 'Push user:' + user.username, - 'new': {'users': [user], 'assets': assets} - } - - results = perm_user_api(perm_info) - return results - - def get_role_info(role_id, type="all"): """ 获取role对应的一些信息 diff --git a/jperm/urls.py b/jperm/urls.py index aa80f7f75..fd40dc1cd 100644 --- a/jperm/urls.py +++ b/jperm/urls.py @@ -13,11 +13,4 @@ urlpatterns = patterns('jperm.views', (r'^role/perm_role_detail/$', perm_role_detail), (r'^role/perm_role_edit/$', perm_role_edit), (r'^role/perm_role_push/$', perm_role_push), - - - (r'^log/$', log), - (r'^sys_user_add/$', sys_user_add), - (r'^perm_user_list/$', sys_user_list), - (r'^sys_user_del/$', sys_user_del), - (r'^sys_user_edit/$', sys_user_edit), ) diff --git a/jperm/utils.py b/jperm/utils.py index 12756b773..a6b3608ee 100644 --- a/jperm/utils.py +++ b/jperm/utils.py @@ -4,7 +4,7 @@ import random import os.path from paramiko.rsakey import RSAKey -from os import chmod, mkdir +from jumpserver.api import mkdir from uuid import uuid4 from jumpserver.settings import KEY_DIR @@ -45,13 +45,13 @@ def gen_keys(): :return: 返回目录名(uuid) """ key_basename = "key-" + uuid4().hex - key_path_dir = os.path.join(KEY_DIR, key_basename) - mkdir(key_path_dir, 0700) - + key_path_dir = os.path.join(KEY_DIR, 'role_key', key_basename) + mkdir(key_path_dir, 0755) key = RSAKey.generate(2048) private_key = os.path.join(key_path_dir, 'id_rsa') public_key = os.path.join(key_path_dir, 'id_rsa.pub') key.write_private_key_file(private_key) + os.chmod(private_key, 0644) with open(public_key, 'w') as content_file: for data in [key.get_name(), @@ -62,8 +62,6 @@ def gen_keys(): return key_path_dir - - if __name__ == "__main__": print gen_keys() diff --git a/jperm/views.py b/jperm/views.py index 2685d6bd6..1c9eaa15c 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -1,22 +1,20 @@ # -*- coding: utf-8 -*- - from django.db.models import Q from jperm.perm_api import * -from jperm.models import PermLog as Log -from jperm.models import SysUser from juser.user_api import gen_ssh_key from juser.models import User, UserGroup from jasset.models import Asset, AssetGroup from jperm.models import PermRole, PermRule +from jumpserver.models import Setting from jperm.utils import updates_dict, gen_keys, get_rand_pass from jperm.ansible_api import Tasks from jperm.perm_api import get_role_info -from jumpserver.api import my_render, get_object +from jumpserver.api import my_render, get_object, CRYPTOR @require_role('admin') @@ -62,7 +60,7 @@ def perm_rule_detail(request): assets = asset_obj return my_render('jperm/perm_rule_detail.html', locals(), request) - + def perm_rule_add(request): """ @@ -89,7 +87,8 @@ def perm_rule_add(request): asset_groups_select = request.POST.getlist('assetgroup', []) roles_select = request.POST.getlist('role', []) rule_name = request.POST.get('rulename') - rule_comment = request.POST.get('comment') + rule_comment = request.POST.get('rule_comment') + rule_ssh_key = request.POST.get("use_publicKey") # 获取需要授权的主机列表 assets_obj = [Asset.objects.get(ip=asset) for asset in assets_select] @@ -115,7 +114,19 @@ def perm_rule_add(request): rule.asset_group = asset_groups_obj rule.role = roles_obj rule.save() - return HttpResponse(u"添加授权规则:%s" % rule.name) + + msg = u"添加授权规则:%s" % rule.name + # 渲染数据 + header_title, path1, path2 = "授权规则", "规则管理", "查看规则" + rules_list = PermRule.objects.all() + + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + rules_list = rules_list.filter(Q(name=keyword)) + rules_list, p, rules, page_range, current_page, show_first, show_end = pages(rules_list, request) + + return my_render('jperm/perm_rule_list.html', locals(), request) @require_role('admin') @@ -131,17 +142,69 @@ def perm_rule_edit(request): rule = PermRule.objects.get(id=rule_id) if request.method == 'GET' and rule_id: - # 渲染数据, 获取所有的rule对象 - users = rule.user.all() - user_groups = rule.user_group.all() - assets = rule.asset.all() - asset_groups = rule.asset_group.all() - roles = rule.role.all() + # 渲染数据, 获取所选的rule对象 + rule_comment = rule.comment + users_select = rule.user.all() + user_groups_select = rule.user_group.all() + assets_select = rule.asset.all() + asset_groups_select = rule.asset_group.all() + roles_select = rule.role.all() + + users = User.objects.all() + user_groups = UserGroup.objects.all() + assets = Asset.objects.all() + asset_groups = AssetGroup.objects.all() + roles = PermRole.objects.all() return my_render('jperm/perm_rule_edit.html', locals(), request) elif request.method == 'POST' and rule_id: - return HttpResponse("uncompleted") + # 获取用户选择的 用户,用户组,资产,资产组,用户角色 + rule_name = request.POST.get('rule_name') + rule_comment = request.POST.get("rule_comment") + users_select = request.POST.getlist('user', []) + user_groups_select = request.POST.getlist('usergroup', []) + assets_select = request.POST.getlist('asset', []) + asset_groups_select = request.POST.getlist('assetgroup', []) + roles_select = request.POST.getlist('role', []) + + # 获取需要授权的主机列表 + assets_obj = [Asset.objects.get(ip=asset) for asset in assets_select] + asset_groups_obj = [AssetGroup.objects.get(name=group) for group in asset_groups_select] + group_assets_obj = [asset for asset in [group.asset_set.all() for group in asset_groups_obj]] + calc_assets = set(group_assets_obj) | set(assets_obj) + + # 获取需要授权的用户列表 + users_obj = [User.objects.get(name=user) for user in users_select] + user_groups_obj = [UserGroup.objects.get(name=group) for group in user_groups_select] + group_users_obj = [user for user in [group.user_set.all() for group in user_groups_obj]] + calc_users = set(group_users_obj) | set(users_obj) + + # 获取授予的角色列表 + roles_obj = [PermRole.objects.get(name=role) for role in roles_select] + + # 仅授权成功的,写回数据库(授权规则,用户,用户组,资产,资产组,用户角色) + rule.user = users_obj + rule.usergroup = user_groups_obj + rule.asset = assets_obj + rule.asset_group = asset_groups_obj + rule.role = roles_obj + rule.name = rule_name + rule.comment = rule.comment + rule.save() + + msg = u"更新授权规则:%s" % rule.name + # 渲染数据 + header_title, path1, path2 = "授权规则", "规则管理", "查看规则" + rules_list = PermRule.objects.all() + + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + rules_list = rules_list.filter(Q(name=keyword)) + rules_list, p, rules, page_range, current_page, show_first, show_end = pages(rules_list, request) + + return my_render('jperm/perm_rule_list.html', locals(), request) @require_role('admin') @@ -201,12 +264,24 @@ def perm_role_add(request): name = request.POST.get("role_name") comment = request.POST.get("role_comment") password = request.POST.get("role_password") + encrypt_pass = CRYPTOR.encrypt(password) # 生成随机密码,生成秘钥对 key_path = gen_keys() - role = PermRole(name=name, comment=comment, password=password, key_path=key_path) + role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) role.save() - return HttpResponse(u"添加角色: %s" % name) + + msg = u"添加角色: %s" % name + # 渲染 刷新数据 + header_title, path1, path2 = "系统角色", "角色管理", "查看角色" + roles_list = PermRole.objects.all() + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + roles_list = roles_list.filter(Q(name=keyword)) + + roles_list, p, roles, page_range, current_page, show_first, show_end = pages(roles_list, request) + return my_render('jperm/perm_role_list.html', locals(), request) else: return HttpResponse(u"不支持该操作") @@ -252,8 +327,12 @@ def perm_role_detail(request): role_info = get_role_info(role_id) # 渲染数据 - for key, value in role_info.iteritems(): - key = value + rules = role_info.get("rules") + assets = role_info.get("assets") + asset_groups = role_info.get("asset_groups") + users = role_info.get("users") + user_groups = role_info.get("user_groups") + return my_render('jperm/perm_role_detail.html', locals(), request) @@ -265,15 +344,38 @@ def perm_role_edit(request): # 渲染数据 header_title, path1, path2 = "系统角色", "角色管理", "角色编辑" + # 渲染数据 + role_id = request.GET.get("id") + role = PermRole.objects.get(id=role_id) + role_pass = CRYPTOR.decrypt(role.password) if request.method == "GET": - role_id = request.GET.get("id") - # 渲染数据 - role = PermRole.objects.get(id=role_id) - return my_render('jperm/perm_role_edit.html', locals(), request) if request.method == "POST": - return HttpResponse(u"未实现") + # 获取 POST 数据 + role_name = request.POST.get("role_name") + role_password = request.POST.get("role_password") + encrypt_role_pass = CRYPTOR.encrypt(role_password) + role_comment = request.POST.get("role_comment") + + # 写入数据库 + role.name = role_name + role.password = encrypt_role_pass + role.comment = role_comment + + role.save() + msg = u"更新系统角色: %s" % role.name + + # 渲染 刷新数据 + header_title, path1, path2 = "系统角色", "角色管理", "查看角色" + roles_list = PermRole.objects.all() + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + roles_list = roles_list.filter(Q(name=keyword)) + + roles_list, p, roles, page_range, current_page, show_first, show_end = pages(roles_list, request) + return my_render('jperm/perm_role_list.html', locals(), request) @require_role('admin') @@ -307,10 +409,20 @@ def perm_role_push(request): calc_assets = set(assets_obj) | set(group_assets_obj) # 生成Inventory - push_resource = [{"hostname": asset.ip, - "port": asset.port, - "username": asset.username, - "password": asset.password} for asset in calc_assets] + push_resource = [] + for asset in calc_assets: + if asset.use_default_auth: + username = Setting.default_user + password = Setting.default_password + port = Setting.default_port + else: + username = asset.username + password = asset.password + port = asset.port + push_resource.append({"hostname": asset.ip, + "port": port, + "username": username, + "password": password}) # 获取角色的推送方式,以及推送需要的信息 roles_obj = [PermRole.objects.get(name=role_name) for role_name in role_names] @@ -326,10 +438,13 @@ def perm_role_push(request): task = Tasks(push_resource) ret = {} ret_failed = [] - if password_push: - ret["password_push"] = task.add_multi_user(**role_pass) - if ret["password_push"].get("status") != "success": - ret_failed.append(1) + + # 因为要先建立用户,所以password 是必选项, + # 而push key是在 password也完成的情况下的 可选项 + ret["password_push"] = task.add_multi_user(**role_pass) + if ret["password_push"].get("status") != "success": + ret_failed.append(1) + if key_push: ret["key_push"] = task.push_multi_key(**role_key) if ret["key_push"].get("status") != "success": @@ -341,108 +456,3 @@ def perm_role_push(request): else: return HttpResponse(u"推送系统角色: %s" % ','.join(role_names)) - - - - - - - - - -@require_role('admin') -def perm_group_list(request): - header_title, path1, path2 = '用户组授权', '授权管理', '用户组授权' - keyword = request.GET.get('search', '') - user_groups_list = UserGroup.objects.all() - if keyword: - request = user_groups_list.filter(Q(name=keyword) | Q(comment=keyword)) - user_groups_list, p, user_groups, page_range, current_page, show_first, show_end = pages(user_groups_list, request) - return my_render('jperm/perm_group_list.html', locals(), request) - - - -@require_role('admin') -def perm_group_edit(request): - header_title, path1, path2 = '用户组授权', '授权管理', '授权更改' - user_group_id = request.GET.get('id', '') - user_group = get_object(UserGroup, id=user_group_id) - asset_all = Asset.objects.all() - asset_group_all = AssetGroup.objects.all() - asset_permed = user_group.asset.all() # 获取授权的资产对象列表 - asset_group_permed = user_group.asset_group.all() # 获取授权的资产组对象列表 - if request.method == 'GET' and user_group: - assets = [asset for asset in asset_all if asset not in asset_permed] - asset_groups = [asset_group for asset_group in asset_group_all if asset_group not in asset_group_permed] - return my_render('jperm/perm_group_edit.html', locals(), request) - elif request.method == 'POST' and user_group: - asset_id_select = request.POST.getlist('asset_select', []) - asset_group_id_select = request.POST.getlist('asset_groups_select', []) - asset_select = get_object_list(Asset, asset_id_select) - asset_group_select = get_object_list(AssetGroup, asset_group_id_select) - asset_new = list(set(asset_select) - set(asset_permed)) # 计算的得到新授权的资产对象列表 - asset_del = list(set(asset_permed) - set(asset_select)) # 计算得到回收权限的资产对象列表 - asset_group_new = list(set(asset_group_select) - set(asset_group_permed)) # 新授权的资产组对象列表 - asset_group_del = list(set(asset_group_permed) - set(asset_group_select)) # 回收的资产组对象列表 - users = user_group.user_set.all() - perm_info = { - 'action': 'perm group edit: ' + user_group.name, - 'del': {'users': users, 'assets': asset_del}, - 'new': {'users': users, 'assets': asset_new} - } - results = perm_user_api(perm_info) - unreachable_asset = [] - failures_asset = [] - for ip in results.get('unreachable'): - unreachable_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) - for ip in results.get('failures'): - failures_asset.extend(filter(lambda x: x, Asset.objects.filter(ip=ip))) - failures_asset.extend(unreachable_asset) # 失败的授权要统计 - for asset in failures_asset: - if asset in asset_select: - asset_select.remove(asset) - else: - asset_select.append(asset) - user_group.asset = asset_select - user_group.asset_group = asset_group_select - user_group.save() # 保存到数据库 - return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") - else: - return HttpResponse('输入错误') - - -def log(request): - header_title, path1, path2 = '授权记录', '授权管理', '授权记录' - log_all = Log.objects.all().order_by('-datetime') - log_all, p, logs, page_range, current_page, show_first, show_end = pages(log_all, request) - return my_render('jperm/perm_log.html', locals(), request) - - -def sys_user_add(request): - asset_group_all = AssetGroup.objects.all() - if request.method == 'POST': - username = request.POST.get('username', '') - password = request.POST.get('password', '') - asset_groups_id = request.POST.getlist('asset_groups_select', []) - comment = request.POST.get('comment') - sys_user = SysUser(username=username, password=password, comment=comment) - sys_user.save() - gen_ssh_key(username, key_dir=os.path.join(SSH_KEY_DIR, 'sysuser'), authorized_keys=False) - results = push_user(sys_user, asset_groups_id) - return HttpResponse(json.dumps(results, sort_keys=True, indent=4), content_type="application/json") - return my_render('jperm/sys_user_add.html', locals(), request) - - -def sys_user_list(request): - users_list = SysUser.objects.all() - users_list, p, users, page_range, current_page, show_first, show_end = pages(users_list, request) - return my_render('jperm/sys_user_list.html', locals(), request) - - -def sys_user_edit(request): - pass - - -def sys_user_del(request): - pass - diff --git a/jumpserver/api.py b/jumpserver/api.py index 4704279f6..cbf7eebc8 100644 --- a/jumpserver/api.py +++ b/jumpserver/api.py @@ -3,26 +3,27 @@ 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 -from settings import * +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 jasset.models import Asset, AssetGroup -# from jlog.models import Log from jlog.models import Log, TtyLog -from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned +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 -import json -import logging def set_log(level): @@ -30,11 +31,15 @@ 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(os.path.join(LOG_DIR, 'jumpserver.log')) + 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) @@ -42,6 +47,63 @@ def set_log(level): 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'] = default.default_port + info['username'] = default.default_user + info['password'] = CRYPTOR.decrypt(default.default_password) + info['ssh_key'] = default.default_pri_key_path + else: + info['port'] = asset.port + info['username'] = asset.username + info['password'] = 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=777) + 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()) + + print user_role_key_path, user.username + 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 @@ -159,8 +221,7 @@ class PyCrypt(object): try: plain_text = cryptor.decrypt(a2b_hex(text)) except TypeError: - # raise ServerError('Decrypt password error, TYpe error.') - pass + raise ServerError('Decrypt password error, TYpe error.') return plain_text.rstrip('\0') @@ -197,9 +258,9 @@ def require_role(role='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': @@ -388,15 +449,16 @@ def bash(cmd): return subprocess.call(cmd, shell=True) -def mkdir(dir_name, username='root', mode=0755): +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) - bash("chown %s:%s '%s'" % (username, username, dir_name)) - os.chmod(dir_name, mode) + os.chmod(dir_name, mode) + if username: + chown(dir_name, username) def http_success(request, msg): @@ -414,4 +476,3 @@ def my_render(template, data, request): CRYPTOR = PyCrypt(KEY) logger = set_log(LOG_LEVEL) -KEY_DIR = os.path.join(BASE_DIR, 'keys') diff --git a/jumpserver/settings.py b/jumpserver/settings.py index cc3f5a8c7..be8464d38 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -17,8 +17,8 @@ config = ConfigParser.ConfigParser() BASE_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) config.read(os.path.join(BASE_DIR, 'jumpserver.conf')) +KEY_DIR = os.path.join(BASE_DIR, 'keys') -KEY_DIR = os.path.join(BASE_DIR, 'role_keys') DB_HOST = config.get('db', 'host') DB_PORT = config.getint('db', 'port') @@ -37,7 +37,7 @@ EMAIL_TIMEOUT = 5 # ======== Log ========== LOG_DIR = os.path.join(BASE_DIR, 'logs') -SSH_KEY_DIR = os.path.join(BASE_DIR, 'role_keys') +SSH_KEY_DIR = os.path.join(BASE_DIR, 'keys/role_keys') KEY = config.get('base', 'key') URL = config.get('base', 'url') LOG_LEVEL = config.get('base', 'log') diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 8dcdf377c..5af20cdd6 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -226,3 +226,14 @@ def ip_str_to_list(ip_str): ip str to list """ return ip_str.split(',') + + +@register.filter(name='key_exist') +def key_exist(username): + """ + ssh key is exist or not + """ + if os.path.isfile(os.path.join(KEY_DIR, 'user', username)): + return True + else: + return False diff --git a/jumpserver/views.py b/jumpserver/views.py index 89bc75fd8..57ae3f5dd 100644 --- a/jumpserver/views.py +++ b/jumpserver/views.py @@ -15,7 +15,6 @@ from jumpserver.api import * from jumpserver.models import Setting 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 @@ -80,18 +79,19 @@ def index_cu(request): # user = get_object(User, id=user_id) login_types = {'L': 'LDAP', 'M': 'MAP'} username = request.user.username - posts = Asset.object.all() - host_count = len(posts) - - new_posts = [] - post_five = [] - for post in posts: - if len(post_five) < 5: - post_five.append(post) - else: - new_posts.append(post_five) - post_five = [] - new_posts.append(post_five) + # TODO: need fix,liuzheng need Asset help + # posts = Asset.object.all() + # host_count = len(posts) + # + # new_posts = [] + # post_five = [] + # for post in posts: + # if len(post_five) < 5: + # post_five.append(post) + # else: + # new_posts.append(post_five) + # post_five = [] + # new_posts.append(post_five) return render_to_response('index_cu.html', locals(), context_instance=RequestContext(request)) @@ -235,7 +235,7 @@ def Login(request): request.session['role_id'] = 1 else: request.session['role_id'] = 0 - return HttpResponseRedirect(request.GET.get('next', '/'), ) + return HttpResponseRedirect(request.session.get('pre_url', '/')) # response.set_cookie('username', username, expires=604800) # response.set_cookie('seed', PyCrypt.md5_crypt(password), expires=604800) # return response @@ -268,7 +268,7 @@ def setting(request): if '' in [username, port] and ('' in password or '' in private_key): return HttpResponse('所填内容不能为空, 且密码和私钥填一个') else: - private_key_path = os.path.join(BASE_DIR, 'role_keys', 'default', 'default_private_key.pem') + private_key_path = os.path.join(BASE_DIR, 'keys/role_keys', 'default', 'default_private_key.pem') if private_key: with open(private_key_path, 'w') as f: f.write(private_key) diff --git a/juser/user_api.py b/juser/user_api.py index 911c554ee..0248e5c6d 100644 --- a/juser/user_api.py +++ b/juser/user_api.py @@ -123,27 +123,27 @@ def db_del_user(username): def gen_ssh_key(username, password='', key_dir=os.path.join(KEY_DIR, 'user'), - authorized_keys=True, home="/home", length=2048): """ generate a user ssh key in a property dir 生成一个用户ssh密钥对 """ + logger.debug('生成ssh key, 并设置authorized_keys') private_key_file = os.path.join(key_dir, username) - mkdir(private_key_file, username) + mkdir(key_dir, mode=777) if os.path.isfile(private_key_file): os.unlink(private_key_file) ret = bash('echo -e "y\n"|ssh-keygen -t rsa -f %s -b %s -P "%s"' % (private_key_file, length, password)) if authorized_keys: auth_key_dir = os.path.join(home, username, '.ssh') - mkdir(auth_key_dir, username, mode=0700) + mkdir(auth_key_dir, mode=0700) authorized_key_file = os.path.join(auth_key_dir, 'authorized_keys') with open(private_key_file+'.pub') as pub_f: with open(authorized_key_file, 'w') as auth_f: auth_f.write(pub_f.read()) os.chmod(authorized_key_file, 0600) - bash('chown %s:%s %s' % (username, username, authorized_key_file)) + chown(authorized_key_file, username) def server_add_user(username, password, ssh_key_pwd, ssh_key_login_need): diff --git a/juser/views.py b/juser/views.py index f0f04c79e..84f39954b 100644 --- a/juser/views.py +++ b/juser/views.py @@ -96,36 +96,37 @@ def group_edit(request): if request.method == 'GET': group_id = request.GET.get('id', '') - user_group = get_object(UserGroup, id=group_id) - if user_group: - users_all = User.objects.all() - users_selected = user_group.user_set.all() - users_remain = [user for user in users_all if user not in users_selected] + # user_group = get_object(UserGroup, id=group_id) + user_group = UserGroup.objects.get(id=group_id) + users_selected = User.objects.filter(group=user_group) + users_remain = User.objects.filter(~Q(group=user_group)) + users_all = User.objects.all() - else: + elif request.method == 'POST': group_id = request.POST.get('group_id', '') group_name = request.POST.get('group_name', '') comment = request.POST.get('comment', '') users_selected = request.POST.getlist('users_selected') - users = [] try: if '' in [group_id, group_name]: raise ServerError('组名不能为空') - user_group = get_object(UserGroup, id=group_id) - other_group = get_object(UserGroup, name=group_name) - - if other_group and other_group.id != int(group_id): + if len(UserGroup.objects.filter(name=group_name)) > 1: raise ServerError(u'%s 用户组已存在' % group_name) + # add user group + for user in User.objects.filter(id__in=users_selected): + user.group.add(UserGroup.objects.get(id=group_id)) + # delete user group + user_group = UserGroup.objects.get(id=group_id) + for user in [user for user in User.objects.filter(group=user_group) if user not in User.objects.filter(id__in=users_selected)]: + user_group_all = user.group.all() + user.group.clear() + for g in user_group_all: + if g == user_group: + continue + user.group.add(g) - for user_id in users_selected: - users.extend(User.objects.filter(id=user_id)) - - if user_group: - user_group.update(name=group_name, comment=comment) - user_group.user_set.clear() - user_group.user_set = users except ServerError, e: error = e @@ -133,8 +134,8 @@ def group_edit(request): return HttpResponseRedirect('/juser/group_list/') else: users_all = User.objects.all() - users_selected = user_group.user_set.all() - users_remain = [user for user in users_all if user not in users_selected] + users_selected = User.objects.filter(group=user_group) + users_remain = User.objects.filter(~Q(group=user_group)) return my_render('juser/group_edit.html', locals(), request) diff --git a/keys/README.md b/keys/README.md new file mode 100644 index 000000000..9e060e41d --- /dev/null +++ b/keys/README.md @@ -0,0 +1,3 @@ +看山是山,看水是水 +看山不是山,看水不是水 +看山是山,看水是水 diff --git a/logs/README.md b/logs/README.md new file mode 100644 index 000000000..309d1a147 --- /dev/null +++ b/logs/README.md @@ -0,0 +1 @@ +永远年轻,永远热泪盈眶 diff --git a/playbook/user_perm.yaml b/playbook/user_perm.yaml deleted file mode 100644 index 4bcfd72e6..000000000 --- a/playbook/user_perm.yaml +++ /dev/null @@ -1,17 +0,0 @@ -- hosts: the_del_group - tasks: - - name: del user - user: name={{ item }} state=absent remove=yes - with_items: [ the_del_users ] - -- hosts: the_new_group - tasks: - - name: add user - user: name={{ item }} state=present - with_items: [ the_new_users ] - - name: .ssh direcotory - file: name=/home/{{ item }}/.ssh mode=700 owner={{ item }} group={{ item }} state=directory - with_items: [ the_new_users ] - - name: set authorizied_file - copy: src=KEY_DIR/{{ item }}.pub dest=/home/{{ item }}/.ssh/authorizied_keys owner={{ item }} group={{ item }} mode=600 - with_items: [ the_new_users ] diff --git a/templates/index.html b/templates/index.html index af6966ef6..0d8baf9f9 100644 --- a/templates/index.html +++ b/templates/index.html @@ -14,7 +14,6 @@
Tips: 如果IP地址不填写, IP默认会设置与主机名一致
+