Merge branch 'dev' of git.coding.net:jumpserver/jumpserver into dev

pull/26/head
ibuler 2015-11-28 21:23:01 +08:00
commit 8ac369b925
18 changed files with 890 additions and 71 deletions

View File

@ -43,7 +43,7 @@ class CommandValueError(AnsibleError):
super(CommandValueError, self).__init__('value:invalid', field, message) super(CommandValueError, self).__init__('value:invalid', field, message)
class MyInventory(object): class MyInventory(Inventory):
""" """
this is my ansible inventory object. this is my ansible inventory object.
""" """
@ -64,7 +64,7 @@ class MyInventory(object):
self.inventory = Inventory(host_list=[]) self.inventory = Inventory(host_list=[])
self.gen_inventory() 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 add hosts to a group
""" """
@ -90,6 +90,7 @@ class MyInventory(object):
my_host.set_variable('ansible_ssh_user', username) my_host.set_variable('ansible_ssh_user', username)
my_host.set_variable('ansible_ssh_pass', password) my_host.set_variable('ansible_ssh_pass', password)
my_host.set_variable('ansible_ssh_private_key_file', ssh_key) my_host.set_variable('ansible_ssh_private_key_file', ssh_key)
# set other variables # set other variables
for key, value in host.iteritems(): for key, value in host.iteritems():
if key not in ["hostname", "port", "username", "password"]: if key not in ["hostname", "port", "username", "password"]:
@ -104,10 +105,10 @@ class MyInventory(object):
add hosts to inventory. add hosts to inventory.
""" """
if isinstance(self.resource, list): 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): elif isinstance(self.resource, dict):
for groupname, hosts_and_vars in self.resource.iteritems(): 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 MyRunner(MyInventory): class MyRunner(MyInventory):
@ -168,6 +169,7 @@ class Command(MyInventory):
) )
self.results = hoc.run() self.results = hoc.run()
ret = {}
if self.stdout: if self.stdout:
data['ok'] = self.stdout data['ok'] = self.stdout
if self.stderr: if self.stderr:
@ -177,6 +179,7 @@ class Command(MyInventory):
return data return data
@property @property
def raw_results(self): def raw_results(self):
""" """
@ -238,7 +241,14 @@ class Tasks(Command):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(Tasks, self).__init__(*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. run command from andible ad-hoc.
command : 必须是一个需要执行的命令字符串 比如 command : 必须是一个需要执行的命令字符串 比如
@ -251,9 +261,11 @@ class Tasks(Command):
subset=group, subset=group,
pattern=pattern, pattern=pattern,
forks=forks, forks=forks,
become=False,
) )
self.results = hoc.run() self.results = hoc.run()
return {"msg": self.msg, "result": self.results}
@property @property
def msg(self): def msg(self):
@ -304,7 +316,7 @@ class Tasks(Command):
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path) module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path)
self.__run(module_args, "authorized_key") 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): def add_user(self, username, password):
""" """
@ -342,7 +354,8 @@ class Tasks(Command):
delete a host user. delete a host user.
""" """
module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % (username) 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"} return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"}
@ -418,8 +431,31 @@ class Tasks(Command):
"product_sn": setup.get("ansible_product_serial") "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_file(self, file_path):
"""
use template to render pushed sudoers file
:return:
"""
module_args1 = 'test'
ret1 = self.__run(module_args1, "script")
module_args2 = 'visudo -c | grep "parsed OK" &> /dev/null && echo "ok" || echo "failed"'
ret2 = self.__run(module_args2, "shell")
ret2_status = [host_value.get("stdout") for host_value in ret2["result"]["contacted"].values()]
result = {}
if not ret1["msg"]:
result["step1"] = "ok"
else:
result["step1"] = "failed"
if not ret2["msg"] and "failed" not in ret2_status:
result["step2"] = "ok"
else:
result["step2"] = "failed"
return result
class CustomAggregateStats(callbacks.AggregateStats): class CustomAggregateStats(callbacks.AggregateStats):
""" """
@ -469,12 +505,12 @@ class MyPlaybook(MyInventory):
playbook_path = os.path.join(ANSIBLE_DIR, playbook_relational_path) playbook_path = os.path.join(ANSIBLE_DIR, playbook_relational_path)
pb = PlayBook( pb = PlayBook(
playbook = playbook_path, playbook=playbook_path,
stats = stats, stats=stats,
callbacks = playbook_cb, callbacks=playbook_cb,
runner_callbacks = runner_cb, runner_callbacks=runner_cb,
inventory = self.inventory, inventory=self.inventory,
extra_vars = extra_vars, extra_vars=extra_vars,
check=False) check=False)
self.results = pb.run() self.results = pb.run()
@ -504,9 +540,14 @@ if __name__ == "__main__":
# }, # },
# } # }
resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902"}] resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902",
command = Command(resource) # "ansible_become": "yes",
print command.run("who") # "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"}] # resource = [{"hostname": "192.168.10.148", "port": "22", "username": "root", "password": "xxx"}]
# task = Tasks(resource) # task = Tasks(resource)

