mirror of https://github.com/jumpserver/jumpserver
[future] 调整app架构
parent
fea76178ee
commit
32a5aec34e
|
@ -0,0 +1,3 @@
|
|||
from ansible import Tasker, AnsiblePlay, AnsibleTask, AnsibleHostResult
|
||||
from sudo import HostAlia, UserAlia, CmdAlia, RunasAlia, Privilege, Extra_conf, Sudo
|
||||
from cron import CronTable
|
|
@ -4,9 +4,7 @@ from __future__ import unicode_literals, absolute_import
|
|||
import logging
|
||||
import json
|
||||
|
||||
from jinja2 import Template
|
||||
from django.db import models
|
||||
from assets.models import Asset
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
|
@ -213,212 +211,6 @@ class AnsibleHostResult(models.Model):
|
|||
return {"msg": "deal with ping data failed, %s" % e.message, "data": None}
|
||||
|
||||
|
||||
class HostAlia(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Host_Alias'))
|
||||
host_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class UserAlia(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('User_Alias'))
|
||||
user_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class CmdAlia(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Command_Alias'))
|
||||
cmd_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class RunasAlia(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Runas_Alias'))
|
||||
runas_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Privilege(models.Model):
|
||||
user = models.ForeignKey(UserAlia, blank=True, null=True, related_name='privileges')
|
||||
host = models.ForeignKey(HostAlia, blank=True, null=True, related_name='privileges')
|
||||
runas = models.ForeignKey(RunasAlia, blank=True, null=True, related_name='privileges')
|
||||
command = models.ForeignKey(CmdAlia, blank=True, null=True, related_name='privileges')
|
||||
nopassword = models.BooleanField(default=True, verbose_name=_('Is_NoPassword'))
|
||||
|
||||
def __unicode__(self):
|
||||
return "[%s %s %s %s %s]" % (self.user.name,
|
||||
self.host.name,
|
||||
self.runas.name,
|
||||
self.command.name,
|
||||
self.nopassword)
|
||||
|
||||
def to_tuple(self):
|
||||
return self.user.name, self.host.name, self.runas.name, self.command.name, self.nopassword
|
||||
|
||||
|
||||
class Extra_conf(models.Model):
|
||||
line = models.TextField(blank=True, null=True, verbose_name=_('Extra_Item'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.line
|
||||
|
||||
|
||||
class Sudo(models.Model):
|
||||
"""
|
||||
Sudo配置文件对象, 用于配置sudo的配置文件
|
||||
|
||||
:param extra_lines: <list> [<line1>, <line2>,...]
|
||||
:param privileges: <list> [(user, host, runas, command, nopassword),]
|
||||
"""
|
||||
|
||||
asset = models.ForeignKey(Asset, null=True, blank=True, related_name='sudos')
|
||||
extra_lines = models.ManyToManyField(Extra_conf, related_name='sudos', blank=True)
|
||||
privilege_items = models.ManyToManyField(Privilege, related_name='sudos', blank=True)
|
||||
|
||||
@property
|
||||
def users(self):
|
||||
return {privilege.user.name: privilege.user.user_items.split(',') for privilege in self.privilege_items.all()}
|
||||
|
||||
@property
|
||||
def commands(self):
|
||||
return {privilege.command.name: privilege.command.cmd_items.split(',') for privilege in self.privilege_items.all()}
|
||||
|
||||
@property
|
||||
def hosts(self):
|
||||
return {privilege.host.name: privilege.host.host_items.split(',') for privilege in self.privilege_items.all()}
|
||||
|
||||
@property
|
||||
def runas(self):
|
||||
return {privilege.runas.name: privilege.runas.runas_items.split(',') for privilege in self.privilege_items.all()}
|
||||
|
||||
@property
|
||||
def extras(self):
|
||||
return [extra.line for extra in self.extra_lines.all()]
|
||||
|
||||
@property
|
||||
def privileges(self):
|
||||
return [privilege.to_tuple() for privilege in self.privilege_items.all()]
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
template = Template(self.__sudoers_jinja2_tmp__)
|
||||
context = {"User_Alias": self.users,
|
||||
"Cmnd_Alias": self.commands,
|
||||
"Host_Alias": self.hosts,
|
||||
"Runas_Alias": self.runas,
|
||||
"Extra_Lines": self.extras,
|
||||
"Privileges": self.privileges}
|
||||
return template.render(context)
|
||||
|
||||
@property
|
||||
def __sudoers_jinja2_tmp__(self):
|
||||
return """# management by JumpServer
|
||||
# This file MUST be edited with the 'visudo' command as root.
|
||||
#
|
||||
# Please consider adding local content in /etc/sudoers.d/ instead of
|
||||
# directly modifying this file.
|
||||
#
|
||||
# See the man page for details on how to write a sudoers file.
|
||||
#
|
||||
Defaults env_reset
|
||||
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
# JumpServer Generate Other Configure is here
|
||||
{% if Extra_Lines -%}
|
||||
{% for line in Extra_Lines -%}
|
||||
{{ line }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# Host alias specification
|
||||
{% if Host_Alias -%}
|
||||
{% for flag, items in Host_Alias.iteritems() -%}
|
||||
Host_Alias {{ flag }} = {{ items|join(', ') }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# User alias specification
|
||||
{% if User_Alias -%}
|
||||
{% for flag, items in User_Alias.iteritems() -%}
|
||||
User_Alias {{ flag }} = {{ items|join(', ') }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
|
||||
# Cmnd alias specification
|
||||
{% if Cmnd_Alias -%}
|
||||
{% for flag, items in Cmnd_Alias.iteritems() -%}
|
||||
Cmnd_Alias {{ flag }} = {{ items|join(', ') }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# Run as alias specification
|
||||
{% if Runas_Alias -%}
|
||||
{% for flag, items in Runas_Alias.iteritems() -%}
|
||||
Runas_Alias {{ flag }} = {{ items|join(', ') }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# User privilege specification
|
||||
root ALL=(ALL:ALL) ALL
|
||||
|
||||
# JumpServer Generate User privilege is here.
|
||||
# Note privileges is a tuple list like [(user, host, runas, command, nopassword),]
|
||||
{% if Privileges -%}
|
||||
{% for User_Flag, Host_Flag, Runas_Flag, Command_Flag, NopassWord in Privileges -%}
|
||||
{% if NopassWord -%}
|
||||
{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) NOPASSWD: {{ Command_Flag }}
|
||||
{%- else -%}
|
||||
{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) {{ Command_Flag }}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# Members of the admin group may gain root privileges
|
||||
%admin ALL=(ALL) ALL
|
||||
|
||||
# Allow members of group sudo to execute any command
|
||||
%sudo ALL=(ALL:ALL) ALL
|
||||
|
||||
# See sudoers(5) for more information on "#include" directives:
|
||||
|
||||
#includedir /etc/sudoers.d
|
||||
"""
|
||||
|
||||
|
||||
class CronTable(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Name'),
|
||||
help_text=_("Description of a crontab entry"))
|
||||
month = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Month'),
|
||||
help_text=_("Month of the year the job should run ( 1-12, *, */2, etc )"))
|
||||
weekday = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('WeekDay'),
|
||||
help_text=_("Day of the week that the job should run"
|
||||
" ( 0-6 for Sunday-Saturday, *, etc )"))
|
||||
day = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Day'),
|
||||
help_text=_("Day of the month the job should run ( 1-31, *, */2, etc )"))
|
||||
hour = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hour'),
|
||||
help_text=_("Hour when the job should run ( 0-23, *, */2, etc )"))
|
||||
minute = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Minute'),
|
||||
help_text=_("Minute when the job should run ( 0-59, *, */2, etc )"))
|
||||
job = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('Job'),
|
||||
help_text=_("The command to execute or, if env is set, the value of "
|
||||
"environment variable. Required if state=present."))
|
||||
user = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('User'),
|
||||
help_text=_("The specific user whose crontab should be modified."))
|
||||
asset = models.ForeignKey(Asset, null=True, blank=True, related_name='crontables')
|
||||
|
||||
@property
|
||||
def describe(self):
|
||||
return "http://docs.ansible.com/ansible/cron_module.html"
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from django.db import models
|
||||
from assets.models import Asset
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class CronTable(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Name'),
|
||||
help_text=_("Description of a crontab entry"))
|
||||
month = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Month'),
|
||||
help_text=_("Month of the year the job should run ( 1-12, *, */2, etc )"))
|
||||
weekday = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('WeekDay'),
|
||||
help_text=_("Day of the week that the job should run"
|
||||
" ( 0-6 for Sunday-Saturday, *, etc )"))
|
||||
day = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Day'),
|
||||
help_text=_("Day of the month the job should run ( 1-31, *, */2, etc )"))
|
||||
hour = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hour'),
|
||||
help_text=_("Hour when the job should run ( 0-23, *, */2, etc )"))
|
||||
minute = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Minute'),
|
||||
help_text=_("Minute when the job should run ( 0-59, *, */2, etc )"))
|
||||
job = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('Job'),
|
||||
help_text=_("The command to execute or, if env is set, the value of "
|
||||
"environment variable. Required if state=present."))
|
||||
user = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('User'),
|
||||
help_text=_("The specific user whose crontab should be modified."))
|
||||
asset = models.ForeignKey(Asset, null=True, blank=True, related_name='crontables')
|
||||
|
||||
@property
|
||||
def describe(self):
|
||||
return "http://docs.ansible.com/ansible/cron_module.html"
|
|
@ -0,0 +1,187 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from jinja2 import Template
|
||||
from django.db import models
|
||||
from assets.models import Asset
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class HostAlia(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Host_Alias'))
|
||||
host_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class UserAlia(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('User_Alias'))
|
||||
user_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class CmdAlia(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Command_Alias'))
|
||||
cmd_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class RunasAlia(models.Model):
|
||||
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Runas_Alias'))
|
||||
runas_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Privilege(models.Model):
|
||||
user = models.ForeignKey(UserAlia, blank=True, null=True, related_name='privileges')
|
||||
host = models.ForeignKey(HostAlia, blank=True, null=True, related_name='privileges')
|
||||
runas = models.ForeignKey(RunasAlia, blank=True, null=True, related_name='privileges')
|
||||
command = models.ForeignKey(CmdAlia, blank=True, null=True, related_name='privileges')
|
||||
nopassword = models.BooleanField(default=True, verbose_name=_('Is_NoPassword'))
|
||||
|
||||
def __unicode__(self):
|
||||
return "[%s %s %s %s %s]" % (self.user.name,
|
||||
self.host.name,
|
||||
self.runas.name,
|
||||
self.command.name,
|
||||
self.nopassword)
|
||||
|
||||
def to_tuple(self):
|
||||
return self.user.name, self.host.name, self.runas.name, self.command.name, self.nopassword
|
||||
|
||||
|
||||
class Extra_conf(models.Model):
|
||||
line = models.TextField(blank=True, null=True, verbose_name=_('Extra_Item'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.line
|
||||
|
||||
|
||||
class Sudo(models.Model):
|
||||
"""
|
||||
Sudo配置文件对象, 用于配置sudo的配置文件
|
||||
|
||||
:param extra_lines: <list> [<line1>, <line2>,...]
|
||||
:param privileges: <list> [(user, host, runas, command, nopassword),]
|
||||
"""
|
||||
|
||||
asset = models.ForeignKey(Asset, null=True, blank=True, related_name='sudos')
|
||||
extra_lines = models.ManyToManyField(Extra_conf, related_name='sudos', blank=True)
|
||||
privilege_items = models.ManyToManyField(Privilege, related_name='sudos', blank=True)
|
||||
|
||||
@property
|
||||
def users(self):
|
||||
return {privilege.user.name: privilege.user.user_items.split(',') for privilege in self.privilege_items.all()}
|
||||
|
||||
@property
|
||||
def commands(self):
|
||||
return {privilege.command.name: privilege.command.cmd_items.split(',') for privilege in self.privilege_items.all()}
|
||||
|
||||
@property
|
||||
def hosts(self):
|
||||
return {privilege.host.name: privilege.host.host_items.split(',') for privilege in self.privilege_items.all()}
|
||||
|
||||
@property
|
||||
def runas(self):
|
||||
return {privilege.runas.name: privilege.runas.runas_items.split(',') for privilege in self.privilege_items.all()}
|
||||
|
||||
@property
|
||||
def extras(self):
|
||||
return [extra.line for extra in self.extra_lines.all()]
|
||||
|
||||
@property
|
||||
def privileges(self):
|
||||
return [privilege.to_tuple() for privilege in self.privilege_items.all()]
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
template = Template(self.__sudoers_jinja2_tmp__)
|
||||
context = {"User_Alias": self.users,
|
||||
"Cmnd_Alias": self.commands,
|
||||
"Host_Alias": self.hosts,
|
||||
"Runas_Alias": self.runas,
|
||||
"Extra_Lines": self.extras,
|
||||
"Privileges": self.privileges}
|
||||
return template.render(context)
|
||||
|
||||
@property
|
||||
def __sudoers_jinja2_tmp__(self):
|
||||
return """# management by JumpServer
|
||||
# This file MUST be edited with the 'visudo' command as root.
|
||||
#
|
||||
# Please consider adding local content in /etc/sudoers.d/ instead of
|
||||
# directly modifying this file.
|
||||
#
|
||||
# See the man page for details on how to write a sudoers file.
|
||||
#
|
||||
Defaults env_reset
|
||||
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
# JumpServer Generate Other Configure is here
|
||||
{% if Extra_Lines -%}
|
||||
{% for line in Extra_Lines -%}
|
||||
{{ line }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# Host alias specification
|
||||
{% if Host_Alias -%}
|
||||
{% for flag, items in Host_Alias.iteritems() -%}
|
||||
Host_Alias {{ flag }} = {{ items|join(', ') }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# User alias specification
|
||||
{% if User_Alias -%}
|
||||
{% for flag, items in User_Alias.iteritems() -%}
|
||||
User_Alias {{ flag }} = {{ items|join(', ') }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
|
||||
# Cmnd alias specification
|
||||
{% if Cmnd_Alias -%}
|
||||
{% for flag, items in Cmnd_Alias.iteritems() -%}
|
||||
Cmnd_Alias {{ flag }} = {{ items|join(', ') }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# Run as alias specification
|
||||
{% if Runas_Alias -%}
|
||||
{% for flag, items in Runas_Alias.iteritems() -%}
|
||||
Runas_Alias {{ flag }} = {{ items|join(', ') }}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# User privilege specification
|
||||
root ALL=(ALL:ALL) ALL
|
||||
|
||||
# JumpServer Generate User privilege is here.
|
||||
# Note privileges is a tuple list like [(user, host, runas, command, nopassword),]
|
||||
{% if Privileges -%}
|
||||
{% for User_Flag, Host_Flag, Runas_Flag, Command_Flag, NopassWord in Privileges -%}
|
||||
{% if NopassWord -%}
|
||||
{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) NOPASSWD: {{ Command_Flag }}
|
||||
{%- else -%}
|
||||
{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) {{ Command_Flag }}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
# Members of the admin group may gain root privileges
|
||||
%admin ALL=(ALL) ALL
|
||||
|
||||
# Allow members of group sudo to execute any command
|
||||
%sudo ALL=(ALL:ALL) ALL
|
||||
|
||||
# See sudoers(5) for more information on "#include" directives:
|
||||
|
||||
#includedir /etc/sudoers.d
|
||||
"""
|
|
@ -1,9 +1,9 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from celery import shared_task
|
||||
from common import celery_app
|
||||
|
||||
from ops.ansible_api import Config, ADHocRunner
|
||||
from common import celery_app
|
||||
from ops.utils.ansible_api import Config, ADHocRunner
|
||||
|
||||
|
||||
@shared_task(name="get_asset_hardware_info")
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load bootstrap %}
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<h5>{% block user_template_title %}{% trans 'Create user' %}{% endblock %}</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" class="form-horizontal" action="" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<h3>{% trans 'Account' %}</h3>
|
||||
{% block username %} {% endblock %}
|
||||
{{ form.email|bootstrap_horizontal }}
|
||||
{{ form.name|bootstrap_horizontal }}
|
||||
{{ form.groups|bootstrap_horizontal }}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
{% block password %} {% endblock %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Security and Role' %}</h3>
|
||||
{{ form.role|bootstrap_horizontal }}
|
||||
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
|
||||
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group date">
|
||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
|
||||
</div>
|
||||
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{# {{ form.date_expired|bootstrap_horizontal }}#}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
|
||||
<div class="col-sm-8">
|
||||
{{ form.enable_otp }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Profile' %}</h3>
|
||||
{{ form.phone|bootstrap_horizontal }}
|
||||
{{ form.wechat|bootstrap_horizontal }}
|
||||
{{ form.comment|bootstrap_horizontal }}
|
||||
<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">{% trans 'Reset' %}</button>
|
||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
|
||||
$('.input-group.date').datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
todayBtn: "linked",
|
||||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
calendarWeeks: true,
|
||||
autoclose: true
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,10 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'cron/_cron.html' %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap %}
|
||||
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
|
||||
{% block username %}
|
||||
{{ form.username|bootstrap_horizontal }}
|
||||
{% endblock %}
|
||||
{% block password %}
|
||||
<h3>{% trans 'Password' %}</h3>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{% trans 'Password' %}</label>
|
||||
<div class="col-sm-8 controls" >
|
||||
{% trans 'Reset link will be generated and sent to the user. ' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,10 +1,393 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
{% extends 'base.html' %}
|
||||
{% load common_tags %}
|
||||
{% load users_tags %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="panel-options">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a href="{% url 'users:user-detail' pk=user_object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'users:user-asset-permission' pk=user_object.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
|
||||
</li>
|
||||
<li><a href="{% url 'users:user-granted-asset' pk=user_object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a></li>
|
||||
<li><a href="{% url 'users:user-login-history' pk=user_object.id %}" class="text-center"><i class="fa fa-calculator-o"></i> {% trans 'Login history' %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<div class="col-sm-7" style="padding-left: 0">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label"><b>{{ user_object.name }}</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">
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<img src="{{ user_object | user_avatar_url }}" class="img-circle" width="64" height="64">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">{% trans 'Name' %}:</td>
|
||||
<td><b>{{ user_object.name }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Username' %}:</td>
|
||||
<td><b>{{ user_object.username }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Email' %}:</td>
|
||||
<td><b>{{ user_object.email }}</b></td>
|
||||
</tr>
|
||||
{% if user_object.phone %}
|
||||
<tr>
|
||||
<td>{% trans 'Phone' %}:</td>
|
||||
<td><b>{{ user_object.phone }}</b></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if user_object.wechat %}
|
||||
<tr>
|
||||
<td>{% trans 'Wechat' %}:</td>
|
||||
<td><b>{{ user_object.wechat }}</b></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>{% trans 'Role' %}:</td>
|
||||
<td><b>{{ user_object.get_role_display }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date expired' %}:</td>
|
||||
<td><b>{{ user_object.date_expired|date:"Y-m-j H:i:s" }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Created by' %}:</td>
|
||||
<td><b>{{ user_object.created_by }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date joined' %}:</td>
|
||||
<td><b>{{ user_object.date_joined|date:"Y-m-j H:i:s" }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Last login' %}:</td>
|
||||
<td><b>{{ user_object.last_login|date:"Y-m-j H:i:s" }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Comment' %}:</td>
|
||||
<td><b>{{ user_object.comment }}</b></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td width="50%">{% trans 'Active' %}:</td>
|
||||
<td><span class="pull-right">
|
||||
<div class="switch">
|
||||
<div class="onoffswitch">
|
||||
<input type="checkbox" {% if user_object.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
|
||||
<label class="onoffswitch-label" for="is_active">
|
||||
<span class="onoffswitch-inner"></span>
|
||||
<span class="onoffswitch-switch"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Enable OTP' %}:</td>
|
||||
<td><span class="pull-right">
|
||||
<div class="switch">
|
||||
<div class="onoffswitch">
|
||||
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.enable_otp %} checked {% endif %}
|
||||
id="enable_otp">
|
||||
<label class="onoffswitch-label" for="enable_otp">
|
||||
<span class="onoffswitch-inner"></span>
|
||||
<span class="onoffswitch-switch"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Reset password' %}:</td>
|
||||
<td>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Reset' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Reset ssh key' %}:</td>
|
||||
<td>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;">{% trans 'Reset' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Update ssh key' %}:</td>
|
||||
<td>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn_update_pk" style="width: 54px;" data-toggle="modal" data-target="#user_update_pk_modal">{% trans 'Update' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">
|
||||
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table group_edit">
|
||||
<tbody>
|
||||
<form>
|
||||
<tr>
|
||||
<td colspan="2" class="no-borders">
|
||||
<select data-placeholder="{% trans 'Join user groups' %}" id="slct_groups" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for group in groups %}
|
||||
<option value="{{ group.id }}" id="opt_{{ group.id }}">{{ group.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="no-borders">
|
||||
<button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
|
||||
{% for group in user_object.groups.all %}
|
||||
<tr>
|
||||
<td ><b class="bdg_user_group" data-gid={{ group.id }}>{{ group.name }}</b></td>
|
||||
<td>
|
||||
<button class="btn btn-danger pull-right btn-xs btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'users/_user_update_pk_modal.html' %}
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
jumpserver.selected_groups = {};
|
||||
|
||||
function updateUserGroups(user_groups) {
|
||||
var the_url = "{% url 'users:group-user-edit-api' pk=user_object.id %}";
|
||||
var body = {
|
||||
id: {{ user_object.id }},
|
||||
groups: Object.assign([], user_groups)
|
||||
};
|
||||
var success = function(data) {
|
||||
// remove all the selected groups from select > option and rendered ul element;
|
||||
$('.select2-selection__rendered').empty();
|
||||
$('#slct_groups').val('');
|
||||
$.map(jumpserver.selected_groups, function(group_name, index) {
|
||||
$('#opt_' + index).remove();
|
||||
// change tr html of user groups.
|
||||
$('.group_edit tbody').append(
|
||||
'<tr>' +
|
||||
'<td><b class="bdg_user_group" data-gid="' + index + '">' + group_name + '</b></td>' +
|
||||
'<td><button class="btn btn-danger btn-xs pull-right btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||
'</tr>'
|
||||
)
|
||||
});
|
||||
// clear jumpserver.selected_groups
|
||||
jumpserver.selected_groups = {};
|
||||
toastr.success('{% trans "UserGroup Update Success!" %}')
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success: success,
|
||||
method: 'PUT'
|
||||
});
|
||||
}
|
||||
$(document).ready(function() {
|
||||
$('.select2').select2()
|
||||
.on('select2:select', function(evt) {
|
||||
var data = evt.params.data;
|
||||
jumpserver.selected_groups[data.id] = data.text;
|
||||
}).on('select2:unselect', function(evt) {
|
||||
var data = evt.params.data;
|
||||
delete jumpserver.selected_groups[data.id]
|
||||
})
|
||||
}).on('click', '#is_active', function() {
|
||||
var the_url = "{% url 'users:user-patch-api' pk=user_object.id %}";
|
||||
var checked = !$(this).prop('checked');
|
||||
var body = {
|
||||
'is_active': checked
|
||||
};
|
||||
var success = '{% trans "Update Successfully!" %}';
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success_message: success
|
||||
});
|
||||
}).on('click', '#enable_otp', function() {
|
||||
var the_url = "{% url 'users:user-patch-api' pk=user_object.id %}";
|
||||
var checked = !$(this).prop('checked');
|
||||
var body = {
|
||||
'enable_otp': checked
|
||||
};
|
||||
var success = '{% trans "Update Successfully!" %}';
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success_message: success
|
||||
});
|
||||
}).on('click', '#btn_add_user_group', function() {
|
||||
if (Object.keys(jumpserver.selected_groups).length === 0) {
|
||||
return false;
|
||||
}
|
||||
var user_groups = $('.bdg_user_group').map(function() {
|
||||
return $(this).data('gid');
|
||||
}).get();
|
||||
$.map(jumpserver.selected_groups, function(value, index) {
|
||||
user_groups.push(parseInt(index));
|
||||
$('#opt_' + index).remove();
|
||||
});
|
||||
updateUserGroups(user_groups)
|
||||
}).on('click', '.btn_delete_user_group', function() {
|
||||
var $this = $(this);
|
||||
var $tr = $this.closest('tr');
|
||||
var $badge = $tr.find('.bdg_user_group');
|
||||
var gid = $badge.data('gid');
|
||||
var group_name = $badge.html() || $badge.text();
|
||||
$('#slct_groups').append(
|
||||
'<option value="' + gid + '" id="opt_' + gid + '">' + group_name + '</option>'
|
||||
);
|
||||
$tr.remove();
|
||||
var user_groups = $('.bdg_user_group').map(function() {
|
||||
return $(this).data('gid');
|
||||
}).get();
|
||||
updateUserGroups(user_groups)
|
||||
}).on('click', '#btn_reset_password', function() {
|
||||
function doReset() {
|
||||
var the_url = '{% url "users:user-reset-password-api" pk=user_object.id %}';
|
||||
var body = {};
|
||||
var success = function() {
|
||||
var msg = "{% trans 'E-mail sent successfully. An e-mail has been sent to the user\'s mailbox.' %}";
|
||||
swal("{% trans 'Password-Reset' %}", msg, "success");
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success: success
|
||||
});
|
||||
}
|
||||
swal({
|
||||
title: "{% trans 'Are you sure?' %}",
|
||||
text: "{% trans 'This will reset the user\'s password.' %}",
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: false
|
||||
}, function() {
|
||||
doReset();
|
||||
});
|
||||
}).on('click', '#btn_reset_pk', function() {
|
||||
function doReset() {
|
||||
var the_url = '{% url "users:user-reset-pk-api" pk=user_object.id %}';
|
||||
var body = {};
|
||||
var success = function() {
|
||||
var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}";
|
||||
swal("{% trans 'SSH-Public-Key Reset' %}", msg, "success");
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success: success
|
||||
});
|
||||
}
|
||||
swal({
|
||||
title: "{% trans 'Are you sure?' %}",
|
||||
text: "{% trans 'This will reset the user\'s public key.' %}",
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: false
|
||||
}, function() {
|
||||
doReset();
|
||||
});
|
||||
}).on('click', '#btn_user_update_pk', function(){
|
||||
var $this = $(this);
|
||||
var pk = $('#txt_pk').val();
|
||||
var the_url = '{% url "users:user-update-pk-api" pk=user_object.id %}';
|
||||
var body = {'_public_key': pk};
|
||||
var success = function() {
|
||||
$('#txt_pk').val('');
|
||||
$this.closest('.modal').modal('hide');
|
||||
var msg = "{% trans 'Successfully updated the SSH public key.' %}";
|
||||
swal("{% trans 'User SSH Public Key Update' %}", msg, "success");
|
||||
};
|
||||
var fail = function() {
|
||||
var msg = "{% trans 'Failed to update the user\'s SSH public key.' %}";
|
||||
swal({
|
||||
title: "{% trans 'User SSH Public Key Update' %}",
|
||||
text: msg,
|
||||
type: "error",
|
||||
showCancelButton: false,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: true
|
||||
}, function () {
|
||||
$('#txt_pk').focus();
|
||||
}
|
||||
);
|
||||
}
|
||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'cron/_cron.html' %}
|
||||
{% load i18n %}
|
||||
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
|
||||
{% block username %}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
|
||||
<div class="col-sm-9 controls" >
|
||||
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user_object.username }}" readonly class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block password %}
|
||||
<h3>{% trans 'Password' %}</h3>
|
||||
<div class="form-group">
|
||||
<label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
|
||||
<div class="col-sm-9 controls" >
|
||||
<input id="password" name="password" type="password" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load bootstrap %}
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<h5>{% block user_template_title %}{% trans 'Create user' %}{% endblock %}</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" class="form-horizontal" action="" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<h3>{% trans 'Account' %}</h3>
|
||||
{% block username %} {% endblock %}
|
||||
{{ form.email|bootstrap_horizontal }}
|
||||
{{ form.name|bootstrap_horizontal }}
|
||||
{{ form.groups|bootstrap_horizontal }}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
{% block password %} {% endblock %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Security and Role' %}</h3>
|
||||
{{ form.role|bootstrap_horizontal }}
|
||||
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
|
||||
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group date">
|
||||
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
|
||||
</div>
|
||||
<span class="help-block ">{{ form.date_expired.errors }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{# {{ form.date_expired|bootstrap_horizontal }}#}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
|
||||
<div class="col-sm-8">
|
||||
{{ form.enable_otp }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hr-line-dashed"></div>
|
||||
<h3>{% trans 'Profile' %}</h3>
|
||||
{{ form.phone|bootstrap_horizontal }}
|
||||
{{ form.wechat|bootstrap_horizontal }}
|
||||
{{ form.comment|bootstrap_horizontal }}
|
||||
<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">{% trans 'Reset' %}</button>
|
||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
|
||||
$('.input-group.date').datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
todayBtn: "linked",
|
||||
keyboardNavigation: false,
|
||||
forceParse: false,
|
||||
calendarWeeks: true,
|
||||
autoclose: true
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,10 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'sudo/_sudo.html' %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap %}
|
||||
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
|
||||
{% block username %}
|
||||
{{ form.username|bootstrap_horizontal }}
|
||||
{% endblock %}
|
||||
{% block password %}
|
||||
<h3>{% trans 'Password' %}</h3>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{% trans 'Password' %}</label>
|
||||
<div class="col-sm-8 controls" >
|
||||
{% trans 'Reset link will be generated and sent to the user. ' %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,10 +1,393 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
{% extends 'base.html' %}
|
||||
{% load common_tags %}
|
||||
{% load users_tags %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
|
||||
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="panel-options">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a href="{% url 'users:user-detail' pk=user_object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'users:user-asset-permission' pk=user_object.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
|
||||
</li>
|
||||
<li><a href="{% url 'users:user-granted-asset' pk=user_object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a></li>
|
||||
<li><a href="{% url 'users:user-login-history' pk=user_object.id %}" class="text-center"><i class="fa fa-calculator-o"></i> {% trans 'Login history' %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<div class="col-sm-7" style="padding-left: 0">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span class="label"><b>{{ user_object.name }}</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">
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<img src="{{ user_object | user_avatar_url }}" class="img-circle" width="64" height="64">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">{% trans 'Name' %}:</td>
|
||||
<td><b>{{ user_object.name }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Username' %}:</td>
|
||||
<td><b>{{ user_object.username }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Email' %}:</td>
|
||||
<td><b>{{ user_object.email }}</b></td>
|
||||
</tr>
|
||||
{% if user_object.phone %}
|
||||
<tr>
|
||||
<td>{% trans 'Phone' %}:</td>
|
||||
<td><b>{{ user_object.phone }}</b></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if user_object.wechat %}
|
||||
<tr>
|
||||
<td>{% trans 'Wechat' %}:</td>
|
||||
<td><b>{{ user_object.wechat }}</b></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>{% trans 'Role' %}:</td>
|
||||
<td><b>{{ user_object.get_role_display }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date expired' %}:</td>
|
||||
<td><b>{{ user_object.date_expired|date:"Y-m-j H:i:s" }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Created by' %}:</td>
|
||||
<td><b>{{ user_object.created_by }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date joined' %}:</td>
|
||||
<td><b>{{ user_object.date_joined|date:"Y-m-j H:i:s" }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Last login' %}:</td>
|
||||
<td><b>{{ user_object.last_login|date:"Y-m-j H:i:s" }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Comment' %}:</td>
|
||||
<td><b>{{ user_object.comment }}</b></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading">
|
||||
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td width="50%">{% trans 'Active' %}:</td>
|
||||
<td><span class="pull-right">
|
||||
<div class="switch">
|
||||
<div class="onoffswitch">
|
||||
<input type="checkbox" {% if user_object.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
|
||||
<label class="onoffswitch-label" for="is_active">
|
||||
<span class="onoffswitch-inner"></span>
|
||||
<span class="onoffswitch-switch"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Enable OTP' %}:</td>
|
||||
<td><span class="pull-right">
|
||||
<div class="switch">
|
||||
<div class="onoffswitch">
|
||||
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.enable_otp %} checked {% endif %}
|
||||
id="enable_otp">
|
||||
<label class="onoffswitch-label" for="enable_otp">
|
||||
<span class="onoffswitch-inner"></span>
|
||||
<span class="onoffswitch-switch"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Reset password' %}:</td>
|
||||
<td>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Reset' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Reset ssh key' %}:</td>
|
||||
<td>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;">{% trans 'Reset' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Update ssh key' %}:</td>
|
||||
<td>
|
||||
<span class="pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs" id="btn_update_pk" style="width: 54px;" data-toggle="modal" data-target="#user_update_pk_modal">{% trans 'Update' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading">
|
||||
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table group_edit">
|
||||
<tbody>
|
||||
<form>
|
||||
<tr>
|
||||
<td colspan="2" class="no-borders">
|
||||
<select data-placeholder="{% trans 'Join user groups' %}" id="slct_groups" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for group in groups %}
|
||||
<option value="{{ group.id }}" id="opt_{{ group.id }}">{{ group.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="no-borders">
|
||||
<button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
|
||||
{% for group in user_object.groups.all %}
|
||||
<tr>
|
||||
<td ><b class="bdg_user_group" data-gid={{ group.id }}>{{ group.name }}</b></td>
|
||||
<td>
|
||||
<button class="btn btn-danger pull-right btn-xs btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'users/_user_update_pk_modal.html' %}
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
jumpserver.selected_groups = {};
|
||||
|
||||
function updateUserGroups(user_groups) {
|
||||
var the_url = "{% url 'users:group-user-edit-api' pk=user_object.id %}";
|
||||
var body = {
|
||||
id: {{ user_object.id }},
|
||||
groups: Object.assign([], user_groups)
|
||||
};
|
||||
var success = function(data) {
|
||||
// remove all the selected groups from select > option and rendered ul element;
|
||||
$('.select2-selection__rendered').empty();
|
||||
$('#slct_groups').val('');
|
||||
$.map(jumpserver.selected_groups, function(group_name, index) {
|
||||
$('#opt_' + index).remove();
|
||||
// change tr html of user groups.
|
||||
$('.group_edit tbody').append(
|
||||
'<tr>' +
|
||||
'<td><b class="bdg_user_group" data-gid="' + index + '">' + group_name + '</b></td>' +
|
||||
'<td><button class="btn btn-danger btn-xs pull-right btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||
'</tr>'
|
||||
)
|
||||
});
|
||||
// clear jumpserver.selected_groups
|
||||
jumpserver.selected_groups = {};
|
||||
toastr.success('{% trans "UserGroup Update Success!" %}')
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success: success,
|
||||
method: 'PUT'
|
||||
});
|
||||
}
|
||||
$(document).ready(function() {
|
||||
$('.select2').select2()
|
||||
.on('select2:select', function(evt) {
|
||||
var data = evt.params.data;
|
||||
jumpserver.selected_groups[data.id] = data.text;
|
||||
}).on('select2:unselect', function(evt) {
|
||||
var data = evt.params.data;
|
||||
delete jumpserver.selected_groups[data.id]
|
||||
})
|
||||
}).on('click', '#is_active', function() {
|
||||
var the_url = "{% url 'users:user-patch-api' pk=user_object.id %}";
|
||||
var checked = !$(this).prop('checked');
|
||||
var body = {
|
||||
'is_active': checked
|
||||
};
|
||||
var success = '{% trans "Update Successfully!" %}';
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success_message: success
|
||||
});
|
||||
}).on('click', '#enable_otp', function() {
|
||||
var the_url = "{% url 'users:user-patch-api' pk=user_object.id %}";
|
||||
var checked = !$(this).prop('checked');
|
||||
var body = {
|
||||
'enable_otp': checked
|
||||
};
|
||||
var success = '{% trans "Update Successfully!" %}';
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success_message: success
|
||||
});
|
||||
}).on('click', '#btn_add_user_group', function() {
|
||||
if (Object.keys(jumpserver.selected_groups).length === 0) {
|
||||
return false;
|
||||
}
|
||||
var user_groups = $('.bdg_user_group').map(function() {
|
||||
return $(this).data('gid');
|
||||
}).get();
|
||||
$.map(jumpserver.selected_groups, function(value, index) {
|
||||
user_groups.push(parseInt(index));
|
||||
$('#opt_' + index).remove();
|
||||
});
|
||||
updateUserGroups(user_groups)
|
||||
}).on('click', '.btn_delete_user_group', function() {
|
||||
var $this = $(this);
|
||||
var $tr = $this.closest('tr');
|
||||
var $badge = $tr.find('.bdg_user_group');
|
||||
var gid = $badge.data('gid');
|
||||
var group_name = $badge.html() || $badge.text();
|
||||
$('#slct_groups').append(
|
||||
'<option value="' + gid + '" id="opt_' + gid + '">' + group_name + '</option>'
|
||||
);
|
||||
$tr.remove();
|
||||
var user_groups = $('.bdg_user_group').map(function() {
|
||||
return $(this).data('gid');
|
||||
}).get();
|
||||
updateUserGroups(user_groups)
|
||||
}).on('click', '#btn_reset_password', function() {
|
||||
function doReset() {
|
||||
var the_url = '{% url "users:user-reset-password-api" pk=user_object.id %}';
|
||||
var body = {};
|
||||
var success = function() {
|
||||
var msg = "{% trans 'E-mail sent successfully. An e-mail has been sent to the user\'s mailbox.' %}";
|
||||
swal("{% trans 'Password-Reset' %}", msg, "success");
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success: success
|
||||
});
|
||||
}
|
||||
swal({
|
||||
title: "{% trans 'Are you sure?' %}",
|
||||
text: "{% trans 'This will reset the user\'s password.' %}",
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: false
|
||||
}, function() {
|
||||
doReset();
|
||||
});
|
||||
}).on('click', '#btn_reset_pk', function() {
|
||||
function doReset() {
|
||||
var the_url = '{% url "users:user-reset-pk-api" pk=user_object.id %}';
|
||||
var body = {};
|
||||
var success = function() {
|
||||
var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}";
|
||||
swal("{% trans 'SSH-Public-Key Reset' %}", msg, "success");
|
||||
};
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
body: JSON.stringify(body),
|
||||
success: success
|
||||
});
|
||||
}
|
||||
swal({
|
||||
title: "{% trans 'Are you sure?' %}",
|
||||
text: "{% trans 'This will reset the user\'s public key.' %}",
|
||||
type: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: false
|
||||
}, function() {
|
||||
doReset();
|
||||
});
|
||||
}).on('click', '#btn_user_update_pk', function(){
|
||||
var $this = $(this);
|
||||
var pk = $('#txt_pk').val();
|
||||
var the_url = '{% url "users:user-update-pk-api" pk=user_object.id %}';
|
||||
var body = {'_public_key': pk};
|
||||
var success = function() {
|
||||
$('#txt_pk').val('');
|
||||
$this.closest('.modal').modal('hide');
|
||||
var msg = "{% trans 'Successfully updated the SSH public key.' %}";
|
||||
swal("{% trans 'User SSH Public Key Update' %}", msg, "success");
|
||||
};
|
||||
var fail = function() {
|
||||
var msg = "{% trans 'Failed to update the user\'s SSH public key.' %}";
|
||||
swal({
|
||||
title: "{% trans 'User SSH Public Key Update' %}",
|
||||
text: msg,
|
||||
type: "error",
|
||||
showCancelButton: false,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: true
|
||||
}, function () {
|
||||
$('#txt_pk').focus();
|
||||
}
|
||||
);
|
||||
}
|
||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{% extends 'sudo/_sudo.html' %}
|
||||
{% load i18n %}
|
||||
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
|
||||
{% block username %}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
|
||||
<div class="col-sm-9 controls" >
|
||||
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user_object.username }}" readonly class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block password %}
|
||||
<h3>{% trans 'Password' %}</h3>
|
||||
<div class="form-group">
|
||||
<label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
|
||||
<div class="col-sm-9 controls" >
|
||||
<input id="password" name="password" type="password" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,7 +18,7 @@ from ansible.utils.display import Display
|
|||
from ansible.playbook.play import Play
|
||||
from ansible.plugins.callback import CallbackBase
|
||||
|
||||
from models import Tasker, AnsiblePlay, AnsibleTask, AnsibleHostResult
|
||||
from ops.models import Tasker, AnsiblePlay, AnsibleTask, AnsibleHostResult
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
|
@ -11,6 +11,3 @@ class ListSudoPrivilegesMixin(object):
|
|||
|
||||
def get_all_privilege(self):
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
|||
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||
|
||||
from .hands import AdminUserRequiredMixin
|
||||
from .utils import CreateSudoPrivilegesMixin, ListSudoPrivilegesMixin
|
||||
from .utils.mixins import CreateSudoPrivilegesMixin, ListSudoPrivilegesMixin
|
||||
from models import *
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue