From 1a0c9cd4e6a9d68be1cded0a64b05a02513b88e8 Mon Sep 17 00:00:00 2001 From: yumaojun <719118794@qq.com> Date: Tue, 24 Nov 2015 22:03:58 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E6=96=B0=E5=A2=9E=20PermSudo=E8=A1=A8,=20?= =?UTF-8?q?=E7=94=A8=E4=BA=8E=E8=AE=B0=E5=BD=95=20sudo=E5=88=AB=E5=90=8D?= =?UTF-8?q?=202.=20=E5=AE=9E=E7=8E=B0Sudo=E8=A1=A8=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84=20=E6=B7=BB=E5=8A=A0=EF=BC=8C=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0=EF=BC=8C=E5=88=A0=E9=99=A4=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=203.=20=E6=B7=BB=E5=8A=A0=E8=A7=92=E8=89=B2=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E9=9C=80=E8=A6=81=E9=80=89=E6=8B=A9=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84=20sudo=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jperm/ansible_api.py | 67 +++++++++----- jperm/models.py | 17 +++- jperm/urls.py | 5 +- jperm/utils.py | 25 +++++ jperm/views.py | 136 +++++++++++++++++++++++++++- jumpserver/templatetags/mytags.py | 9 ++ templates/jperm/perm_role_add.html | 11 +++ templates/jperm/perm_role_edit.html | 11 +++ templates/jperm/perm_role_list.html | 3 + templates/jperm/perm_sudo_add.html | 120 ++++++++++++++++++++++++ templates/jperm/perm_sudo_edit.html | 120 ++++++++++++++++++++++++ templates/jperm/perm_sudo_list.html | 112 +++++++++++++++++++++++ templates/jperm/role_sudo.j2 | 126 ++++++++++++++++++++++++++ templates/nav.html | 4 +- 14 files changed, 737 insertions(+), 29 deletions(-) create mode 100644 templates/jperm/perm_sudo_add.html create mode 100644 templates/jperm/perm_sudo_edit.html create mode 100644 templates/jperm/perm_sudo_list.html create mode 100644 templates/jperm/role_sudo.j2 diff --git a/jperm/ansible_api.py b/jperm/ansible_api.py index 3a8b50c61..ee7c2ddee 100644 --- a/jperm/ansible_api.py +++ b/jperm/ansible_api.py @@ -20,7 +20,6 @@ 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), @@ -44,7 +43,7 @@ class CommandValueError(AnsibleError): super(CommandValueError, self).__init__('value:invalid', field, message) -class MyInventory(object): +class MyInventory(Inventory): """ this is my ansible inventory object. """ @@ -65,7 +64,7 @@ class MyInventory(object): self.inventory = Inventory(host_list=[]) self.gen_inventory() - def add_group(self, hosts, groupname, groupvars=None): + def my_add_group(self, hosts, groupname, groupvars=None): """ add hosts to a group """ @@ -83,11 +82,13 @@ class MyInventory(object): hostport = host.get("port") username = host.get("username") password = host.get("password") + sudo_password = host.get("sudo_password") my_host = Host(name=hostname, port=hostport) my_host.set_variable('ansible_ssh_host', hostname) my_host.set_variable('ansible_ssh_port', hostport) my_host.set_variable('ansible_ssh_user', username) my_host.set_variable('ansible_ssh_pass', password) + # set other variables for key, value in host.iteritems(): if key not in ["hostname", "port", "username", "password"]: @@ -102,10 +103,10 @@ class MyInventory(object): add hosts to inventory. """ if isinstance(self.resource, list): - self.add_group(self.resource, 'default_group') + self.my_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")) + self.my_add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars")) class Command(MyInventory): @@ -125,7 +126,7 @@ class Command(MyInventory): if module_name not in ["raw", "command", "shell"]: raise CommandValueError("module_name", - "module_name must be of the 'raw, command, shell'") + "module_name must be of the 'raw, command, shell'") hoc = Runner(module_name=module_name, module_args=command, timeout=timeout, @@ -136,15 +137,17 @@ class Command(MyInventory): ) self.results = hoc.run() + ret = {} if self.stdout: - return {"ok": self.stdout} + ret["ok"] = self.stdout else: msg = [] if self.stderr: msg.append(self.stderr) if self.dark: msg.append(self.dark) - return {"failed": msg} + ret["failed"] = msg + return ret @property def raw_results(self): @@ -206,7 +209,14 @@ 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='default_group', pattern='*'): + def __run(self, + module_args, + module_name="command", + timeout=5, + forks=10, + group='default_group', + pattern='*', + ): """ run command from andible ad-hoc. command : 必须是一个需要执行的命令字符串, 比如 @@ -219,6 +229,7 @@ class Tasks(Command): subset=group, pattern=pattern, forks=forks, + become=False, ) self.results = hoc.run() @@ -272,7 +283,7 @@ class Tasks(Command): module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path) self.__run(module_args, "authorized_key") - return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"} + return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok"} def add_user(self, username, password): """ @@ -310,7 +321,8 @@ class Tasks(Command): delete a host user. """ module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % (username) - self.__run(module_args, "user") + self.__run(module_args, + "user",) return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"} @@ -386,9 +398,15 @@ class Tasks(Command): "product_sn": setup.get("ansible_product_serial") } - return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok", "result": result} - + return {"failed": self.msg, "ok": result} + def push_sudo(self, role_custo, role_name, role_chosen): + """ + use template to render pushed sudoers file + :return: + """ + module_args = 'src=%s dest=%s owner=root group=root mode=0440' % (username, encrypt_pass) + self.__run(module_args, "template") class CustomAggregateStats(callbacks.AggregateStats): @@ -440,12 +458,12 @@ class MyPlaybook(MyInventory): playbook_path = os.path.join(ANSIBLE_DIR, playbook_relational_path) pb = PlayBook( - playbook = playbook_path, - stats = stats, - callbacks = playbook_cb, - runner_callbacks = runner_cb, - inventory = self.inventory, - extra_vars = extra_vars, + playbook=playbook_path, + stats=stats, + callbacks=playbook_cb, + runner_callbacks=runner_cb, + inventory=self.inventory, + extra_vars=extra_vars, check=False) self.results = pb.run() @@ -475,9 +493,14 @@ if __name__ == "__main__": # }, # } - resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902"}] - command = Command(resource) - print command.run("who") + resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902", + # "ansible_become": "yes", + # "ansible_become_method": "sudo", + # # "ansible_become_user": "root", + # "ansible_become_pass": "yusky0902", + }] + cmd = Command(resource) + print cmd.run('ls') # 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 dc8643b67..019411f9b 100644 --- a/jperm/models.py +++ b/jperm/models.py @@ -19,12 +19,23 @@ class SysUser(models.Model): comment = models.CharField(max_length=100, null=True, blank=True, default='') +class PermSudo(models.Model): + name = models.CharField(max_length=100, unique=True) + date_added = models.DateTimeField(auto_now=True) + commands = models.TextField() + comment = models.CharField(max_length=100, null=True, blank=True, default='') + + def __unicode__(self): + return self.name + + class PermRole(models.Model): name = models.CharField(max_length=100, unique=True) comment = models.CharField(max_length=100, null=True, blank=True, default='') password = models.CharField(max_length=100) key_path = models.CharField(max_length=100) date_added = models.DateTimeField(auto_now=True) + sudo = models.ManyToManyField(PermSudo, related_name='perm_role') def __unicode__(self): return self.name @@ -41,4 +52,8 @@ class PermRule(models.Model): role = models.ManyToManyField(PermRole, related_name='perm_rule') def __unicode__(self): - return self.name \ No newline at end of file + return self.name + + + + diff --git a/jperm/urls.py b/jperm/urls.py index aa80f7f75..e382a2a2c 100644 --- a/jperm/urls.py +++ b/jperm/urls.py @@ -13,7 +13,10 @@ 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'^sudo/$', perm_sudo_list), + (r'^sudo/perm_sudo_add/$', perm_sudo_add), + (r'^sudo/perm_sudo_delete/$', perm_sudo_delete), + (r'^sudo/perm_sudo_edit/$', perm_sudo_edit), (r'^log/$', log), (r'^sys_user_add/$', sys_user_add), diff --git a/jperm/utils.py b/jperm/utils.py index 63054b30b..ea68488a8 100644 --- a/jperm/utils.py +++ b/jperm/utils.py @@ -6,6 +6,8 @@ import os.path from paramiko.rsakey import RSAKey from os import chmod, makedirs from uuid import uuid4 +from django.template.loader import get_template +from django.template import Context from jumpserver.settings import KEY_DIR @@ -62,6 +64,29 @@ def gen_keys(): return key_path_dir +def gen_sudo(role_custom, role_name, role_chosen): + """ + 生成sudo file, 仅测试了cenos7 + role_custom: 自定义支持的sudo 命令 格式: 'CMD1, CMD2, CMD3, ...' + role_name: role name + role_chosen: 选择那些sudo的命令别名: + NETWORKING, SOFTWARE, SERVICES, STORAGE, + DELEGATING, PROCESSES, LOCATE, DRIVERS + :return: + """ + sudo_file_basename = os.path.join(os.path.dirname(KEY_DIR), 'role_sudo_file') + makedirs(sudo_file_basename) + sudo_file_path = os.path.join(sudo_file_basename, role_name) + + t = get_template('role_sudo.j2') + content = t.render(Context({"role_custom": role_custom, + "role_name": role_name, + "role_chosen": role_chosen, + })) + with open(sudo_file_path, 'w') as f: + f.write(content) + return sudo_file_path + if __name__ == "__main__": diff --git a/jperm/views.py b/jperm/views.py index f961a8e07..7c4f59c82 100644 --- a/jperm/views.py +++ b/jperm/views.py @@ -9,7 +9,7 @@ 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 jperm.models import PermRole, PermRule, PermSudo from jumpserver.models import Setting from jperm.utils import updates_dict, gen_keys, get_rand_pass @@ -259,19 +259,24 @@ def perm_role_add(request): if request.method == "GET": default_password = get_rand_pass() + sudos = PermSudo.objects.all() return my_render('jperm/perm_role_add.html', locals(), request) elif request.method == "POST": - # 获取参数: name, comment + # 获取参数: name, comment, sudo name = request.POST.get("role_name") comment = request.POST.get("role_comment") password = request.POST.get("role_password") + sudos_name = request.POST.getlist("sudo_name") + sudos_obj = [PermSudo.objects.get(name=sudo_name) for sudo_name in sudos_name] encrypt_pass = CRYPTOR.encrypt(password) # 生成随机密码,生成秘钥对 key_path = gen_keys() role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path) role.save() + role.sudo = sudos_obj + role.save() msg = u"添加角色: %s" % name # 渲染 刷新数据 @@ -350,6 +355,7 @@ def perm_role_edit(request): role_id = request.GET.get("id") role = PermRole.objects.get(id=role_id) role_pass = CRYPTOR.decrypt(role.password) + role_sudos = role.sudo.all() if request.method == "GET": return my_render('jperm/perm_role_edit.html', locals(), request) @@ -359,11 +365,14 @@ def perm_role_edit(request): role_password = request.POST.get("role_password") encrypt_role_pass = CRYPTOR.encrypt(role_password) role_comment = request.POST.get("role_comment") + role_sudo_names = request.POST.getlist("sudo_name") + role_sudos = [PermSudo.objects.get(name=sudo_name) for sudo_name in role_sudo_names] # 写入数据库 role.name = role_name role.password = encrypt_role_pass role.comment = role_comment + role.sudo = role_sudos role.save() msg = u"更新系统角色: %s" % role.name @@ -380,8 +389,6 @@ def perm_role_edit(request): return my_render('jperm/perm_role_list.html', locals(), request) - - @require_role('admin') def perm_role_push(request): """ @@ -461,6 +468,127 @@ def perm_role_push(request): return HttpResponse(u"推送系统角色: %s" % ','.join(role_names)) +@require_role('admin') +def perm_sudo_list(request): + """ + list sudo commands alias + :param request: + :return: + """ + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "查看别名" + + # 获取所有sudo 命令别名 + sudos_list = PermSudo.objects.all() + + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + sudos_list = sudos_list.filter(Q(name=keyword)) + + sudos_list, p, sudos, page_range, current_page, show_first, show_end = pages(sudos_list, request) + + return my_render('jperm/perm_sudo_list.html', locals(), request) + + +@require_role('admin') +def perm_sudo_add(request): + """ + list sudo commands alias + :param request: + :return: + """ + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "添加别名" + + if request.method == "GET": + return my_render('jperm/perm_sudo_add.html', locals(), request) + + elif request.method == "POST": + # 获取参数: name, comment + name = request.POST.get("sudo_name") + comment = request.POST.get("sudo_comment") + commands = request.POST.get("sudo_commands") + + sudo = PermSudo(name=name, comment=comment, commands=commands) + sudo.save() + + msg = u"添加Sudo命令别名: %s" % name + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "查看别名" + # 获取所有sudo 命令别名 + sudos_list = PermSudo.objects.all() + + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + roles_list = sudos_list.filter(Q(name=keyword)) + + sudos_list, p, sudos, page_range, current_page, show_first, show_end = pages(sudos_list, request) + + return my_render('jperm/perm_sudo_list.html', locals(), request) + else: + return HttpResponse(u"不支持该操作") + + +@require_role('admin') +def perm_sudo_edit(request): + """ + list sudo commands alias + :param request: + :return: + """ + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "编辑别名" + + sudo_id = request.GET.get("id") + sudo = PermSudo.objects.get(id=sudo_id) + if request.method == "GET": + return my_render('jperm/perm_sudo_edit.html', locals(), request) + + if request.method == "POST": + name = request.POST.get("sudo_name") + commands = request.POST.get("sudo_commands") + comment = request.POST.get("sudo_comment") + sudo.name = name + sudo.commands = commands + sudo.comment = comment + sudo.save() + + msg = u"更新命令别名: %s" % name + # 渲染数据 + header_title, path1, path2 = "Sudo命令", "别名管理", "查看别名" + # 获取所有sudo 命令别名 + sudos_list = PermSudo.objects.all() + # TODO: 搜索和分页 + keyword = request.GET.get('search', '') + if keyword: + sudos_list = sudos_list.filter(Q(name=keyword)) + sudos_list, p, sudos, page_range, current_page, show_first, show_end = pages(sudos_list, request) + return my_render('jperm/perm_sudo_list.html', locals(), request) + + +@require_role('admin') +def perm_sudo_delete(request): + """ + list sudo commands alias + :param request: + :return: + """ + if request.method == "POST": + # 获取参数删除的role对象 + sudo_id = request.POST.get("id") + sudo = PermSudo.objects.get(id=sudo_id) + # 数据库里删除记录 + sudo.delete() + return HttpResponse(u"删除角色: %s" % sudo.name) + else: + return HttpResponse(u"不支持该操作") + + + + + diff --git a/jumpserver/templatetags/mytags.py b/jumpserver/templatetags/mytags.py index 8dcdf377c..e99c30f05 100644 --- a/jumpserver/templatetags/mytags.py +++ b/jumpserver/templatetags/mytags.py @@ -226,3 +226,12 @@ def ip_str_to_list(ip_str): ip str to list """ return ip_str.split(',') + + +@register.filter(name='role_contain_which_sudos') +def role_contain_which_sudos(role): + """ + get role sudo commands + """ + sudo_names = [sudo.name for sudo in role.sudo.all()] + return ','.join(sudo_names) diff --git a/templates/jperm/perm_role_add.html b/templates/jperm/perm_role_add.html index 243452db0..1f534ac2a 100644 --- a/templates/jperm/perm_role_add.html +++ b/templates/jperm/perm_role_add.html @@ -47,6 +47,17 @@
+命令别名 | +系统命令 | +创建时间 | +操作 | +
---|---|---|---|
{{ sudo.name }} | +{{ sudo.commands }} | +{{ sudo.date_added | date:"Y-m-d H:i:s"}} | ++{# 详情#} + 编辑 + + | +