View File

@ -5,12 +5,37 @@ from jasset.models import Asset, AssetGroup
from juser.models import User, UserGroup 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 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): class PermRole(models.Model):
name = models.CharField(max_length=100, unique=True) name = models.CharField(max_length=100, unique=True)
comment = models.CharField(max_length=100, null=True, blank=True, default='') comment = models.CharField(max_length=100, null=True, blank=True, default='')
password = models.CharField(max_length=100) password = models.CharField(max_length=100)
key_path = models.CharField(max_length=100) key_path = models.CharField(max_length=100)
date_added = models.DateTimeField(auto_now=True) date_added = models.DateTimeField(auto_now=True)
sudo = models.ManyToManyField(PermSudo, related_name='perm_role')
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@ -28,3 +53,13 @@ class PermRule(models.Model):
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class PermPush(models.Model):
date_added = models.DateTimeField(auto_now=True)
asset = models.ManyToManyField(Asset, related_name='perm_push')
asset_group = models.ManyToManyField(AssetGroup, related_name='perm_push')
role = models.ManyToManyField(PermRole, related_name='perm_push')
is_public_key = models.BooleanField(default=False)
is_password = models.BooleanField(default=False)

View File

@ -266,6 +266,40 @@ def get_role_info(role_id, type="all"):
return u"不支持的查询" return u"不支持的查询"
def get_role_push_host(role):
"""
get the role push host
:return: the asset object
"""
# 计算该role 所有push记录 总共推送的主机
assets = []
asset_groups = []
for push in role.perm_push.all():
assets.extend(push.asset.all())
asset_groups.extend(push.asset_group.all())
group_assets = []
for asset_group in asset_groups:
group_assets.extend(asset_group.asset_set.all())
cacl_assets = set(assets) | set(group_assets)
# 计算所有主机 在push记录里面的 使用密码和使用秘钥状况
result = []
for asset in cacl_assets:
all_push = asset.perm_push.all()
if True in [push.is_password for push in all_push if role in push.role.all()]:
is_password = u""
else:
is_password = u""
if True in [push.is_public_key for push in all_push if role in push.role.all()]:
is_public_key = u""
else:
is_public_key = u""
result.append({"ip": asset.ip,
"group": ','.join([group.name for group in asset.group.all()]),
"password": is_password,
"pubkey": is_public_key})
return result
if __name__ == "__main__": if __name__ == "__main__":
print get_role_info(1) print get_role_info(1)

View File

@ -13,4 +13,9 @@ urlpatterns = patterns('jperm.views',
(r'^role/perm_role_detail/$', perm_role_detail), (r'^role/perm_role_detail/$', perm_role_detail),
(r'^role/perm_role_edit/$', perm_role_edit), (r'^role/perm_role_edit/$', perm_role_edit),
(r'^role/perm_role_push/$', perm_role_push), (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),
) )

View File

