mirror of https://github.com/jumpserver/jumpserver
Merge branch 'master' of code.simcu.com:jumpserver/jumpserver
commit
7cff5cbf61
|
@ -1,6 +1,7 @@
|
|||
.DS_Store
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.swp
|
||||
env
|
||||
env*
|
||||
dist
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# coding:utf-8
|
||||
from django import forms
|
||||
|
||||
from .models import IDC, Asset, AssetGroup
|
||||
from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ class AssetForm(forms.ModelForm):
|
|||
|
||||
fields = [
|
||||
"ip", "other_ip", "remote_card_ip", "hostname", "port", "groups", "username", "password",
|
||||
"idc", "mac_addr", "brand", "cpu", "memory", "disk", "os", "cabinet_no", "cabinet_pos",
|
||||
"idc", "mac_address", "brand", "cpu", "memory", "disk", "os", "cabinet_no", "cabinet_pos",
|
||||
"number", "status", "type", "env", "sn", "is_active", "comment"
|
||||
]
|
||||
|
||||
|
@ -21,6 +21,7 @@ class AssetForm(forms.ModelForm):
|
|||
|
||||
|
||||
class AssetGroupForm(forms.ModelForm):
|
||||
# See AdminUserForm comment same it
|
||||
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
|
||||
label=_('Asset'),
|
||||
required=False,
|
||||
|
@ -51,6 +52,7 @@ class AssetGroupForm(forms.ModelForm):
|
|||
|
||||
|
||||
class IDCForm(forms.ModelForm):
|
||||
# See AdminUserForm comment same it
|
||||
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
|
||||
label=_('Asset'),
|
||||
required=False,
|
||||
|
@ -78,3 +80,135 @@ class IDCForm(forms.ModelForm):
|
|||
'network': forms.Textarea(
|
||||
attrs={'placeholder': '192.168.1.0/24\n192.168.2.0/24'})
|
||||
}
|
||||
|
||||
|
||||
class AdminUserForm(forms.ModelForm):
|
||||
# Admin user assets define, let user select, save it in form not in view
|
||||
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
|
||||
label=_('Asset'),
|
||||
required=False,
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
|
||||
)
|
||||
auto_generate_key = forms.BooleanField(required=True, initial=True)
|
||||
# Form field name can not start with `_`, so redefine it,
|
||||
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
|
||||
help_text=_('If also set private key, use that first'), required=False)
|
||||
# Need use upload private key file except paste private key content
|
||||
private_key_file = forms.FileField(required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# When update a admin user instance, initial it
|
||||
if kwargs.get('instance'):
|
||||
initial = kwargs.get('initial', {})
|
||||
initial['assets'] = kwargs['instance'].assets.all()
|
||||
super(AdminUserForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def _save_m2m(self):
|
||||
# Save assets relation with admin user
|
||||
super(AdminUserForm, self)._save_m2m()
|
||||
assets = self.cleaned_data['assets']
|
||||
self.instance.assets.clear()
|
||||
self.instance.assets.add(*tuple(assets))
|
||||
|
||||
def save(self, commit=True):
|
||||
# Because we define custom field, so we need rewrite :method: `save`
|
||||
admin_user = super(AdminUserForm, self).save(commit=commit)
|
||||
password = self.cleaned_data['password']
|
||||
private_key_file = self.cleaned_data['private_key_file']
|
||||
|
||||
if password:
|
||||
admin_user.password = password
|
||||
print(password)
|
||||
# Todo: Validate private key file, and generate public key
|
||||
# Todo: Auto generate private key and public key
|
||||
if private_key_file:
|
||||
admin_user.private_key = private_key_file.read()
|
||||
admin_user.save()
|
||||
return self.instance
|
||||
|
||||
class Meta:
|
||||
model = AdminUser
|
||||
fields = ['name', 'username', 'auto_generate_key', 'password', 'private_key_file', 'as_default', 'comment']
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
||||
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
||||
}
|
||||
help_texts = {
|
||||
'name': '* required',
|
||||
'username': '* required',
|
||||
}
|
||||
|
||||
|
||||
class SystemUserForm(forms.ModelForm):
|
||||
# Admin user assets define, let user select, save it in form not in view
|
||||
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
|
||||
label=_('Asset'),
|
||||
required=False,
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
|
||||
)
|
||||
asset_groups = forms.ModelMultipleChoiceField(queryset=AssetGroup.objects.all(),
|
||||
label=_('Asset group'),
|
||||
required=False,
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'select2',
|
||||
'data-placeholder': _('Select asset groups')})
|
||||
)
|
||||
auto_generate_key = forms.BooleanField(initial=True)
|
||||
# Form field name can not start with `_`, so redefine it,
|
||||
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
|
||||
help_text=_('If also set private key, use that first'), required=False)
|
||||
# Need use upload private key file except paste private key content
|
||||
private_key_file = forms.FileField(required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# When update a admin user instance, initial it
|
||||
if kwargs.get('instance'):
|
||||
initial = kwargs.get('initial', {})
|
||||
initial['assets'] = kwargs['instance'].assets.all()
|
||||
initial['asset_groups'] = kwargs['instance'].asset_groups.all()
|
||||
super(SystemUserForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def _save_m2m(self):
|
||||
# Save assets relation with admin user
|
||||
super(SystemUserForm, self)._save_m2m()
|
||||
assets = self.cleaned_data['assets']
|
||||
asset_groups = self.cleaned_data['asset_groups']
|
||||
self.instance.assets.clear()
|
||||
self.instance.assets.add(*tuple(assets))
|
||||
self.instance.asset_groups.clear()
|
||||
self.instance.asset_groups.add(*tuple(asset_groups))
|
||||
|
||||
def save(self, commit=True):
|
||||
# Because we define custom field, so we need rewrite :method: `save`
|
||||
system_user = super(SystemUserForm, self).save(commit=commit)
|
||||
password = self.cleaned_data['password']
|
||||
private_key_file = self.cleaned_data['private_key_file']
|
||||
|
||||
if password:
|
||||
system_user.password = password
|
||||
print(password)
|
||||
# Todo: Validate private key file, and generate public key
|
||||
# Todo: Auto generate private key and public key
|
||||
if private_key_file:
|
||||
system_user.private_key = private_key_file.read()
|
||||
system_user.save()
|
||||
return self.instance
|
||||
|
||||
class Meta:
|
||||
model = SystemUser
|
||||
fields = [
|
||||
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'as_default',
|
||||
'auto_push', 'auto_update', 'sudo', 'comment', 'shell', 'home', 'uid',
|
||||
]
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
||||
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
||||
}
|
||||
help_texts = {
|
||||
'name': '* required',
|
||||
'username': '* required',
|
||||
'auth_push': 'Auto push system user to asset',
|
||||
'auth_update': 'Auto update system user ssh key',
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10 on 2016-09-07 15:11
|
||||
# Generated by Django 1.10 on 2016-09-08 03:02
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
@ -18,15 +18,15 @@ class Migration(migrations.Migration):
|
|||
name='AdminUser',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(blank=True, max_length=128, null=True, unique=True, verbose_name='Name')),
|
||||
('username', models.CharField(blank=True, max_length=16, null=True, verbose_name='Username')),
|
||||
('password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key', models.CharField(blank=True, max_length=4096, null=True, verbose_name='SSH private key')),
|
||||
('is_default', models.BooleanField(default=True, verbose_name='As default')),
|
||||
('auto_update', models.BooleanField(default=True, verbose_name='Auto update pass/key')),
|
||||
('date_created', models.DateTimeField(auto_now=True, null=True)),
|
||||
('create_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
|
||||
('username', models.CharField(max_length=16, verbose_name='Username')),
|
||||
('_password', models.CharField(blank=True, max_length=256, verbose_name='Password')),
|
||||
('_private_key', models.CharField(blank=True, max_length=4096, verbose_name='SSH private key')),
|
||||
('_public_key', models.CharField(blank=True, max_length=4096, verbose_name='SSH public key')),
|
||||
('as_default', models.BooleanField(default=False, verbose_name='As default')),
|
||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
||||
('date_created', models.DateTimeField(auto_now=True, null=True)),
|
||||
('created_by', models.CharField(max_length=32, null=True, verbose_name='Created by')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'admin_user',
|
||||
|
@ -142,7 +142,7 @@ class Migration(migrations.Migration):
|
|||
('home', models.CharField(blank=True, max_length=64, verbose_name='Home')),
|
||||
('uid', models.IntegerField(blank=True, verbose_name='Uid')),
|
||||
('date_created', models.DateTimeField(auto_now=True, null=True)),
|
||||
('create_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
|
||||
('created_by', models.CharField(blank=True, max_length=32, verbose_name='Created by')),
|
||||
('comment', models.CharField(blank=True, max_length=128, verbose_name='Comment')),
|
||||
],
|
||||
options={
|
||||
|
|
|
@ -61,22 +61,39 @@ class AssetExtend(models.Model):
|
|||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
return '%(key)s: %(value)s' % {'key': self.key, 'value': self.value}
|
||||
|
||||
@classmethod
|
||||
def initial(cls):
|
||||
for k, v in (
|
||||
(_('status'), _('In use')),
|
||||
(_('status'), _('Out of use')),
|
||||
(_('type'), _('Server')),
|
||||
(_('type'), _('VM')),
|
||||
(_('type'), _('Switch')),
|
||||
(_('type'), _('Router')),
|
||||
(_('type'), _('Firewall')),
|
||||
(_('type'), _('Storage')),
|
||||
(_('env'), _('Production')),
|
||||
(_('env'), _('Development')),
|
||||
(_('env'), _('Testing')),
|
||||
):
|
||||
cls.objects.create(key=k, value=v, created_by='System')
|
||||
|
||||
class Meta:
|
||||
db_table = 'asset_extend'
|
||||
|
||||
|
||||
class AdminUser(models.Model):
|
||||
name = models.CharField(max_length=128, unique=True, null=True, blank=True, verbose_name=_('Name'))
|
||||
username = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Username'))
|
||||
_password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_('Password'))
|
||||
_private_key = models.CharField(max_length=4096, null=True, blank=True, verbose_name=_('SSH private key'))
|
||||
_public_key = models.CharField(max_length=4096, null=True, blank=True, verbose_name=_('SSH public key'))
|
||||
as_default = models.BooleanField(default=True, verbose_name=_('As default'))
|
||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
||||
username = models.CharField(max_length=16, verbose_name=_('Username'))
|
||||
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
|
||||
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
|
||||
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
||||
as_default = models.BooleanField(default=False, verbose_name=_('As default'))
|
||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||
date_created = models.DateTimeField(auto_now=True, null=True, blank=True)
|
||||
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
|
||||
date_created = models.DateTimeField(auto_now=True, null=True)
|
||||
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
@ -110,7 +127,7 @@ class AdminUser(models.Model):
|
|||
|
||||
@classmethod
|
||||
def generate_fake(cls, count=100):
|
||||
from random import seed, choice
|
||||
from random import seed
|
||||
import forgery_py
|
||||
from django.db import IntegrityError
|
||||
|
||||
|
@ -132,31 +149,74 @@ class AdminUser(models.Model):
|
|||
class SystemUser(models.Model):
|
||||
PROTOCOL_CHOICES = (
|
||||
('ssh', 'ssh'),
|
||||
('telnet', 'telnet'),
|
||||
)
|
||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
||||
username = models.CharField(max_length=16, blank=True, verbose_name=_('Username'))
|
||||
password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
|
||||
protocol = models.CharField(max_length=16, default='ssh', verbose_name=_('Protocol'))
|
||||
private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
|
||||
public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
||||
is_default = models.BooleanField(default=True, verbose_name=_('As default'))
|
||||
username = models.CharField(max_length=16, verbose_name=_('Username'))
|
||||
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
|
||||
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
|
||||
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
|
||||
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
||||
as_default = models.BooleanField(default=False, verbose_name=_('As default'))
|
||||
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
|
||||
auto_update = models.BooleanField(default=True, verbose_name=_('Auto update pass/key'))
|
||||
sudo = models.TextField(max_length=4096, blank=True, verbose_name=_('Sudo'))
|
||||
shell = models.CharField(max_length=64, blank=True, verbose_name=_('Shell'))
|
||||
sudo = models.TextField(max_length=4096, default='/user/bin/whoami', verbose_name=_('Sudo'))
|
||||
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
|
||||
home = models.CharField(max_length=64, blank=True, verbose_name=_('Home'))
|
||||
uid = models.IntegerField(blank=True, verbose_name=_('Uid'))
|
||||
date_created = models.DateTimeField(auto_now=True, null=True)
|
||||
uid = models.IntegerField(null=True, blank=True, verbose_name=_('Uid'))
|
||||
date_created = models.DateTimeField(auto_now=True)
|
||||
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
|
||||
comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment'))
|
||||
comment = models.TextField(max_length=128, blank=True, verbose_name=_('Comment'))
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
return decrypt(self._password)
|
||||
|
||||
@password.setter
|
||||
def password(self, password_raw):
|
||||
self._password = encrypt(password_raw)
|
||||
|
||||
@property
|
||||
def private_key(self):
|
||||
return decrypt(self._private_key)
|
||||
|
||||
@private_key.setter
|
||||
def private_key(self, private_key_raw):
|
||||
self._private_key = encrypt(private_key_raw)
|
||||
|
||||
@property
|
||||
def public_key(self):
|
||||
return decrypt(self._public_key)
|
||||
|
||||
@public_key.setter
|
||||
def public_key(self, public_key_raw):
|
||||
self._public_key = encrypt(public_key_raw)
|
||||
|
||||
class Meta:
|
||||
db_table = 'system_user'
|
||||
|
||||
@classmethod
|
||||
def generate_fake(cls, count=100):
|
||||
from random import seed
|
||||
import forgery_py
|
||||
from django.db import IntegrityError
|
||||
|
||||
seed()
|
||||
for i in range(count):
|
||||
obj = cls(name=forgery_py.name.full_name(),
|
||||
username=forgery_py.internet.user_name(),
|
||||
password=forgery_py.lorem_ipsum.word(),
|
||||
comment=forgery_py.lorem_ipsum.sentence(),
|
||||
created_by='Fake')
|
||||
try:
|
||||
obj.save()
|
||||
logger.debug('Generate fake asset group: %s' % obj.name)
|
||||
except IntegrityError:
|
||||
print('Error continue')
|
||||
continue
|
||||
|
||||
|
||||
class AssetGroup(models.Model):
|
||||
name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
|
||||
|
@ -204,10 +264,11 @@ class Asset(models.Model):
|
|||
groups = models.ManyToManyField(AssetGroup, related_name='assets', verbose_name=_('Asset groups'))
|
||||
username = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Admin user'))
|
||||
password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_("Admin password"))
|
||||
admin_user = models.ForeignKey(AdminUser, null=True, on_delete=models.SET_NULL, verbose_name=_("Admin user"))
|
||||
system_user = models.ManyToManyField(SystemUser, blank=True, verbose_name=_("System User"))
|
||||
admin_user = models.ForeignKey(AdminUser, null=True, related_name='assets',
|
||||
on_delete=models.SET_NULL, verbose_name=_("Admin user"))
|
||||
system_user = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User"))
|
||||
idc = models.ForeignKey(IDC, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC'))
|
||||
mac_addr = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address"))
|
||||
mac_address = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address"))
|
||||
brand = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Brand'))
|
||||
cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU'))
|
||||
memory = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Memory'))
|
||||
|
@ -226,7 +287,7 @@ class Asset(models.Model):
|
|||
comment = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Comment'))
|
||||
|
||||
def __unicode__(self):
|
||||
return '%(ip)s:%(port)d' % {'ip': self.ip, 'port': self.port}
|
||||
return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port}
|
||||
|
||||
def initial(self):
|
||||
pass
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
{% 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>
|
||||
{% 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>{% trans 'Create admin user' %}</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 enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
|
||||
{% csrf_token %}
|
||||
{{ form.name|bootstrap_horizontal }}
|
||||
{{ form.username|bootstrap_horizontal }}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
|
||||
<div class="col-sm-8">
|
||||
{{ form.auto_generate_key}}
|
||||
</div>
|
||||
</div>
|
||||
{{ form.password|bootstrap_horizontal }}
|
||||
{{ form.private_key_file|bootstrap_horizontal }}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.as_default.id_for_label }}" class="col-sm-2 control-label">{% trans 'As default' %}</label>
|
||||
<div class="col-sm-8">
|
||||
{{ form.as_default}}
|
||||
</div>
|
||||
</div>
|
||||
{{ form.assets|bootstrap_horizontal }}
|
||||
{{ form.comment|bootstrap_horizontal }}
|
||||
|
||||
<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>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,220 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load common_tags %}
|
||||
{% load users_tags %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% 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>
|
||||
{% 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="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||
</li>
|
||||
<li><a href="" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}</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>{{ admin_user.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">
|
||||
<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 class="ibox-content">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td>{% trans 'Name' %}:</td>
|
||||
<td><b>{{ admin_user.name }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Username' %}:</td>
|
||||
<td><b>{{ admin_user.username }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Date created' %}:</td>
|
||||
<td><b>{{ admin_user.date_created }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Created by' %}:</td>
|
||||
<td><b>{{ asset_group.created_by }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Comment' %}:</td>
|
||||
<td><b>{{ admin_user.comment }}</b></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span style="float: left">{% trans 'Asset list of ' %} <b>{{ admin_user.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">
|
||||
<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 class="ibox-content">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Hostname' %}</th>
|
||||
<th>{% trans 'IP' %}</th>
|
||||
<th>{% trans 'Port' %}</th>
|
||||
<th>{% trans 'Alive' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for asset in page_obj %}
|
||||
<tr>
|
||||
<td>{{ asset.hostname }}</td>
|
||||
<td>{{ asset.ip }}</td>
|
||||
<td>{{ asset.port }}</td>
|
||||
<td>Alive</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
{% include '_pagination.html' %}
|
||||
</div>
|
||||
</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 update' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td width="50%">{% trans 'Get install script' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Get' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td width="50%">{% trans 'Retest asset connectivity' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Start' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td width="50%">{% trans 'Reset private key' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</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 'Replace asset admin user with this' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<form>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for group in groups %}
|
||||
<option value="{{ group.id }}">{{ group.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<button type="button" class="btn btn-info btn-sm">{% trans 'Replace' %}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
{# function switch_user_status(obj) {#}
|
||||
{# var status = $(obj).prop('checked');#}
|
||||
{##}
|
||||
{# $.ajax({#}
|
||||
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
|
||||
{# type: "PUT",#}
|
||||
{# data: {#}
|
||||
{# 'is_active': status#}
|
||||
{# },#}
|
||||
{# success: function (data, status) {#}
|
||||
{# console.log(data)#}
|
||||
{# },#}
|
||||
{# error: function () {#}
|
||||
{# console.log('error')#}
|
||||
{# }#}
|
||||
{# })#}
|
||||
{# }#}
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -20,7 +20,7 @@
|
|||
<tr class="gradeX">
|
||||
<td class="text-center">{{ admin_user.id }}</td>
|
||||
<td>
|
||||
<a href="{% url 'users:user-detail' pk=user.id %}">
|
||||
<a href="{% url 'assets:admin-user-detail' pk=admin_user.id %}">
|
||||
{{ admin_user.name }}
|
||||
</a>
|
||||
</td>
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span>{% trans 'Asset list of ' %} <b>{{ asset_group.name }}</b></span>
|
||||
<span style="float: left"></span>{% trans 'Asset list of ' %} <b>{{ asset_group.name }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
|
@ -118,7 +118,7 @@
|
|||
<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 'Associate asset user(system/admin)' %}
|
||||
<i class="fa fa-info-circle"></i> {% trans 'Associate system user' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
|
@ -207,23 +207,23 @@
|
|||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
function switch_user_status(obj) {
|
||||
var status = $(obj).prop('checked');
|
||||
|
||||
$.ajax({
|
||||
url: "{% url 'users:user-active-api' pk=user.id %}",
|
||||
type: "PUT",
|
||||
data: {
|
||||
'is_active': status
|
||||
},
|
||||
success: function (data, status) {
|
||||
console.log(data)
|
||||
},
|
||||
error: function () {
|
||||
console.log('error')
|
||||
}
|
||||
})
|
||||
}
|
||||
{# function switch_user_status(obj) {#}
|
||||
{# var status = $(obj).prop('checked');#}
|
||||
{##}
|
||||
{# $.ajax({#}
|
||||
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
|
||||
{# type: "PUT",#}
|
||||
{# data: {#}
|
||||
{# 'is_active': status#}
|
||||
{# },#}
|
||||
{# success: function (data, status) {#}
|
||||
{# console.log(data)#}
|
||||
{# },#}
|
||||
{# error: function () {#}
|
||||
{# console.log('error')#}
|
||||
{# }#}
|
||||
{# })#}
|
||||
{# }#}
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
})
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load common_tags %}
|
||||
{% load users_tags %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% 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>
|
||||
{% 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>
|
||||
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||
</li>
|
||||
<li class="active"><a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}</a>
|
||||
</li>
|
||||
<li><a href="{% url 'assets:system-user-asset-group' pk=system_user.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate asset groups' %}</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 style="float: left">{% trans 'Asset list of ' %} <b>{{ admin_user.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">
|
||||
<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 class="ibox-content">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Hostname' %}</th>
|
||||
<th>{% trans 'IP' %}</th>
|
||||
<th>{% trans 'Port' %}</th>
|
||||
<th>{% trans 'Alive' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for asset in page_obj %}
|
||||
<tr>
|
||||
<td>{{ asset.hostname }}</td>
|
||||
<td>{{ asset.ip }}</td>
|
||||
<td>{{ asset.port }}</td>
|
||||
<td>Alive</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
{% include '_pagination.html' %}
|
||||
</div>
|
||||
</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 'Add asset to this system user' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<form>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for asset in assets %}
|
||||
<option value="{{ asset.id }}">{{ asset.ip}}:{{ asset.port }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<button type="button" class="btn btn-primary btn-sm">{% trans 'Add' %}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
{# function switch_user_status(obj) {#}
|
||||
{# var status = $(obj).prop('checked');#}
|
||||
{##}
|
||||
{# $.ajax({#}
|
||||
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
|
||||
{# type: "PUT",#}
|
||||
{# data: {#}
|
||||
{# 'is_active': status#}
|
||||
{# },#}
|
||||
{# success: function (data, status) {#}
|
||||
{# console.log(data)#}
|
||||
{# },#}
|
||||
{# error: function () {#}
|
||||
{# console.log('error')#}
|
||||
{# }#}
|
||||
{# })#}
|
||||
{# }#}
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,140 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load common_tags %}
|
||||
{% load users_tags %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% 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>
|
||||
{% 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>
|
||||
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||
</li>
|
||||
<li><a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="{% url 'assets:system-user-asset-group' pk=system_user.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate asset groups' %}</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 style="float: left">{% trans 'Asset list of ' %} <b>{{ admin_user.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">
|
||||
<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 class="ibox-content">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Asset num' %}</th>
|
||||
<th>{% trans 'Unavailable num' %}</th>
|
||||
<th>{% trans 'Comment' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for asset_group in page_obj %}
|
||||
<tr>
|
||||
<td>{{ asset_group.name }}</td>
|
||||
<td>{{ asset_group_group.assets.count }}</td>
|
||||
<td>{{ asset_group_group.assets.count }}</td>
|
||||
<td>{{ asset_group.comment|truncatewords:4 }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row">
|
||||
{% include '_pagination.html' %}
|
||||
</div>
|
||||
</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 'Add asset group to this system user' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<form>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<select data-placeholder="{% trans 'Select asset group' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||
{% for asset_group in asset_groups %}
|
||||
<option value="{{ asset_group.id }}">{{ asset_group.name}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="no-borders-tr">
|
||||
<td colspan="2">
|
||||
<button type="button" class="btn btn-primary btn-sm">{% trans 'Add' %}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
{# function switch_user_status(obj) {#}
|
||||
{# var status = $(obj).prop('checked');#}
|
||||
{##}
|
||||
{# $.ajax({#}
|
||||
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
|
||||
{# type: "PUT",#}
|
||||
{# data: {#}
|
||||
{# 'is_active': status#}
|
||||
{# },#}
|
||||
{# success: function (data, status) {#}
|
||||
{# console.log(data)#}
|
||||
{# },#}
|
||||
{# error: function () {#}
|
||||
{# console.log('error')#}
|
||||
{# }#}
|
||||
{# })#}
|
||||
{# }#}
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,95 @@
|
|||
{% 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>
|
||||
{% 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>{% trans 'Create system user' %}</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 enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
|
||||
{% csrf_token %}
|
||||
{{ form.name|bootstrap_horizontal }}
|
||||
{{ form.username|bootstrap_horizontal }}
|
||||
{{ form.protocol|bootstrap_horizontal }}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
|
||||
<div class="col-sm-8">
|
||||
{{ form.auto_generate_key}}
|
||||
</div>
|
||||
</div>
|
||||
{{ form.password|bootstrap_horizontal }}
|
||||
{{ form.private_key_file|bootstrap_horizontal }}
|
||||
<div class="form-group">
|
||||
<label for="{{ form.as_default.id_for_label }}" class="col-sm-2 control-label">{% trans 'As default' %}</label>
|
||||
<div class="col-sm-8">
|
||||
{{ form.as_default}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="{{ form.as_push.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto push' %}</label>
|
||||
<div class="col-sm-8">
|
||||
{{ form.auto_push}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="{{ form.as_update.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto update' %}</label>
|
||||
<div class="col-sm-8">
|
||||
{{ form.auto_update}}
|
||||
</div>
|
||||
</div>
|
||||
{{ form.assets|bootstrap_horizontal }}
|
||||
{{ form.asset_groups|bootstrap_horizontal }}
|
||||
{{ form.sudo|bootstrap_horizontal }}
|
||||
{{ form.comment|bootstrap_horizontal }}
|
||||
{{ form.home|bootstrap_horizontal }}
|
||||
{{ form.shell|bootstrap_horizontal }}
|
||||
{{ form.uid|bootstrap_horizontal }}
|
||||
|
||||
<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>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
|
||||
if ($('#'+'{{ form.protocol.id_for_label }}').val() == 'telnet') {
|
||||
$('#'+'{{ form.auto_generate_key.id_for_label }}').closest('.form-group').remove();
|
||||
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').remove();
|
||||
$('#'+'{{ form.auto_push.id_for_label }}').closest('.form-group').remove();
|
||||
$('#'+'{{ form.auto_update.id_for_label }}').closest('.form-group').remove();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,192 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load common_tags %}
|
||||
{% load users_tags %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% 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>
|
||||
{% 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 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'assets:system-user-asset-group' pk=system_user.id %}" class="text-center">
|
||||
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate asset groups' %}</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>{{ system_user.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">
|
||||
<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 class="ibox-content">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td>{% trans 'Name' %}:</td>
|
||||
<td><b>{{ system_user.name }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Username' %}:</td>
|
||||
<td><b>{{ system_user.username }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Protocol' %}:</td>
|
||||
<td><b>{{ system_user.protocol }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Auto push' %}:</td>
|
||||
<td><b>{{ system_user.protocol }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Auto update' %}:</td>
|
||||
<td><b>{{ system_user.auto_update }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'As default' %}:</td>
|
||||
<td><b>{{ system_user.protocol }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Sudo' %}:</td>
|
||||
<td><b>{{ system_user.sudo }}</b></td>
|
||||
</tr>
|
||||
{% if system_user.shell %}
|
||||
<tr>
|
||||
<td>{% trans 'Shell' %}:</td>
|
||||
<td><b>{{ system_user.shell }}</b></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if system_user.home %}
|
||||
<tr>
|
||||
<td>{% trans 'Home' %}:</td>
|
||||
<td><b>{{ system_user.home }}</b></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if system_user.uid %}
|
||||
<tr>
|
||||
<td>{% trans 'Uid' %}:</td>
|
||||
<td><b>{{ system_user.uid }}</b></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>{% trans 'Date created' %}:</td>
|
||||
<td><b>{{ system_user.date_created }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Created by' %}:</td>
|
||||
<td><b>{{ asset_group.created_by }}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{% trans 'Comment' %}:</td>
|
||||
<td><b>{{ system_user.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 update' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr class="no-borders-tr">
|
||||
<td width="50%">{% trans 'Get mannual install script' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Get' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td width="50%">{% trans 'Retest asset connectivity' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Start' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td width="50%">{% trans 'Reset private key' %}:</td>
|
||||
<td>
|
||||
<span style="float: right">
|
||||
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
{# function switch_user_status(obj) {#}
|
||||
{# var status = $(obj).prop('checked');#}
|
||||
{##}
|
||||
{# $.ajax({#}
|
||||
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
|
||||
{# type: "PUT",#}
|
||||
{# data: {#}
|
||||
{# 'is_active': status#}
|
||||
{# },#}
|
||||
{# success: function (data, status) {#}
|
||||
{# console.log(data)#}
|
||||
{# },#}
|
||||
{# error: function () {#}
|
||||
{# console.log('error')#}
|
||||
{# }#}
|
||||
{# })#}
|
||||
{# }#}
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,43 @@
|
|||
{% extends '_list_base.html' %}
|
||||
{% load i18n %}
|
||||
{% load common_tags %}
|
||||
{% block content_left_head %}
|
||||
<a href="{% url 'assets:system-user-create' %}" class="btn btn-sm btn-primary "> {% trans "Create system user" %} </a>
|
||||
{% endblock %}
|
||||
|
||||
{% block table_head %}
|
||||
<th class="text-center">{% trans 'ID' %}</th>
|
||||
<th class="text-center"><a href="{% url 'assets:system-user-list' %}?sort=name">{% trans 'Name' %}</a></th>
|
||||
<th class="text-center"><a href="{% url 'assets:system-user-list' %}?sort=username">{% trans 'Username' %}</a></th>
|
||||
<th class="text-center">{% trans 'Asset num' %}</th>
|
||||
<th class="text-center">{% trans 'Asset group num' %}</th>
|
||||
<th class="text-center">{% trans 'Unavailable' %}</th>
|
||||
<th class="text-center">{% trans 'Comment' %}</th>
|
||||
<th class="text-center"></th>
|
||||
{% endblock %}
|
||||
|
||||
{% block table_body %}
|
||||
{% for system_user in system_user_list %}
|
||||
<tr class="gradeX">
|
||||
<td class="text-center">{{ system_user.id }}</td>
|
||||
<td>
|
||||
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}">
|
||||
{{ system_user.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">{{ system_user.username }}</td>
|
||||
<td class="text-center">{{ system_user.assets.count }}</td>
|
||||
<td class="text-center">{{ system_user.asset_groups.count }}</td>
|
||||
<td class="text-center">{{ system_user.assets.count }}</td>
|
||||
<td class="text-center">{{ system_user.comment|truncatewords:4 }}</td>
|
||||
<td class="text-center">
|
||||
<!-- Todo: Click script button will paste a url to clipboard like: curl http://url/system_user_create.sh | bash -->
|
||||
<a href="{% url 'assets:system-user-update' pk=system_user.id %}" class="btn btn-xs btn-primary">{% trans 'Script' %}</a>
|
||||
<!-- Todo: Click refresh button will run a task to test admin user could connect asset or not immediately -->
|
||||
<a href="{% url 'assets:system-user-update' pk=system_user.id %}" class="btn btn-xs btn-warning">{% trans 'Refresh' %}</a>
|
||||
<a href="{% url 'assets:system-user-update' pk=system_user.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
|
||||
<a href="{% url 'assets:system-user-delete' pk=system_user.id %}" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
|
@ -12,26 +12,43 @@ import views
|
|||
app_name = 'assets'
|
||||
|
||||
urlpatterns = [
|
||||
# Resource asset url
|
||||
url(r'^$', views.AssetListView.as_view(), name='asset-index'),
|
||||
url(r'^asset$', views.AssetListView.as_view(), name='asset-list'),
|
||||
url(r'^asset/create$', views.AssetCreateView.as_view(), name='asset-create'),
|
||||
url(r'^asset/(?P<pk>[0-9]+)$', views.AssetDetailView.as_view(), name='asset-detail'),
|
||||
url(r'^asset/(?P<pk>[0-9]+)/update', views.AssetUpdateView.as_view(), name='asset-update'),
|
||||
url(r'^asset/(?P<pk>[0-9]+)/delete$', views.AssetDeleteView.as_view(), name='asset-delete'),
|
||||
|
||||
# Resource asset group url
|
||||
url(r'^asset-group$', views.AssetGroupListView.as_view(), name='asset-group-list'),
|
||||
url(r'^asset-group/create$', views.AssetGroupCreateView.as_view(), name='asset-group-create'),
|
||||
url(r'^asset-group/(?P<pk>[0-9]+)$', views.AssetGroupDetailView.as_view(), name='asset-group-detail'),
|
||||
url(r'^asset-group/(?P<pk>[0-9]+)/update$', views.AssetGroupUpdateView.as_view(), name='asset-group-update'),
|
||||
url(r'^asset-group/(?P<pk>[0-9]+)/delete$', views.AssetGroupDeleteView.as_view(), name='asset-group-delete'),
|
||||
|
||||
# Resource idc url
|
||||
url(r'^idc$', views.IDCListView.as_view(), name='idc-list'),
|
||||
url(r'^idc/create$', views.IDCCreateView.as_view(), name='idc-create'),
|
||||
url(r'^idc/(?P<pk>[0-9]+)$', views.IDCDetailView.as_view(), name='idc-detail'),
|
||||
url(r'^idc/(?P<pk>[0-9]+)/update', views.IDCUpdateView.as_view(), name='idc-update'),
|
||||
url(r'^idc/(?P<pk>[0-9]+)/delete$', views.IDCDeleteView.as_view(), name='idc-delete'),
|
||||
|
||||
# Resource admin user url
|
||||
url(r'^admin-user$', views.AdminUserListView.as_view(), name='admin-user-list'),
|
||||
url(r'^admin-user/create$', views.AdminUserCreateView.as_view(), name='admin-user-create'),
|
||||
url(r'^admin-user/(?P<pk>[0-9]+)$', views.AdminUserDetailView.as_view(), name='admin-user-detail'),
|
||||
url(r'^admin-user/(?P<pk>[0-9]+)/update', views.AdminUserUpdateView.as_view(), name='admin-user-update'),
|
||||
url(r'^admin-user/(?P<pk>[0-9]+)/delete$', views.AdminUserDeleteView.as_view(), name='admin-user-delete'),
|
||||
|
||||
# Resource system user url
|
||||
url(r'^system-user$', views.SystemUserListView.as_view(), name='system-user-list'),
|
||||
url(r'^system-user/create$', views.SystemUserCreateView.as_view(), name='system-user-create'),
|
||||
url(r'^system-user/(?P<pk>[0-9]+)$', views.SystemUserDetailView.as_view(), name='system-user-detail'),
|
||||
url(r'^system-user/(?P<pk>[0-9]+)/update', views.SystemUserUpdateView.as_view(), name='system-user-update'),
|
||||
url(r'^system-user/(?P<pk>[0-9]+)/delete$', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
|
||||
url(r'^system-user/(?P<pk>[0-9]+)/asset$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
|
||||
url(r'^system-user/(?P<pk>[0-9]+)/asset-group$', views.SystemUserAssetGroupView.as_view(),
|
||||
name='system-user-asset-group'),
|
||||
# url(r'^api/v1.0/', include(router.urls)),
|
||||
]
|
||||
|
|
|
@ -2,19 +2,16 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic import TemplateView, ListView
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from django.views.generic import TemplateView, ListView
|
||||
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||
from django.urls import reverse_lazy
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||
|
||||
from .models import Asset, AssetGroup, IDC, AssetExtend, AdminUser, SystemUser
|
||||
from .forms import AssetForm, AssetGroupForm, IDCForm
|
||||
from .forms import AssetForm, AssetGroupForm, IDCForm, AdminUserForm, SystemUserForm
|
||||
from .hands import AdminUserRequiredMixin
|
||||
|
||||
|
||||
|
@ -216,7 +213,7 @@ class AdminUserListView(AdminUserRequiredMixin, ListView):
|
|||
return super(AdminUserListView, self).get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
# Todo: Default group by lose asset connection num
|
||||
# Todo: Default order by lose asset connection num
|
||||
self.queryset = super(AdminUserListView, self).get_queryset()
|
||||
self.keyword = keyword = self.request.GET.get('keyword', '')
|
||||
self.sort = sort = self.request.GET.get('sort', '-date_created')
|
||||
|
@ -230,17 +227,208 @@ class AdminUserListView(AdminUserRequiredMixin, ListView):
|
|||
return self.queryset
|
||||
|
||||
|
||||
class AdminUserCreateView(AdminUserRequiredMixin, CreateView):
|
||||
pass
|
||||
class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = AdminUser
|
||||
form_class = AdminUserForm
|
||||
template_name = 'assets/admin_user_create_update.html'
|
||||
success_url = reverse_lazy('assets:admin-user-list')
|
||||
success_message = _('Create admin user <a href="%s">%s</a> successfully.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'assets',
|
||||
'action': 'Create admin user'
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(AdminUserCreateView, self).get_context_data(**kwargs)
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
return self.success_message % (
|
||||
reverse_lazy('assets:admin-user-detail', kwargs={'pk': self.object.pk}),
|
||||
self.object.name,
|
||||
)
|
||||
|
||||
|
||||
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||
pass
|
||||
model = AdminUser
|
||||
form_class = AdminUserForm
|
||||
template_name = 'assets/admin_user_create_update.html'
|
||||
success_message = _('Update admin user <a href="%s">%s</a> successfully.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'assets',
|
||||
'action': 'Update admin user'
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(AdminUserUpdateView, self).get_context_data(**kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
success_url = reverse_lazy('assets:admin-user-detail', pk=self.object.pk)
|
||||
return success_url
|
||||
|
||||
|
||||
class AdminUserDetailView(AdminUserRequiredMixin, DetailView):
|
||||
pass
|
||||
class AdminUserDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
template_name = 'assets/admin_user_detail.html'
|
||||
context_object_name = 'admin_user'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=AdminUser.objects.all())
|
||||
return super(AdminUserDetailView, self).get(request, *args, **kwargs)
|
||||
|
||||
# Todo: queryset default order by connectivity, need ops support
|
||||
def get_queryset(self):
|
||||
return self.object.assets.all()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'assets',
|
||||
'action': 'Admin user detail'
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(AdminUserDetailView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class AdminUserDeleteView(AdminUserRequiredMixin, DeleteView):
|
||||
pass
|
||||
model = AdminUser
|
||||
template_name = 'assets/delete_confirm.html'
|
||||
success_url = 'assets:admin-user-list'
|
||||
|
||||
|
||||
class SystemUserListView(AdminUserRequiredMixin, ListView):
|
||||
model = SystemUser
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
context_object_name = 'system_user_list'
|
||||
template_name = 'assets/system_user_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': _('Assets'),
|
||||
'action': _('System user list'),
|
||||
'keyword': self.request.GET.get('keyword', '')
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(SystemUserListView, self).get_context_data(**kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
# Todo: Default order by lose asset connection num
|
||||
self.queryset = super(SystemUserListView, self).get_queryset()
|
||||
self.keyword = keyword = self.request.GET.get('keyword', '')
|
||||
self.sort = sort = self.request.GET.get('sort', '-date_created')
|
||||
|
||||
if keyword:
|
||||
self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
|
||||
Q(comment__icontains=keyword))
|
||||
|
||||
if sort:
|
||||
self.queryset = self.queryset.order_by(sort)
|
||||
return self.queryset
|
||||
|
||||
|
||||
class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = SystemUser
|
||||
form_class = SystemUserForm
|
||||
template_name = 'assets/system_user_create_update.html'
|
||||
success_url = reverse_lazy('assets:system-user-list')
|
||||
success_message = _('Create system user <a href="%s">%s</a> successfully.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': _('Assets'),
|
||||
'action': _('Create system user'),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(SystemUserCreateView, self).get_context_data(**kwargs)
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
return self.success_message % (
|
||||
reverse_lazy('assets:system-user-detail', kwargs={'pk': self.object.pk}),
|
||||
self.object.name,
|
||||
)
|
||||
|
||||
|
||||
class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||
model = SystemUser
|
||||
form_class = SystemUserForm
|
||||
template_name = 'assets/system_user_create_update.html'
|
||||
success_message = _('Update system user <a href="%s">%s</a> successfully.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': _('Assets'),
|
||||
'action': _('Update system user')
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(SystemUserUpdateView, self).get_context_data(**kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
success_url = reverse_lazy('assets:system-user-detail', pk=self.object.pk)
|
||||
return success_url
|
||||
|
||||
|
||||
class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
|
||||
template_name = 'assets/system_user_detail.html'
|
||||
context_object_name = 'system_user'
|
||||
model = SystemUser
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': _('Assets'),
|
||||
'action': _('System user detail')
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(SystemUserDetailView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class SystemUserDeleteView(AdminUserRequiredMixin, DeleteView):
|
||||
model = SystemUser
|
||||
template_name = 'assets/delete_confirm.html'
|
||||
success_url = 'assets:system-user-list'
|
||||
|
||||
|
||||
class SystemUserAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
template_name = 'assets/system_user_asset.html'
|
||||
context_object_name = 'system_user'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=SystemUser.objects.all())
|
||||
return super(SystemUserAssetView, self).get(request, *args, **kwargs)
|
||||
|
||||
# Todo: queryset default order by connectivity, need ops support
|
||||
def get_queryset(self):
|
||||
return self.object.assets.all()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'assets',
|
||||
'action': 'System user asset',
|
||||
'assets': self.get_queryset(),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(SystemUserAssetView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class SystemUserAssetGroupView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
template_name = 'assets/system_user_asset_group.html'
|
||||
context_object_name = 'system_user'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=SystemUser.objects.all())
|
||||
return super(SystemUserAssetGroupView, self).get(request, *args, **kwargs)
|
||||
|
||||
# Todo: queryset default order by connectivity, need ops support
|
||||
def get_queryset(self):
|
||||
return self.object.asset_groups.all()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'assets',
|
||||
'action': 'System user asset group',
|
||||
'asset_groups': self.get_queryset(),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(SystemUserAssetGroupView, self).get_context_data(**kwargs)
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -108,6 +108,7 @@ TEMPLATES = [
|
|||
# WSGI_APPLICATION = 'jumpserver.wsgi.application'
|
||||
|
||||
LOGIN_REDIRECT_URL = reverse_lazy('index')
|
||||
LOGIN_URL = reverse_lazy('users:login')
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
|
||||
|
@ -227,7 +228,7 @@ USE_L10N = True
|
|||
USE_TZ = True
|
||||
|
||||
# I18N translation
|
||||
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale'),]
|
||||
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale'), ]
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.10/howto/static-files/
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<li id="asset-group"><a href="{% url 'assets:asset-group-list' %}">{% trans 'Asset group' %}</a></li>
|
||||
<li id="idc"><a href="{% url 'assets:idc-list' %}">{% trans 'IDC' %}</a></li>
|
||||
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
|
||||
<li id="system-user"><a href="">{% trans 'System user' %}</a></li>
|
||||
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
|
||||
<li id=""><a href="">{% trans 'Label' %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
|
|
@ -18,6 +18,7 @@ class UserLoginForm(AuthenticationForm):
|
|||
|
||||
|
||||
class UserCreateForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = [
|
||||
|
@ -67,3 +68,23 @@ class UserGroupForm(forms.ModelForm):
|
|||
help_texts = {
|
||||
'name': '* required'
|
||||
}
|
||||
|
||||
|
||||
class UserInfoForm(forms.Form):
|
||||
name = forms.CharField(max_length=20, label=_('name'))
|
||||
avatar = forms.ImageField(label=_('avatar'), required=False)
|
||||
wechat = forms.CharField(max_length=30, label=_('wechat'), required=False)
|
||||
phone = forms.CharField(max_length=20, label=_('phone'), required=False)
|
||||
enable_otp = forms.BooleanField(required=False, label=_('enable otp'))
|
||||
|
||||
|
||||
class UserKeyForm(forms.Form):
|
||||
private_key = forms.CharField(max_length=5000, widget=forms.Textarea, label=_('private key'))
|
||||
|
||||
def clean_private_key(self):
|
||||
from users.utils import validate_ssh_pk
|
||||
ssh_pk = self.cleaned_data['private_key']
|
||||
checked, reason = validate_ssh_pk(ssh_pk)
|
||||
if not checked:
|
||||
raise forms.ValidationError(_('Not a valid ssh private key.'))
|
||||
return ssh_pk
|
||||
|
|
|
@ -2,19 +2,17 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.utils import timezone
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser, Permission
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.core import signing
|
||||
from django.db import models, IntegrityError
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from django.db import IntegrityError
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from rest_framework.authtoken.models import Token
|
||||
from django.core import signing
|
||||
|
||||
from common.utils import encrypt, decrypt
|
||||
|
||||
|
@ -44,16 +42,15 @@ class UserGroup(models.Model):
|
|||
|
||||
@classmethod
|
||||
def generate_fake(cls, count=100):
|
||||
from random import seed, randint, choice
|
||||
from random import seed, choice
|
||||
import forgery_py
|
||||
from django.db import IntegrityError
|
||||
|
||||
seed()
|
||||
for i in range(count):
|
||||
group = cls(name=forgery_py.name.full_name(),
|
||||
comment=forgery_py.lorem_ipsum.sentence(),
|
||||
created_by=choice(User.objects.all()).username
|
||||
)
|
||||
)
|
||||
try:
|
||||
group.save()
|
||||
except IntegrityError:
|
||||
|
@ -84,7 +81,7 @@ class User(AbstractUser):
|
|||
_private_key = models.CharField(max_length=5000, blank=True, verbose_name=_('ssh private key'))
|
||||
_public_key = models.CharField(max_length=1000, blank=True, verbose_name=_('ssh public key'))
|
||||
comment = models.TextField(max_length=200, blank=True, verbose_name=_('Comment'))
|
||||
is_first_login = models.BooleanField(default=False)
|
||||
is_first_login = models.BooleanField(default=True)
|
||||
date_expired = models.DateTimeField(default=date_expired_default, blank=True, null=True,
|
||||
verbose_name=_('Date expired'))
|
||||
created_by = models.CharField(max_length=30, default='', verbose_name=_('Created by'))
|
||||
|
@ -235,7 +232,7 @@ class User(AbstractUser):
|
|||
wechat=forgery_py.internet.user_name(True),
|
||||
comment=forgery_py.lorem_ipsum.sentence(),
|
||||
created_by=choice(cls.objects.all()).username,
|
||||
)
|
||||
)
|
||||
try:
|
||||
user.save()
|
||||
except IntegrityError:
|
||||
|
@ -264,4 +261,3 @@ def create_auth_token(sender, instance=None, created=False, **kwargs):
|
|||
Token.objects.create(user=instance)
|
||||
except IntegrityError:
|
||||
pass
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load bootstrap %}
|
||||
|
||||
{% block custom_head_css_js %}
|
||||
{{ wizard.form.media }}
|
||||
<link href="{% static 'css/plugins/steps/jquery.steps.css' %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="ibox">
|
||||
<div class="ibox-title">
|
||||
<h5>{% trans 'First Login' %}</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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<div class="wizard">
|
||||
<div class="steps clearfix">
|
||||
<ul role="tablist">
|
||||
{% for step in wizard.steps.all %}
|
||||
<li role="tab" class="{% ifequal step wizard.steps.first %}first{% endifequal %} {% ifequal step wizard.steps.current %}current{% else %}disabled{% endifequal %} {% ifequal step wizard.steps.last %}last{% endifequal %}"
|
||||
aria-disabled="false" aria-selected="true">
|
||||
<a href="javascript:void(0)"><span class="number">{% trans 'Step' %} {{ step }}</span></a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content clearfix">
|
||||
<form action="" method="post" class="form col-lg-8 p-m" id="fl_form">
|
||||
{% csrf_token %}
|
||||
{{ wizard.management_form }}
|
||||
{% if wizard.form.forms %}
|
||||
{{ wizard.form.management_form }}
|
||||
{% for form in wizard.form.forms %}
|
||||
{{ form|bootstrap }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ wizard.form|bootstrap }}
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
<div class="actions clearfix">
|
||||
<ul>
|
||||
{% if wizard.steps.prev %}
|
||||
<li><a class="fl_goto" data-goto="{{ wizard.steps.first }}">{% trans "first step" %}</a></li>
|
||||
<li><a class="fl_goto" name="wizard_goto_step" data-goto="{{ wizard.steps.prev }}">{% trans "prev step" %}</a></li>
|
||||
{% endif %}
|
||||
<li><a id="fl_submit">{% trans "submit" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
$(document).on('click', ".fl_goto", function(){
|
||||
var $form = $('#fl_form');
|
||||
$('<input />', {'name': 'wizard_goto_step', 'value': $(this).data('goto'), 'type': 'hidden'}).appendTo($form);
|
||||
$form.submit();
|
||||
return false;
|
||||
}).on('click', '#fl_submit', function(){
|
||||
$('#fl_form').submit();
|
||||
return false;
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -40,7 +40,7 @@
|
|||
<div class="col-md-6">
|
||||
<div class="ibox-content">
|
||||
<div><img src="{% static 'img/logo.png' %}" width="82" height="82"> <span class="font-bold text-center" style="font-size: 32px; font-family: inherit">{% trans 'Login' %}</span></div>
|
||||
<form class="m-t" role="form" method="post" action="{% url 'users:login' %}">
|
||||
<form class="m-t" role="form" method="post" action="">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
{% if 'captcha' in form.errors %}
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
<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' %}
|
||||
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<table class="table">
|
||||
|
@ -191,7 +191,7 @@
|
|||
<tr>
|
||||
<td ><b class="bdg_user_group" data-gid={{ group.id }}>{{ group.name }}</b></td>
|
||||
<td>
|
||||
<button class="btn btn-danger btn-sm btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
|
||||
<button class="btn btn-danger btn-xs btn_delete_user_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -16,6 +16,7 @@ urlpatterns = [
|
|||
name='reset-password-success'),
|
||||
url(r'^user$', views.UserListView.as_view(), name='user-list'),
|
||||
url(r'^user/(?P<pk>[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'),
|
||||
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'),
|
||||
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'),
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
#
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import os
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from paramiko.rsakey import RSAKey
|
||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from paramiko.rsakey import RSAKey
|
||||
|
||||
from common.tasks import send_mail_async
|
||||
from common.utils import reverse
|
||||
from users.models import User
|
||||
|
||||
|
||||
try:
|
||||
|
@ -125,5 +125,56 @@ def send_reset_password_mail(user):
|
|||
send_mail_async.delay(subject, message, recipient_list, html_message=message)
|
||||
|
||||
|
||||
def validate_ssh_pk(text):
|
||||
"""
|
||||
Expects a SSH private key as string.
|
||||
Returns a boolean and a error message.
|
||||
If the text is parsed as private key successfully,
|
||||
(True,'') is returned. Otherwise,
|
||||
(False, <message describing the error>) is returned.
|
||||
|
||||
from https://github.com/githubnemo/SSH-private-key-validator/blob/master/validate.py
|
||||
|
||||
"""
|
||||
|
||||
if not text:
|
||||
return False, 'No text given'
|
||||
|
||||
startPattern = re.compile("^-----BEGIN [A-Z]+ PRIVATE KEY-----")
|
||||
optionPattern = re.compile("^.+: .+")
|
||||
contentPattern = re.compile("^([a-zA-Z0-9+/]{64}|[a-zA-Z0-9+/]{1,64}[=]{0,2})$")
|
||||
endPattern = re.compile("^-----END [A-Z]+ PRIVATE KEY-----")
|
||||
|
||||
def contentState(text):
|
||||
for i in range(0, len(text)):
|
||||
line = text[i]
|
||||
|
||||
if endPattern.match(line):
|
||||
if i == len(text) - 1 or len(text[i + 1]) == 0:
|
||||
return True, ''
|
||||
else:
|
||||
return False, 'At end but content coming'
|
||||
|
||||
elif not contentPattern.match(line):
|
||||
return False, 'Wrong string in content section'
|
||||
|
||||
return False, 'No content or missing end line'
|
||||
|
||||
def optionState(text):
|
||||
for i in range(0, len(text)):
|
||||
line = text[i]
|
||||
|
||||
if line[-1:] == '\\':
|
||||
return optionState(text[i + 2:])
|
||||
|
||||
if not optionPattern.match(line):
|
||||
return contentState(text[i + 1:])
|
||||
|
||||
return False, 'Expected option, found nothing'
|
||||
|
||||
def startState(text):
|
||||
if len(text) == 0 or not startPattern.match(text[0]):
|
||||
return False, 'Header is wrong'
|
||||
return optionState(text[1:])
|
||||
|
||||
return startState([n.strip() for n in text.splitlines()])
|
||||
|
|
|
@ -6,7 +6,9 @@ import logging
|
|||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import login as auth_login, logout as auth_logout
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, reverse, redirect
|
||||
|
@ -21,10 +23,12 @@ from django.views.generic.list import ListView
|
|||
from django.views.generic.edit import CreateView, DeleteView, UpdateView, FormView
|
||||
from django.views.generic.detail import DetailView
|
||||
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
|
||||
from common.utils import get_object_or_none
|
||||
|
||||
from .models import User, UserGroup
|
||||
from .forms import UserCreateForm, UserUpdateForm, UserGroupForm, UserLoginForm
|
||||
from .forms import (UserCreateForm, UserUpdateForm, UserGroupForm, UserLoginForm, UserInfoForm, UserKeyForm)
|
||||
from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_password_mail
|
||||
|
||||
|
||||
|
@ -41,12 +45,20 @@ class UserLoginView(FormView):
|
|||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if request.user.is_staff:
|
||||
return redirect(request.POST.get(self.redirect_field_name, reverse('index')))
|
||||
return self.render_to_response(self.get_context_data(**kwargs))
|
||||
return redirect(self.get_success_url())
|
||||
return super(UserLoginView, self).get(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
auth_login(self.request, form.get_user())
|
||||
return redirect(self.request.POST.get(self.redirect_field_name, reverse('index')))
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self):
|
||||
if self.request.user.is_first_login:
|
||||
return reverse('users:user-first-login')
|
||||
|
||||
return self.request.POST.get(
|
||||
self.redirect_field_name,
|
||||
self.request.GET.get(self.redirect_field_name, reverse('index')))
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
|
@ -97,7 +109,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
|||
form_class = UserCreateForm
|
||||
template_name = 'users/user_create.html'
|
||||
success_url = reverse_lazy('users:user-list')
|
||||
success_message = _('Create user <a href="%s">%s</a> success.')
|
||||
success_message = _('Create user <a href="%s">%s</a> successfully.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserCreateView, self).get_context_data(**kwargs)
|
||||
|
@ -286,3 +298,42 @@ class UserResetPasswordView(TemplateView):
|
|||
|
||||
user.reset_password(password)
|
||||
return HttpResponseRedirect(reverse('users:reset-password-success'))
|
||||
|
||||
|
||||
class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
|
||||
template_name = 'users/first_login.html'
|
||||
form_list = [UserInfoForm, UserKeyForm]
|
||||
file_storage = default_storage
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.user.is_authenticated() and not request.user.is_first_login:
|
||||
return redirect(reverse('index'))
|
||||
return super(UserFirstLoginView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def done(self, form_list, form_dict, **kwargs):
|
||||
user = self.request.user
|
||||
for form in form_list:
|
||||
for field in form:
|
||||
if field.value():
|
||||
setattr(user, field.name, field.value())
|
||||
if field.name == 'enable_otp':
|
||||
user.enable_otp = field.value()
|
||||
user.is_first_login = False
|
||||
user.save()
|
||||
return redirect(reverse('index'))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserFirstLoginView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('First Login')})
|
||||
return context
|
||||
|
||||
def get_form_initial(self, step):
|
||||
user = self.request.user
|
||||
if step == '0':
|
||||
return {
|
||||
'name': user.name or user.username,
|
||||
'enable_otp': user.enable_otp or True,
|
||||
'wechat': user.wechat or '',
|
||||
'phone': user.phone or ''
|
||||
}
|
||||
return super(UserFirstLoginView, self).get_form_initial(step)
|
||||
|
|
|
@ -17,3 +17,4 @@ paramiko==2.0.2
|
|||
celery==3.1.23
|
||||
ansible==2.1.1.0
|
||||
django-simple-captcha==0.5.2
|
||||
django-formtools==1.0
|
||||
|
|
Loading…
Reference in New Issue