@ -8,6 +8,12 @@ from paramiko.rsakey import RSAKey
from jumpserver.api import mkdir from jumpserver.api import mkdir
from uuid import uuid4 from uuid import uuid4
from jumpserver.api import CRYPTOR from jumpserver.api import CRYPTOR
from os import makedirs
from django.template.loader import get_template
from django.template import Context
from tempfile import NamedTemporaryFile
from jumpserver.settings import KEY_DIR from jumpserver.settings import KEY_DIR
@ -66,6 +72,47 @@ def gen_keys(key="", key_path_dir=""):
return key_path_dir 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
def get_add_sudo_script(sudo_chosen_aliase, sudo_chosen_obj):
"""
get the sudo file
:param kwargs:
:return:
"""
sudo_j2 = get_template('jperm/role_sudo.j2')
sudo_content = sudo_j2.render(Context({"sudo_chosen_aliase": sudo_chosen_aliase,
"sudo_chosen_obj": sudo_chosen_obj}))
sudo_file = NamedTemporaryFile(delete=False)
sudo_file.write(sudo_content)
sudo_file.close()
return sudo_file.name
if __name__ == "__main__": if __name__ == "__main__":
print gen_keys() print gen_keys()

View File

@ -5,15 +5,14 @@ from paramiko import SSHException
from jperm.perm_api import * from jperm.perm_api import *
from juser.user_api import gen_ssh_key from juser.user_api import gen_ssh_key
from juser.models import User, UserGroup from juser.models import User, UserGroup
from jasset.models import Asset, AssetGroup from jasset.models import Asset, AssetGroup
from jperm.models import PermRole, PermRule from jperm.models import PermRole, PermRule, PermSudo, PermPush
from jumpserver.models import Setting from jumpserver.models import Setting
from jperm.utils import updates_dict, gen_keys, get_rand_pass from jperm.utils import updates_dict, gen_keys, get_rand_pass, get_add_sudo_script
from jperm.ansible_api import Tasks from jperm.ansible_api import Tasks
from jperm.perm_api import get_role_info from jperm.perm_api import get_role_info, get_role_push_host
from jumpserver.api import my_render, get_object, CRYPTOR from jumpserver.api import my_render, get_object, CRYPTOR
@ -256,37 +255,36 @@ def perm_role_add(request):
# 渲染数据 # 渲染数据
header_title, path1, path2 = "系统角色", "角色管理", "添加角色" header_title, path1, path2 = "系统角色", "角色管理", "添加角色"
if request.method == "POST": if request.method == "GET":
# 获取参数: name, comment default_password = get_rand_pass()
name = request.POST.get("role_name", "") sudos = PermSudo.objects.all()
comment = request.POST.get("role_comment", "") return my_render('jperm/perm_role_add.html', locals(), request)
password = request.POST.get("role_password", "")
key_content = request.POST.get("role_key", "") elif request.method == "POST":
try: # 获取参数: name, comment, sudo
if get_object(PermRole, name=name): name = request.POST.get("role_name")
raise ServerError('已经存在该用户 %s' % 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
# 渲染 刷新数据
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))
if '' == password and '' == key_content:
raise ServerError('账号和密码必填一项')
if password:
encrypt_pass = CRYPTOR.encrypt(password)
else:
encrypt_pass = CRYPTOR.encrypt(CRYPTOR.gen_rand_pass(20))
# 生成随机密码,生成秘钥对
if key_content:
try:
key_path = gen_keys(key=key_content)
except SSHException:
raise ServerError('输入的密钥不合法')
else:
key_path = gen_keys()
logger.debug('generate role key: %s' % key_path)
role = PermRole(name=name, comment=comment, password=encrypt_pass, key_path=key_path)
role.save()
msg = u"添加角色: %s" % name
return HttpResponseRedirect('/jperm/role/')
except ServerError, e:
error = e
return my_render('jperm/perm_role_add.html', locals(), request) return my_render('jperm/perm_role_add.html', locals(), request)
@ -337,6 +335,7 @@ def perm_role_detail(request):
asset_groups = role_info.get("asset_groups") asset_groups = role_info.get("asset_groups")
users = role_info.get("users") users = role_info.get("users")
user_groups = role_info.get("user_groups") user_groups = role_info.get("user_groups")
push_info = get_role_push_host(PermRole.objects.get(id=role_id))
return my_render('jperm/perm_role_detail.html', locals(), request) return my_render('jperm/perm_role_detail.html', locals(), request)
@ -351,13 +350,20 @@ def perm_role_edit(request):
# 渲染数据 # 渲染数据
role_id = request.GET.get("id") role_id = request.GET.get("id")
role = get_object(PermRole, id=role_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)
if request.method == "POST": if request.method == "POST":
# 获取 POST 数据 # 获取 POST 数据
role_name = request.POST.get("role_name") role_name = request.POST.get("role_name")
role_password = request.POST.get("role_password") role_password = request.POST.get("role_password")
role_comment = request.POST.get("role_comment") 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]
key_content = request.POST.get("role_key", "") key_content = request.POST.get("role_key", "")
try: try:
if not role: if not role:
@ -375,7 +381,9 @@ def perm_role_edit(request):
logger.debug('Recreate role key: %s' % role.key_path) logger.debug('Recreate role key: %s' % role.key_path)
# 写入数据库 # 写入数据库
role.name = role_name role.name = role_name
role.password = encrypt_role_pass
role.comment = role_comment role.comment = role_comment
role.sudo = role_sudos
role.save() role.save()
msg = u"更新系统角色: %s" % role.name msg = u"更新系统角色: %s" % role.name
@ -386,6 +394,7 @@ def perm_role_edit(request):
return my_render('jperm/perm_role_edit.html', locals(), request) return my_render('jperm/perm_role_edit.html', locals(), request)
@require_role('admin') @require_role('admin')
def perm_role_push(request): def perm_role_push(request):
""" """
@ -445,22 +454,184 @@ def perm_role_push(request):
key_push = request.POST.get("use_publicKey") key_push = request.POST.get("use_publicKey")
task = Tasks(push_resource) task = Tasks(push_resource)
ret = {} ret = {}
ret_failed = [] ret_failed = {}
# 因为要先建立用户所以password 是必选项, # 因为要先建立用户所以password 是必选项而push key是在 password也完成的情况下的 可选项
# 而push key是在 password也完成的情况下的 可选项 # 1. 以password 方式推送角色
ret["password_push"] = task.add_multi_user(**role_pass) if password_push:
if ret["password_push"].get("status") != "success": ret["password_push"] = task.add_multi_user(**role_pass)
ret_failed.append(1) if ret["password_push"].get("status") != "success":
ret_failed["step1"] == "failed"
# 2. 以秘钥 方式推送角色
if key_push: if key_push:
ret["password_push"] = task.add_multi_user(**role_pass)
if ret["password_push"].get("status") != "success":
ret_failed["step2-1"] = "failed"
ret["key_push"] = task.push_multi_key(**role_key) ret["key_push"] = task.push_multi_key(**role_key)
if ret["key_push"].get("status") != "success": if ret["key_push"].get("status") != "success":
ret_failed.append(1) ret_failed["step2-2"] = "failed"
print ret # 3. 推送sudo配置文件
sudo_chosen_aliase = {}
sudo_alias = []
for role in roles_obj:
role_alias = [sudo.name for sudo in role.sudo.all()]
sudo_alias.extend(role_alias)
sudo_chosen_aliase[role.name] = ','.join(role_alias)
sudo_chosen_obj = [PermSudo.objects.get(name=sudo_name) for sudo_name in set(sudo_alias)]
add_sudo_script = get_add_sudo_script(sudo_chosen_aliase, sudo_chosen_obj)
ret_sudo = task.push_sudo_file(add_sudo_script)
if ret_sudo["step1"] != "ok" or ret_sudo["step2"] != "ok":
ret_failed["step3"] = "failed"
os.remove(add_sudo_script)
# 结果汇总统计
if ret_failed: if ret_failed:
return HttpResponse(u"推送失败") # 推送失败
error = u"推送失败, 原因: %s 失败" % ','.join(ret_failed.keys())
else: else:
return HttpResponse(u"推送系统角色: %s" % ','.join(role_names)) # 推送成功 回写push表
msg = u"推送系统角色: %s" % ','.join(role_names)
push = PermPush(is_public_key=bool(key_push), is_password=bool(password_push))
push.save()
push.asset_group = asset_groups_obj
push.asset = calc_assets
push.role = roles_obj
push.save()
# 渲染 刷新数据
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')
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.strip(), comment=comment, commands=commands.strip())
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.strip()
sudo.commands = commands.strip()
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"不支持该操作")

View File

@ -250,3 +250,12 @@ def check_role(asset_id, user):
""" """
return user return user
@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)

View File

@ -55,6 +55,17 @@
</div> </div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group">
<label for="sudo" class="col-sm-2 control-label">角色Sudo命令<span class="red-fonts">*</span></label>
<div class="col-sm-8" id="sudo_name">
<select name="sudo_name" data-placeholder="请选择Sudo别名" class="chosen-select form-control m-b" multiple tabindex="2">
{% for sudo in sudos %}
<option >{{ sudo.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<label for="role_comment" class="col-sm-2 control-label">备注</label> <label for="role_comment" class="col-sm-2 control-label">备注</label>
<div class="col-sm-8"> <div class="col-sm-8">

View File

@ -5,7 +5,6 @@
{% block content %} {% block content %}
{% include 'nav_cat_bar.html' %} {% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
<div class="row"> <div class="row">
<div class="col-lg-4"> <div class="col-lg-4">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
@ -51,8 +50,8 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-4"> <div class="col-lg-4">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-title">
@ -97,8 +96,8 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-4"> <div class="col-lg-4">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-title">
@ -143,8 +142,60 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label label-primary"><b>推送主机</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
<li><a href="#"></a>
</li>
<li><a href="#"></a>
</li>
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div> </div>
<div class="ibox-content">
<div>
<div class="text-left">
<table class="table table-striped" id="ugedit" >
<thead>
<tr>
<th class="text-center">主机</th>
<th class="text-center">主机组</th>
<th class="text-center">使用密码</th>
<th class="text-center">使用秘钥</th>
</tr>
</thead>
<tbody>
{% for host in push_info %}
<tr class="gradeX">
<td class="text-center"> {{ host.ip }} </td>
<td class="text-center"> {{ host.group }} </td>
<td class="text-center"> {{ host.password }} </td>
<td class="text-center"> {{ host.pubkey }} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div> </div>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -56,6 +56,17 @@
</div> </div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group">
<label for="sudo" class="col-sm-2 control-label">角色Sudo命令<span class="red-fonts">*</span></label>
<div class="col-sm-8" id="sudo_name">
<select name="sudo_name" data-placeholder="请选择Sudo别名" class="chosen-select form-control m-b" multiple tabindex="2">
{% for sudo in role_sudos %}
<option selected >{{ sudo.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<label for="role_comment" class="col-sm-2 control-label">备注</label> <label for="role_comment" class="col-sm-2 control-label">备注</label>
<div class="col-sm-8"> <div class="col-sm-8">

View File

@ -52,6 +52,7 @@
<th class="text-center">名称 </th> <th class="text-center">名称 </th>
<th class="text-center">备注</th> <th class="text-center">备注</th>
<th class="text-center">创建时间</th> <th class="text-center">创建时间</th>
<th class="text-center">sudo别名</th>
<th class="text-center">操作</th> <th class="text-center">操作</th>
</tr> </tr>
</thead> </thead>
@ -61,6 +62,7 @@
<td class="text-center"> {{ role.name }} </td> <td class="text-center"> {{ role.name }} </td>
<td class="text-center"> {{ role.comment }} </td> <td class="text-center"> {{ role.comment }} </td>
<td class="text-center"> {{ role.date_added | date:"Y-m-d H:i:s"}} </td> <td class="text-center"> {{ role.date_added | date:"Y-m-d H:i:s"}} </td>
<td class="text-center"> {{ role | role_contain_which_sudos }} </td>
<td class="text-center"> <td class="text-center">
<a href="/jperm/role/perm_role_detail/?id={{ role.id }}" class="btn btn-xs btn-primary">详情</a> <a href="/jperm/role/perm_role_detail/?id={{ role.id }}" class="btn btn-xs btn-primary">详情</a>
<a href="/jperm/role/perm_role_edit/?id={{ role.id }}" class="btn btn-xs btn-info">编辑</a> <a href="/jperm/role/perm_role_edit/?id={{ role.id }}" class="btn btn-xs btn-info">编辑</a>
@ -98,6 +100,7 @@ function remove_role(role_id){
del_row.remove() del_row.remove()
}, },
error: function (msg) { error: function (msg) {
console.log(msg)
alert("失败: " + msg) alert("失败: " + msg)
} }
}); });

View File

@ -77,6 +77,16 @@
</div> </div>
</div> </div>
</div> </div>
<div class="form-group">
<label for="j_group" class="col-sm-2 control-label">使用密码</label>
<div class="col-sm-1">
<div class="radio i-checks">
<label>
<input type="checkbox" value="1" id="use_password" name="use_password">
</label>
</div>
</div>
</div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">

View File

@ -36,7 +36,7 @@
<div class="form-group"> <div class="form-group">
<label for="rulename" class="col-sm-2 control-label">授权名称<span class="red-fonts">*</span></label> <label for="rulename" class="col-sm-2 control-label">授权名称<span class="red-fonts">*</span></label>
<div class="col-sm-8"> <div class="col-sm-8">
<input id="rulename" name="rulename" placeholder="Rule Name" type="text" class="form-control" {% if error %}value="{{ username }}" {% endif %}> <input id="rulename" name="rulename" placeholder="Rule Name" type="text" class="form-control">
</div> </div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
@ -44,8 +44,9 @@
<label for="user" class="col-sm-2 control-label">用户</label> <label for="user" class="col-sm-2 control-label">用户</label>
<div class="col-sm-8"> <div class="col-sm-8">
<select name="user" id="user" data-placeholder="用户名" class="chosen-select form-control m-b" multiple tabindex="2"> <select name="user" id="user" data-placeholder="用户名" class="chosen-select form-control m-b" multiple tabindex="2">
{% for user in users %} {% for user in users %}
<option value="{{ user.name }}">{{ user.name }}</option> <option>{{ user.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
<span class="help-block m-b-none">用户和用户组必选一个</span> <span class="help-block m-b-none">用户和用户组必选一个</span>
@ -55,6 +56,7 @@
<label for="usergroup" class="col-sm-2 control-label">用户组</label> <label for="usergroup" class="col-sm-2 control-label">用户组</label>
<div class="col-sm-8"> <div class="col-sm-8">
<select name="usergroup" id="usergroup" data-placeholder="请选择用户组" class="chosen-select form-control m-b" multiple tabindex="2"> <select name="usergroup" id="usergroup" data-placeholder="请选择用户组" class="chosen-select form-control m-b" multiple tabindex="2">
{% for user_group in user_groups %} {% for user_group in user_groups %}
<option value="{{ user_group.name }}">{{ user_group.name }}</option> <option value="{{ user_group.name }}">{{ user_group.name }}</option>
{% endfor %} {% endfor %}
@ -86,7 +88,7 @@
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<label for="role" class="col-sm-2 control-label">角色<span class="red-fonts">*</span></label> <label for="role" class="col-sm-2 control-label">角色<span class="red-fonts">*</span></label>
<div class="col-sm-8"> <div class="col-sm-8" id="role_name">
<select name="role" data-placeholder="请选择角色" class="chosen-select form-control m-b" multiple tabindex="2"> <select name="role" data-placeholder="请选择角色" class="chosen-select form-control m-b" multiple tabindex="2">
{% for role in roles %} {% for role in roles %}
<option value="{{ role.name }}">{{ role.name }}</option> <option value="{{ role.name }}">{{ role.name }}</option>

View File

@ -0,0 +1,120 @@
{% extends 'base.html' %}
{% block self_head_css_js %}
<link href="/static/css/plugins/datapicker/datepicker3.css" rel="stylesheet">
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
{% endblock %}
{% load mytags %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" id="sudoForm" class="form-horizontal" action="">
{% if error %}
<div class="alert alert-warning text-center">{{ error }}</div>
{% endif %}
{% if msg %}
<div class="alert alert-success text-center">{{ msg }}</div>
{% endif %}
<div class="form-group">
<label for="sudo_name" class="col-sm-2 control-label">命令别名<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<input id="sudo_name" name="sudo_name" placeholder="Sudo Command Alias" type="text" class="form-control">
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="sudo_commands_label" class="col-sm-2 control-label">系统命令<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<textarea id="sudo_commands" name="sudo_commands" class="form-control" rows="3"></textarea>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="sudo_comment" class="col-sm-2 control-label">备注</label>
<div class="col-sm-8">
<input id="sudo_comment" name="sudo_comment" placeholder="Sudo Comment" type="text" class="form-control">
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">取消</button>
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block self_footer_js %}
<script>
$(document).ready(function(){
$("input.role").click(function(){
if($("input.role[value=GA]").is( ":checked" )){
$("#admin_groups").css("display", 'none');
}
else {
$("#admin_groups").css("display", 'block');
}
});
$('#use_password').click(function(){
if ($(this).is(':checked')){
$('#admin_account_password').css('display', 'block')
}
else {
$('#admin_account_password').css('display', 'none')
}
});
$('#use_publicKey').click(function(){
if ($(this).is(':checked')){
$('#admin_account_publicKey').css('display', 'block')
}
else {
$('#admin_account_publicKey').css('display', 'none')
}
});
});
var config = {
'.chosen-select' : {},
'.chosen-select-deselect' : {allow_single_deselect:true},
'.chosen-select-no-single' : {disable_search_threshold:10},
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
'.chosen-select-width' : {width:"95%"}
};
for (var selector in config) {
$(selector).chosen(config[selector]);
}
</script>
<script src="/static/js/cropper/cropper.min.js"></script>
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
{% endblock %}

View File

@ -0,0 +1,120 @@
{% extends 'base.html' %}
{% block self_head_css_js %}
<link href="/static/css/plugins/datapicker/datepicker3.css" rel="stylesheet">
<link href="/static/css/plugins/chosen/chosen.css" rel="stylesheet">
<script src="/static/js/plugins/chosen/chosen.jquery.js"></script>
{% endblock %}
{% load mytags %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>填写基本信息</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" id="sudoForm" class="form-horizontal" action="">
{% if error %}
<div class="alert alert-warning text-center">{{ error }}</div>
{% endif %}
{% if msg %}
<div class="alert alert-success text-center">{{ msg }}</div>
{% endif %}
<div class="form-group">
<label for="sudo_name" class="col-sm-2 control-label">命令别名<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<input id="sudo_name" name="sudo_name" placeholder="Sudo Command Alias" type="text" class="form-control" value={{ sudo.name }}>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="sudo_commands_label" class="col-sm-2 control-label">系统命令<span class="red-fonts">*</span></label>
<div class="col-sm-8">
<textarea id="sudo_commands" name="sudo_commands" class="form-control" rows="3">{{ sudo.commands }}</textarea>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<label for="sudo_comment" class="col-sm-2 control-label">备注</label>
<div class="col-sm-8">
<input id="sudo_comment" name="sudo_comment" placeholder="Sudo Comment" type="text" class="form-control" value={{ sudo.commnet }}>
</div>
</div>
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">取消</button>
<button id="submit_button" class="btn btn-primary" type="submit">确认保存</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block self_footer_js %}
<script>
$(document).ready(function(){
$("input.role").click(function(){
if($("input.role[value=GA]").is( ":checked" )){
$("#admin_groups").css("display", 'none');
}
else {
$("#admin_groups").css("display", 'block');
}
});
$('#use_password').click(function(){
if ($(this).is(':checked')){
$('#admin_account_password').css('display', 'block')
}
else {
$('#admin_account_password').css('display', 'none')
}
});
$('#use_publicKey').click(function(){
if ($(this).is(':checked')){
$('#admin_account_publicKey').css('display', 'block')
}
else {
$('#admin_account_publicKey').css('display', 'none')
}
});
});
var config = {
'.chosen-select' : {},
'.chosen-select-deselect' : {allow_single_deselect:true},
'.chosen-select-no-single' : {disable_search_threshold:10},
'.chosen-select-no-results': {no_results_text:'Oops, nothing found!'},
'.chosen-select-width' : {width:"95%"}
};
for (var selector in config) {
$(selector).chosen(config[selector]);
}
</script>
<script src="/static/js/cropper/cropper.min.js"></script>
<script src="/static/js/datapicker/bootstrap-datepicker.js"></script>
{% endblock %}

View File

@ -0,0 +1,112 @@
{% extends 'base.html' %}
{% load mytags %}
{% block content %}
{% include 'nav_cat_bar.html' %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-lg-10">
<div class="ibox float-e-margins">
<div>
{% if error %}
<div class="alert alert-warning text-center">{{ error }}</div>
{% endif %}
{% if msg %}
<div class="alert alert-success text-center">{{ msg }}</div>
{% endif %}
</div>
<div class="ibox-title">
<h5> 所有Sudo命令别名</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="">
<a href="/jperm/sudo/perm_sudo_add/" class="btn btn-sm btn-primary "> 添加别名 </a>
<a id="del_btn" class="btn btn-sm btn-danger "> 删除所选 </a>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" id="search_input" name="search" placeholder="Search">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
- 搜索 -
</button>
</div>
</div>
</form>
</div>
<table class="table table-striped table-bordered table-hover " id="editable" >
<thead>
<tr>
<th class="text-center">命令别名 </th>
<th class="text-center">系统命令</th>
<th class="text-center">创建时间</th>
<th class="text-center">操作</th>
</tr>
</thead>
<tbody id="edittbody">
{% for sudo in sudos %}
<tr class="gradeX" id={{ sudo.id }}>
<td class="text-center"> {{ sudo.name }} </td>
<td class="text-center"> {{ sudo.commands }} </td>
<td class="text-center"> {{ sudo.date_added | date:"Y-m-d H:i:s"}} </td>
<td class="text-center">
{# <a href="/jperm/sudo/perm_sudo_detail/?id={{ sudo.id }}" class="btn btn-xs btn-primary">详情</a>#}
<a href="/jperm/sudo/perm_sudo_edit/?id={{ sudo.id }}" class="btn btn-xs btn-info">编辑</a>
<button onclick="remove_sudo({{ sudo.id }})" class="btn btn-xs btn-danger">删除</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
<div class="col-sm-6">
<div class="dataTables_info" id="editable_info" role="status" aria-live="polite">
Showing {{ users.start_index }} to {{ users.end_index }} of {{ p.count }} entries
</div>
</div>
{% include 'paginator.html' %}
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function remove_sudo(sudo_id){
if (confirm("确认删除")) {
$.ajax({
type: "POST",
url: "/jperm/sudo/perm_sudo_delete/",
data: "id=" + sudo_id,
success: function(msg){
alert( "成功: " + msg );
var del_row = $('tbody#edittbody>tr#' + sudo_id);
del_row.remove()
},
error: function (msg) {
alert("失败: " + msg)
}
});
}
}
</script>
{% endblock %}

View File

@ -0,0 +1,31 @@
#!/bin/bash
sudo_file=/etc/sudoers
# Add Command Aliases
add_cmd_alias() {
{% for sudo in sudo_chosen_obj %}
if $(grep '^Cmnd_Alias {{ sudo.name }}' ${sudo_file} &> /dev/null); then
sed -i 's@^Cmnd_Alias.*{{ sudo.name }}.*@Cmnd_Alias {{ sudo.name }} = {{ sudo.commands }}@g' ${sudo_file}
else
echo "Cmnd_Alias {{ sudo.name }} = {{ sudo.commands }}" >> ${sudo_file}
fi
{% endfor %}
}
add_role_chosen() {
{% for role, alias in sudo_chosen_aliase.items %}
if $(grep '^{{ role }}' ${sudo_file} &> /dev/null); then
sed -i 's@^{{ role }}.*@{{ role }} ALL = {{ alias }}@g' ${sudo_file}
else
echo "{{ role }} ALL = {{ alias }}" >> ${sudo_file}
fi
{% endfor %}
}
add_cmd_alias
add_role_chosen

View File

@ -28,9 +28,15 @@
<a href="/jperm/rule/">授权规则</a> <a href="/jperm/rule/">授权规则</a>
</li> </li>
<li class="role"> <li class="sudo_list sudo_edit sudo_add cmd_list cmd_edit cmd_add sudo_detail">
<a href="/jperm/role/">系统角色</a> <a href="/jperm/role/">系统角色</a>
</li> </li>
<li class="sudo_list sudo_edit sudo_add cmd_list cmd_edit cmd_add sudo_detail">
<a href="/jperm/sudo/">Sudo命令</a>
</li>
<li class="apply_show online"><a href="/jperm/apply_show/online/">权限审批</a></li>
<li class="apply_show online"><a href="/jperm/log/">授权记录</a></li>
</ul> </ul>
</li> </li>
<li id="jlog"> <li id="jlog">