Commit 6e69c018 authored by ibuler's avatar ibuler

Merge branch 'audits'

parents 8731e081 aff37092
# ~*~ coding: utf-8 ~*~
from rest_framework import serializers
from rest_framework import viewsets, serializers, generics
from rest_framework import viewsets, generics, mixins
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateDestroyAPIView
from django.shortcuts import get_object_or_404
from common.mixins import BulkDeleteApiMixin
from common.utils import get_object_or_none, signer
from .hands import IsSuperUserOrTerminalUser, IsSuperUser
from .models import AssetGroup, Asset, IDC, SystemUser
from .serializers import AssetBulkUpdateSerializer
from .models import AssetGroup, Asset, IDC, SystemUser, AdminUser
from . import serializers
class AssetGroupSerializer(serializers.ModelSerializer):
class Meta:
model = AssetGroup
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
# fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
class AssetViewSet(viewsets.ModelViewSet):
"""API endpoint that allows Asset to be viewed or edited."""
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
class IDCSerializer(serializers.ModelSerializer):
class Meta:
model = IDC
# fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
def get_queryset(self):
queryset = super(AssetViewSet, self).get_queryset()
idc = self.request.query_params.get('idc', '')
if idc:
queryset = queryset.filter(idc__id=idc)
return queryset
class AssetGroupViewSet(viewsets.ModelViewSet):
......@@ -35,26 +31,44 @@ class AssetGroupViewSet(viewsets.ModelViewSet):
some other comment
"""
queryset = AssetGroup.objects.all()
serializer_class = AssetGroupSerializer
class AssetViewSet(viewsets.ModelViewSet):
"""API endpoint that allows Asset to be viewed or edited."""
queryset = Asset.objects.all()
serializer_class = AssetSerializer
serializer_class = serializers.AssetGroupSerializer
class IDCViewSet(viewsets.ReadOnlyModelViewSet):
class IDCViewSet(viewsets.ModelViewSet):
"""API endpoint that allows IDC to be viewed or edited."""
queryset = IDC.objects.all()
serializer_class = IDCSerializer
serializer_class = serializers.IDCSerializer
permission_classes = (IsSuperUser,)
class AdminUserViewSet(viewsets.ModelViewSet):
queryset = AdminUser.objects.all()
serializer_class = serializers.AdminUserSerializer
permission_classes = (IsSuperUser,)
class SystemUserViewSet(viewsets.ModelViewSet):
queryset = SystemUser.objects.all()
serializer_class = serializers.SystemUserSerializer
permission_classes = (IsSuperUser,)
# class IDCAssetsApi(generics.ListAPIView):
# model = IDC
# serializer_class = serializers.AssetSerializer
#
# def get(self, request, *args, **kwargs):
# filter_kwargs = {self.lookup_field: self.kwargs[self.lookup_field]}
# self.object = get_object_or_404(self.model, **filter_kwargs)
# return super(IDCAssetsApi, self).get(request, *args, **kwargs)
#
# def get_queryset(self):
# return self.object.assets.all()
class AssetListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
queryset = Asset.objects.all()
serializer_class = AssetBulkUpdateSerializer
serializer_class = serializers.AssetSerializer
permission_classes = (IsSuperUser,)
......
# coding:utf-8
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser, Tag
from django.utils.translation import gettext_lazy as _
from common.utils import validate_ssh_private_key, ssh_pubkey_gen
# class AssetForm(forms.ModelForm):
......@@ -23,12 +24,10 @@ from django.utils.translation import gettext_lazy as _
#
class AssetCreateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None)
if instance:
initial = kwargs.get('initial', {})
#tags = instance.tags.all()
initial['tags'] = [t.pk for t in kwargs['instance'].tags.all()]
super(AssetCreateForm, self).__init__(*args, **kwargs)
......@@ -141,7 +140,6 @@ class AdminUserForm(forms.ModelForm):
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)
......@@ -166,21 +164,36 @@ class AdminUserForm(forms.ModelForm):
# 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']
private_key = self.cleaned_data['private_key_file']
public_key = ssh_pubkey_gen(private_key)
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()
if private_key:
admin_user.private_key = private_key
admin_user.public_key = public_key
admin_user.save()
return self.instance
return admin_user
def clean_private_key_file(self):
private_key_file = self.cleaned_data['private_key_file']
if private_key_file:
private_key = private_key_file.read()
if not validate_ssh_private_key(private_key):
raise forms.ValidationError(_('Invalid private key'))
return private_key
return private_key_file
def clean(self):
password = self.cleaned_data['password']
private_key_file = self.cleaned_data.get('private_key_file', '')
if not (password or private_key_file):
raise forms.ValidationError(_('Password and private key file must be input one'))
class Meta:
model = AdminUser
fields = ['name', 'username', 'auto_generate_key', 'password', 'private_key_file', 'as_default', 'comment']
fields = ['name', 'username', 'password', 'private_key_file', 'comment']
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
......
......@@ -6,8 +6,9 @@ from django.db import models
from django.core import serializers
import logging
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from common.utils import signer
from common.utils import signer, validate_ssh_private_key
logger = logging.getLogger(__name__)
......@@ -91,13 +92,21 @@ class AssetExtend(models.Model):
unique_together = ('key', 'value')
def private_key_validator(value):
if not validate_ssh_private_key(value):
raise ValidationError(
_('%(value)s is not an even number'),
params={'value': value},
)
class AdminUser(models.Model):
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'))
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'),
validators=[private_key_validator,])
_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_add=True, null=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
......@@ -107,7 +116,7 @@ class AdminUser(models.Model):
@property
def password(self):
return decrypt(self._password)
return signer.unsign(self._password)
@password.setter
def password(self, password_raw):
......@@ -129,6 +138,10 @@ class AdminUser(models.Model):
def public_key(self, public_key_raw):
self._public_key = signer.sign(public_key_raw)
@property
def assets_amount(self):
return self.assets.count()
class Meta:
db_table = 'admin_user'
......@@ -216,6 +229,10 @@ class SystemUser(models.Model):
assets = set(self.assets.all()) | self.get_assets_inherit_from_asset_groups()
return list(assets)
@property
def assets_amount(self):
return self.assets.count()
class Meta:
db_table = 'system_user'
......@@ -298,9 +315,8 @@ class Asset(models.Model):
admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets',
on_delete=models.SET_NULL, verbose_name=_("Admin user"))
system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User"))
idc = models.ForeignKey(IDC, null=True, related_name='assets',
idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets',
on_delete=models.SET_NULL, verbose_name=_('IDC'),)
# default=get_default_idc)
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'))
......@@ -329,6 +345,7 @@ class Asset(models.Model):
def __unicode__(self):
return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port}
@property
def is_valid(self):
warning = ''
if not self.is_active:
......
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
from rest_framework import viewsets, serializers,generics
from .models import AssetGroup, Asset, IDC, AssetExtend
from .models import AssetGroup, Asset, IDC, AssetExtend, AdminUser, SystemUser
from common.mixins import BulkDeleteApiMixin
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
class AssetBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer):
# group_display = serializers.SerializerMethodField()
# active_display = serializers.SerializerMethodField()
#groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all())
class AssetGroupSerializer(serializers.ModelSerializer):
assets_amount = serializers.SerializerMethodField()
# assets = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = AssetGroup
@staticmethod
def get_assets_amount(obj):
return obj.assets.count()
class AdminUserSerializer(serializers.ModelSerializer):
class Meta:
model = AdminUser
def get_field_names(self, declared_fields, info):
fields = super(AdminUserSerializer, self).get_field_names(declared_fields, info)
fields.append('assets_amount')
return fields
class SystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
exclude = ('_password', '_private_key', '_public_key')
def get_field_names(self, declared_fields, info):
fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info)
fields.append('assets_amount')
return fields
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
system_users = SystemUserSerializer(many=True, read_only=True)
admin_user = AdminUserSerializer(many=False, read_only=True)
class Meta(object):
model = Asset
list_serializer_class = BulkListSerializer
fields = ['id', 'port', 'idc']
# def get_group_display(self, obj):
# return " ".join([group.name for group in obj.groups.all()])
#
# def get_active_display(self, obj):
# # TODO: user ative state
# return not (obj.is_expired and obj.is_active)
\ No newline at end of file
class AssetGrantedSerializer(serializers.ModelSerializer):
system_users = SystemUserSerializer(many=True, read_only=True)
is_inherited = serializers.SerializerMethodField()
system_users_join = serializers.SerializerMethodField()
class Meta(object):
model = Asset
fields = ("id", "hostname", "ip", "port", "system_users", "is_inherited",
"is_active", "system_users_join", "comment")
@staticmethod
def get_is_inherited(obj):
if getattr(obj, 'inherited', ''):
return True
else:
return False
@staticmethod
def get_system_users_join(obj):
return ', '.join([system_user.username for system_user in obj.system_users.all()])
class IDCSerializer(serializers.ModelSerializer):
assets_amount = serializers.SerializerMethodField()
class Meta:
model = IDC
@staticmethod
def get_assets_amount(obj):
return obj.assets.count()
def get_field_names(self, declared_fields, info):
fields = super(IDCSerializer, self).get_field_names(declared_fields, info)
fields.append('assets_amount')
return fields
......@@ -31,20 +31,8 @@
{% 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 }}
......
{% extends '_base_list.html' %}
{% load i18n %}
{% load common_tags %}
{% block content_left_head %}
<a href="{% url 'assets:admin-user-create' %}" class="btn btn-sm btn-primary "> {% trans "Create admin user" %} </a>
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_head %}
<th class="text-center">{% trans 'ID' %}</th>
<th class="text-center"><a href="{% url 'assets:admin-user-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center"><a href="{% url 'assets:admin-user-list' %}?sort=username">{% trans 'Username' %}</a></th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Lost connection' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center"></th>
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5">
<a href="{% url "assets:admin-user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create admin user" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="admin_user_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Lost connection' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block table_body %}
{% for admin_user in admin_user_list %}
<tr class="gradeX">
<td class="text-center">{{ admin_user.id }}</td>
<td>
<a href="{% url 'assets:admin-user-detail' pk=admin_user.id %}">
{{ admin_user.name }}
</a>
</td>
<td class="text-center">{{ admin_user.username }}</td>
<td class="text-center">{{ admin_user.assets.count }}</td>
<td class="text-center">{{ admin_user.assets.count }}</td>
<td class="text-center">{{ admin_user.comment|truncatewords:8 }}</td>
<td class="text-center">
<!-- Todo: Click script button will paste a url to clipboard like: curl http://url/admin_user_create.sh | bash -->
<a href="{% url 'assets:admin-user-update' pk=admin_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:admin-user-update' pk=admin_user.id %}" class="btn btn-xs btn-warning">{% trans 'Refresh' %}</a>
<a href="{% url 'assets:admin-user-update' pk=admin_user.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a onclick="obj_del(this,'{{ admin_user.name }}','{% url 'assets:admin-user-delete' admin_user.id %}')" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function(){
var options = {
ele: $('#admin_user_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 8 ? cellData.substring(0, 24) + '...': cellData;
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
}},
{# {targets: 6, createdCell: function (td, cellData) {#}
{# if (!cellData) {#}
{# $(td).html('<i class="fa fa-times text-danger"></i>')#}
{# } else {#}
{# $(td).html('<i class="fa fa-check text-navy"></i>')#}
{# }#}
{# }},#}
{targets: 6, createdCell: function (td, cellData, rowData) {
var script_btn = '<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs btn-primary">{% trans "Script" %}</a>'.replace('99991937', cellData);
var update_btn = '<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
$(td).html(script_btn + update_btn + del_btn)
}}],
ajax_url: '{% url "api-assets:admin-user-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" }, {data: function () {return 'lost'} },
{data: "comment" }, {data: "id" }],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
});
</script>
{% endblock %}
......@@ -30,12 +30,11 @@
<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 class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</form>
{% endblock %}
{% block custom_foot_js %}
......@@ -44,10 +43,9 @@
$('.select2').select2();
$("#id_tags").select2({
tags: true,
maximumSelectionLength: 8, //最多能够选择的个数
maximumSelectionLength: 8 //最多能够选择的个数
//closeOnSelect: false
});
})
</script>
{% endblock %}
\ No newline at end of file
......@@ -5,13 +5,6 @@
{% 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>
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: left;
}
</style>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -94,7 +87,7 @@ div.dataTables_wrapper div.dataTables_filter,
$(document).ready(function () {
$('.select2').select2();
$('.select2-system-user').select2();
})
});
$('#add_asset').on('click',function(){
$('#modal').modal('show');
......@@ -104,7 +97,7 @@ div.dataTables_wrapper div.dataTables_filter,
show: false,
backdrop: 'static',
keyboard: 'false',
remote:"{% url 'assets:asset-modal-list' %}?group_id={{ group_id }}",
remote:"{% url 'assets:asset-modal-list' %}?group_id={{ group_id }}"
});
$('#modal').on('show.bs.modal',function(){
......
......@@ -7,19 +7,11 @@
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<style>
div.dataTables_wrapper div.dataTables_filter {
margin-left: 15px;
float: right !important;
}
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: right !important;
}
.custom{
/*float:left;*/
margin-right:5px;
}
#modal .modal-body { max-height: 200px; }
.custom{
/*float:left;*/
margin-right:5px;
}
#modal .modal-body { max-height: 200px; }
</style>
{% endblock %}
{% block content_left_head %}{% endblock %}
......@@ -80,7 +72,8 @@ div.dataTables_wrapper div.dataTables_filter,
</td>
<td class="text-center">
<a href="{% url 'assets:asset-update' pk=asset.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a onclick="objectDelete(this,'{{ asset.hostname }}','{% url 'assets:asset-delete' pk=asset.id %}')" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
<a onclick="objectDelete(this,'{{ asset.hostname }}','{% url 'api-assets:asset-detail' pk=asset.id %}')" class="btn btn-xs btn-danger del">
{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
......@@ -190,18 +183,18 @@ div.dataTables_wrapper div.dataTables_filter,
}else{
$(this).addClass('selected');
this.children[0].children[0].checked=1;
};
}
});
$('#btn_bulk_update').on('click',function(){
var column2 = table.rows('.selected').data();
var id_list = [];
var plain_id_list = [];
var the_url = "{% url 'assets:asset-bulk-update-api' %}";
var the_url = "{% url 'api-assets:asset-bulk-update' %}";
for(var i=0;i<column2.length;i++){
id_list.push({id: column2[i].id,hostname:column2[i].ip});
plain_id_list.push(parseInt(column2[i].id));
};
}
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
//APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
......@@ -227,18 +220,18 @@ div.dataTables_wrapper div.dataTables_filter,
}
});
});
};
}
function doUpdate() {
// alert(plain_id_list);
// $('#asset_bulk_update_modal').modal('show');
window.location.href="{% url 'assets:asset-modal-update' %}?plain_id_lists="+plain_id_list
};
}
var action = $('#slct_bulk_update option:selected').val();
if (id_list.length === 0) {
action = 'default';
};
}
switch(action) {
case 'deactive':
alert(action+"未完成");
......
......@@ -46,9 +46,7 @@
</div>
<script type="text/javascript">
$(document).ready(function(){
var table = $('#editable').DataTable({
"aLengthMenu": [[10, 25, 50, -1], ["10", "25", "50", "all"]],
"iDisplayLength":25,
......
......@@ -8,7 +8,7 @@
<body>
<form action="" method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object.name }}"?</p>
<p>{% trans 'Are you sure delete' %} <b>{{ object.name }} </b> ?</p>
<input type="submit" value="Confirm" />
</form>
</body>
......
{% 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:idc-detail' pk=idc.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li class="active"><a href="{% url 'assets:idc-assets' pk=idc.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'IDC 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 style="float: left">{% trans 'IDC assets' %} <b>{{ idc.name }} </b><span class="badge"></span></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 table-striped table-bordered table-hover " id="idc_assets_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Type' %}</th>
<th>{% trans 'Valid' %}</th>
</tr>
</thead>
<tbody>
</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 'Attach to assets ' %}
</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_remain %}
<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 'Attach' %}</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
var options = {
ele: $('#idc_assets_table'),
buttons: [],
order: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}}],
ajax_url: '{% url "api-assets:asset-list" %}?idc={{ idc.id }}',
columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port" },
{data: "type" }, {data: "is_active" }]
};
jumpserver.initDataTable(options);
});
</script>
{% endblock %}
\ No newline at end of file
{% 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:idc-detail' pk=idc.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
</li>
<li>
<a href="{% url 'assets:idc-assets' pk=idc.id %}" class="text-center">
<i class="fa fa-bar-chart-o"></i> {% trans 'IDC assets' %}
</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:idc-update' pk=idc.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger" href="{% url 'assets:idc-delete' pk=idc.id %}">
<i class="fa fa-edit"></i>Delete
</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-9" style="padding-left: 0;">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ idc.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>{% trans 'Name' %}:</td>
<td><b>{{ idc.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Bandwidth' %}:</td>
<td><b>{{ idc.bandwidth }}</b></td>
</tr>
<tr>
<td>{% trans 'Contact' %}:</td>
<td><b>{{ idc.contact }}</b></td>
</tr>
<tr>
<td>{% trans 'Phone' %}:</td>
<td><b>{{ idc.phone }}</b></td>
</tr>
<tr>
<td>{% trans 'Address' %}:</td>
<td><b>{{ idc.address }}</b></td>
</tr>
<tr>
<td>{% trans 'Intranet' %}:</td>
<td><b>{{ idc.Intranet }}</b></td>
</tr>
<tr>
<td>{% trans 'Extranet' %}:</td>
<td><b>{{ idc.extranet }}</b></td>
</tr>
<tr>
<td>{% trans 'Operator' %}:</td>
<td><b>{{ idc.operator }}</b></td>
</tr>
<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>
</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 %}
\ No newline at end of file
{% extends '_base_list.html' %}
{% load i18n %}
{% load common_tags %}
{% block content_left_head %}
<a href="{% url 'assets:idc-create' %}" class="btn btn-sm btn-primary "> {% trans "Create IDC" %} </a>
{% endblock %}
{% block table_head %}
<th class="text-center">
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">
</th>
<th class="text-center"><a href="{% url 'assets:idc-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Contact' %}</th>
<th class="text-center">{% trans 'Phone' %}</th>
<th class="text-center">{% trans 'operation' %}</th>
{% endblock %}
{% block table_body %}
{% for idc in idc_list %}
<tr class="gradeX">
<td class="text-center">
<input type="checkbox" name="checked" value="{{ idc.id }}">
</td>
<td class="text-center">{{ idc.name }}</td>
<td class="text-center">{{ idc.assets.count }}</td>
{# <td class="text-center">{{ idc.bandwidth }}</td>#}
<td class="text-center">{{ idc.contact }}</td>
<td class="text-center">{{ idc.phone }}</td>
{# <td class="text-center">{{ idc.address }}</td>#}
<td class="text-center">
<a href="{% url 'assets:idc-update' pk=idc.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>
<a onclick="objectDelete(this, '{{ idc.name }}', '{% url 'assets:idc-delete' idc.id %}')" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>
</td>
</tr>
{% endfor %}
{% load i18n static %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5">
<a href="{% url "assets:idc-create" %}" class="btn btn-sm btn-primary"> {% trans "Create IDC" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="idc_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center"><a href="{% url 'assets:idc-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Contact' %}</th>
<th class="text-center">{% trans 'Phone' %}</th>
<th class="text-center">{% trans 'Operator' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function(){
var options = {
ele: $('#idc_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:idc-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{# {targets: 4, createdCell: function (td, cellData) {#}
{# var innerHtml = cellData.length > 8 ? cellData.substring(0, 8) + '...': cellData;#}
{# $(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');#}
{# }},#}
{# {targets: 6, createdCell: function (td, cellData) {#}
{# if (!cellData) {#}
{# $(td).html('<i class="fa fa-times text-danger"></i>')#}
{# } else {#}
{# $(td).html('<i class="fa fa-check text-navy"></i>')#}
{# }#}
{# }},#}
{targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:idc-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
$(td).html(update_btn + del_btn)
}}],
ajax_url: '{% url "api-assets:idc-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }, {data: "contact" }, {data: "phone" },
{data: "operator" }, {data: "id" }],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
});
</script>
{% endblock %}
# coding:utf-8
from django.conf.urls import url, include
import views
import api
# from .api import (
# AssetGroupViewSet, AssetViewSet, IDCViewSet
# )
# from rest_framework import routers
# router = routers.DefaultRouter()
# router.register(r'assetgroup', AssetGroupViewSet)
# router.register(r'asset', AssetViewSet)
# router.register(r'idc', IDCViewSet)
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'),
url(r'^asset-modal$', views.AssetModalListView.as_view(), name='asset-modal-list'),
url(r'^asset-modal-update$', views.AssetModalCreateView.as_view(), name='asset-modal-update'),
# 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'),
url(r'^tags$', views.TagsListView.as_view(), name='asset-tag-list'),
url(r'^asset-by-tag/(?P<tag_id>[0-9]+)$', views.TagView.as_view(), name='asset-tags'),
url(r'^tags/create$', views.AssetTagCreateView.as_view(), name='asset-tag-create'),
url(r'^asset-tag/(?P<pk>[0-9]+)$', views.AssetTagDetailView.as_view(), name='asset-tag-detail'),
url(r'^asset-tag/(?P<pk>[0-9]+)/update$', views.AssetTagUpdateView.as_view(), name='asset-tag-update'),
url(r'^asset-tag/(?P<pk>[0-9]+)/delete$', views.AssetTagDeleteView.as_view(), name='asset-tag-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'),
]
urlpatterns += [
url(r'^v1/assets/$', api.AssetViewSet.as_view({'get':'list'}), name='assets-list-api'),
url(r'^v1/assets_bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update-api'),
url(r'^v1/idc/$', api.IDCViewSet.as_view({'get':'list'}), name='idc-list-json'),
url(r'^v1/system-user/auth/', api.SystemUserAuthApi.as_view(), name='system-user-auth'),
]
# coding:utf-8
from django.conf.urls import url
from .. import api
from rest_framework import routers
app_name = 'assets'
router = routers.DefaultRouter()
router.register(r'v1/asset-groups', api.AssetGroupViewSet, 'asset-group')
router.register(r'v1/assets', api.AssetViewSet, 'asset')
router.register(r'v1/idc', api.IDCViewSet, 'idc')
router.register(r'v1/admin-user', api.AdminUserViewSet, 'admin-user')
router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
urlpatterns = [
url(r'^v1/assets_bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
# url(r'^v1/idc/(?P<pk>[0-9]+)/assets/$', api.IDCAssetsApi.as_view(), name='api-idc-assets'),
url(r'^v1/system-user/auth/', api.SystemUserAuthApi.as_view(), name='system-user-auth'),
]
urlpatterns += router.urls
# coding:utf-8
from django.conf.urls import url
from .. 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'),
url(r'^asset-modal$', views.AssetModalListView.as_view(), name='asset-modal-list'),
url(r'^asset-modal-update$', views.AssetModalCreateView.as_view(), name='asset-modal-update'),
# 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'),
url(r'^tags/$', views.TagsListView.as_view(), name='asset-tag-list'),
url(r'^asset-by-tag/(?P<tag_id>[0-9]+)/$', views.TagView.as_view(), name='asset-tags'),
url(r'^tags/create/$', views.AssetTagCreateView.as_view(), name='asset-tag-create'),
url(r'^asset-tag/(?P<pk>[0-9]+)/$', views.AssetTagDetailView.as_view(), name='asset-tag-detail'),
url(r'^asset-tag/(?P<pk>[0-9]+)/update/$', views.AssetTagUpdateView.as_view(), name='asset-tag-update'),
url(r'^asset-tag/(?P<pk>[0-9]+)/delete/$', views.AssetTagDeleteView.as_view(), name='asset-tag-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'),
url(r'^idc/(?P<pk>[0-9]+)/assets/$', views.IDCAssetsView.as_view(), name='idc-assets'),
# 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'),
]
......@@ -30,21 +30,21 @@ class AssetListView(AdminUserRequiredMixin, ListView):
@staticmethod
def sorted_by_valid_and_ip(asset):
ip_list = int_seq(asset.ip.split('.'))
ip_list.insert(0, asset.is_valid()[0])
ip_list.insert(0, asset.is_valid[0])
return ip_list
def get_context_data(self, **kwargs):
context = {
'app': 'Assets',
'action': 'asset list',
'tag_list': [(i.id,i.name,i.asset_set.all().count())for i in Tag.objects.all().order_by('name')]
'tag_list': [(i.id, i.name, i.asset_set.all().count())for i in Tag.objects.all().order_by('name')]
}
kwargs.update(context)
return super(AssetListView, self).get_context_data(**kwargs)
class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView):
class AssetCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, CreateView):
model = Asset
tag_type = 'asset'
form_class = AssetCreateForm
......@@ -58,7 +58,8 @@ class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView):
return super(AssetCreateView, self).form_valid(form)
def form_invalid(self, form):
print(form.errors)
if form.errors.get('__all__'):
form.errors['all'] = form.errors.get('__all__')
return super(AssetCreateView, self).form_invalid(form)
def get_context_data(self, **kwargs):
......@@ -207,7 +208,7 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
plain_id_lists = self.request.GET.get('plain_id_lists')
self.s = self.request.GET.get('plain_id_lists')
if "," in str(self.s):
self.plain_id_lists = [int(x) for x in self.s.split(',')]
self.plain_id_lists = [int(x) for x in self.s.split(',')]
else:
self.plain_id_lists = [self.s]
......@@ -217,19 +218,19 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
else:
plain_id_lists = [int(self.s)]
context = {
'all_assets':plain_id_lists
'all_assets' :plain_id_lists
}
kwargs.update(context)
if group_id:
group = AssetGroup.objects.get(id=group_id)
context = {
'all_assets':[x.id for x in group.assets.all()]
'all_assets': [x.id for x in group.assets.all()]
}
kwargs.update(context)
if tag_id:
tag = Tag.objects.get(id=tag_id)
context = {
'all_assets':[x.id for x in tag.asset_set.all()]
'all_assets': [x.id for x in tag.asset_set.all()]
}
kwargs.update(context)
return super(AssetModalListView, self).get_context_data(**kwargs)
......@@ -341,33 +342,33 @@ class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:asset-group-list')
class IDCListView(AdminUserRequiredMixin, ListView):
model = IDC
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'idc_list'
class IDCListView(AdminUserRequiredMixin, TemplateView):
# model = IDC
# paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
# context_object_name = 'idc_list'
template_name = 'assets/idc_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('IDC list'),
'keyword': self.request.GET.get('keyword', '')
# 'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(IDCListView, self).get_context_data(**kwargs)
def get_queryset(self):
self.queryset = super(IDCListView, 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
# def get_queryset(self):
# self.queryset = super(IDCListView, 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 IDCCreateView(AdminUserRequiredMixin, CreateView):
......@@ -414,7 +415,15 @@ class IDCUpdateView(AdminUserRequiredMixin, UpdateView):
class IDCDetailView(AdminUserRequiredMixin, DetailView):
pass
model = IDC
template_name = 'assets/idc_detail.html'
context_object_name = 'idc'
class IDCAssetsView(AdminUserRequiredMixin, DetailView):
model = IDC
template_name = 'assets/idc_assets.html'
context_object_name = 'idc'
class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
......@@ -423,34 +432,34 @@ class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:idc-list')
class AdminUserListView(AdminUserRequiredMixin, ListView):
class AdminUserListView(AdminUserRequiredMixin, TemplateView):
model = AdminUser
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'admin_user_list'
# paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
# context_object_name = 'admin_user_list'
template_name = 'assets/admin_user_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Admin user list'),
'keyword': self.request.GET.get('keyword', '')
# 'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(AdminUserListView, self).get_context_data(**kwargs)
def get_queryset(self):
# 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')
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
# def get_queryset(self):
# 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')
#
# 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 AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
......@@ -475,6 +484,9 @@ class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
))
return success_message
def form_invalid(self, form):
return super(AdminUserCreateView, self).form_invalid(form)
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser
......
......@@ -3,14 +3,14 @@
from __future__ import absolute_import, unicode_literals
from rest_framework import generics
from rest_framework import generics, viewsets
from rest_framework.views import APIView, Response
from . import models, serializers
from .hands import IsSuperUserOrTerminalUser, Terminal
class ProxyLogListCreateApi(generics.ListCreateAPIView):
class ProxyLogViewSet(viewsets.ModelViewSet):
"""User proxy to backend server need call this api.
params: {
......@@ -34,18 +34,8 @@ class ProxyLogListCreateApi(generics.ListCreateAPIView):
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
def perform_create(self, serializer):
# Todo: May be save log_file
super(ProxyLogListCreateApi, self).perform_create(serializer)
class ProxyLogDetailApi(generics.RetrieveUpdateDestroyAPIView):
queryset = models.ProxyLog.objects.all()
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
class CommandLogListCreateApi(generics.ListCreateAPIView):
class CommandLogViewSet(viewsets.ModelViewSet):
queryset = models.CommandLog.objects.all()
serializer_class = serializers.CommandLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
......
# ~*~ coding: utf-8 ~*~
#
from users.utils import AdminUserRequiredMixin
from users.models import User
from assets.models import Asset, SystemUser
from users.backends import IsSuperUserOrTerminalUser
......
......@@ -22,9 +22,7 @@ class LoginLog(models.Model):
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
login_city = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Login city'))
user_agent = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('User agent'))
from_terminal = models.ForeignKey
date_login = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
date_logout = models.DateTimeField(null=True, verbose_name=_('Date logout'))
class Meta:
db_table = 'login_log'
......
#!/usr/bin/env python
# ~*~ coding: utf-8 ~*~
#
from celery import shared_task
from .utils import write_login_log
@shared_task
def write_login_log_async(*args, **kwargs):
write_login_log(*args, **kwargs)
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% load common_tags %}
{% block content_left_head %}
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<style>
#search_btn {
margin-bottom: 0;
}
</style>
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
<div class="form-group" id="date">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
</div>
</div>
<div class="input-group">
<select class="select2 form-control" name="username">
<option value="">{% trans 'Select user' %}</option>
{% for user in user_list %}
<option value="{{ user.username }}" {% if user.username == username %} selected {% endif %}>{{ user.username }}</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_head %}
<th class="text-center">{% trans 'ID' %}</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'UA' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'City' %}</th>
<th class="text-center">{% trans 'Date' %}</th>
{% endblock %}
{% block table_body %}
{% for login_log in login_log_list %}
<tr class="gradeX">
<td class="text-center">
{{ login_log.id }}
{# <a href="{% url 'audits:proxy-log-detail' pk=login_log.id %}">{{ login_log.id }}</a>#}
</td>
<td class="text-center">{{ login_log.username }}</td>
<td class="text-center">{{ login_log.name }}</td>
<td class="text-center">{{ login_log.get_login_type_display }}</td>
{% if login_log.login_type == 'W' %}
<td class="text-center">
<span href="javascript:void(0);" data-toggle="tooltips" title="{{ login_log.user_agent }}">{{ login_log.user_agent | truncatechars:20 }}</span>
</td>
{% else %}
<td class="text-center">{{ login_log.terminal }}</td>
{% endif %}
<td class="text-center">{{ login_log.login_ip }}</td>
<td class="text-center">{{ login_log.login_city }}</td>
<td class="text-center">{{ login_log.date_login }}</td>
</tr>
{% endfor %}
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function() {
$('table').DataTable({
"searching": false,
"paging": false,
"order": []
});
$('#date .input-daterange').datepicker({
dateFormat: 'mm/dd/yy',
keyboardNavigation: false,
forceParse: false,
autoclose: true
});
$('.select2').select2();
})
</script>
{% endblock %}
from django.conf.urls import url
from rest_framework import routers
from .. import api
app_name = 'audits'
router = routers.DefaultRouter()
router.register(r'v1/proxy-log', api.ProxyLogViewSet, 'proxy-log')
router.register(r'v1/command-log', api.CommandLogViewSet, 'command-log')
urlpatterns = router.urls
from django.conf.urls import url
import api
import views
from .. import views
app_name = 'audits'
......@@ -11,11 +8,7 @@ urlpatterns = [
url(r'^proxy-log/(?P<pk>\d+)$', views.ProxyLogDetailView.as_view(), name='proxy-log-detail'),
url(r'^proxy-log/(?P<pk>\d+)/commands$', views.ProxyLogCommandsListView.as_view(), name='proxy-log-commands-list'),
url(r'^command-log$', views.CommandLogListView.as_view(), name='command-log-list'),
url(r'^login-log$', views.LoginLogListView.as_view(), name='login-log-list'),
]
urlpatterns += [
url(r'^v1/proxy-log/$', api.ProxyLogListCreateApi.as_view(), name='proxy-log-list-create-api'),
url(r'^v1/proxy-log/(?P<pk>\d+)/$', api.ProxyLogDetailApi.as_view(), name='proxy-log-detail-api'),
url(r'^v1/command-log/$', api.CommandLogListCreateApi.as_view(), name='command-log-create-list-api'),
]
# ~*~ coding: utf-8 ~*~
#
from users.utils import AdminUserRequiredMixin
from __future__ import unicode_literals
import requests
import ipaddress
from .models import LoginLog
def validate_ip(ip):
try:
ipaddress.ip_address(ip.decode('utf-8'))
return True
except ValueError:
print('valid error')
return False
def write_login_log(username, name='', login_type='W',
terminal='', login_ip='', user_agent=''):
if not (login_ip and validate_ip(login_ip)):
login_ip = '0.0.0.0'
if not name:
name = username
login_city = get_ip_city(login_ip)
LoginLog.objects.create(username=username, name=name, login_type=login_type, login_ip=login_ip,
terminal=terminal, login_city=login_city, user_agent=user_agent)
def get_ip_city(ip, timeout=10):
# Taobao ip api: http://ip.taobao.com//service/getIpInfo.php?ip=8.8.8.8
# Sina ip api: http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=8.8.8.8&format=js
url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' + ip
r = requests.get(url, timeout=timeout)
city = 'Unknown'
if r.status_code == 200:
try:
data = r.json()
if data['code'] == 0:
city = data['data']['country'] + data['data']['city']
except ValueError:
pass
return city
......@@ -10,13 +10,13 @@ from django.urls import reverse_lazy
from django.conf import settings
from django.db.models import Q
from .models import ProxyLog, CommandLog
from .utils import AdminUserRequiredMixin
from .hands import User, Asset, SystemUser
from .models import ProxyLog, CommandLog, LoginLog
from .hands import User, Asset, SystemUser, AdminUserRequiredMixin
seven_days_ago_s = (datetime.datetime.now()-datetime.timedelta(7)).strftime('%m/%d/%Y')
now_s = datetime.datetime.now().strftime('%m/%d/%Y')
date_now = timezone.localtime(timezone.now())
now_s = date_now.strftime('%m/%d/%Y')
seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
class ProxyLogListView(AdminUserRequiredMixin, ListView):
......@@ -54,6 +54,7 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
return self.queryset
def get_context_data(self, **kwargs):
print(self.date_to_s)
context = {
'app': _('Audits'),
'action': _('Proxy log list'),
......@@ -152,3 +153,43 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
}
kwargs.update(context)
return super(CommandLogListView, self).get_context_data(**kwargs)
class LoginLogListView(AdminUserRequiredMixin, ListView):
model = LoginLog
template_name = 'audits/login_log_list.html'
context_object_name = 'login_log_list'
def get_queryset(self):
self.queryset = super(LoginLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.username = username = self.request.GET.get('username', '')
self.date_from_s = date_from_s = self.request.GET.get('date_from', '%s' % seven_days_ago_s)
self.date_to_s = date_to_s = self.request.GET.get('date_to', '%s' % now_s)
if date_from_s:
date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
self.queryset = self.queryset.filter(date_login__gt=date_from)
if date_to_s:
date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
self.queryset = self.queryset.filter(date_login__lt=date_to)
if username:
self.queryset = self.queryset.filter(username=username)
if keyword:
self.queryset = self.queryset.filter(Q(username__contains=keyword) |
Q(name__icontains=keyword) |
Q(login_ip=keyword)).distinct()
return self.queryset
def get_context_data(self, **kwargs):
context = {
'app': _('Audits'),
'action': _('Proxy log list'),
'user_list': User.objects.all().order_by('username'),
'keyword': self.keyword,
'date_from': self.date_from_s,
'date_to': self.date_to_s,
'username': self.username,
}
kwargs.update(context)
return super(LoginLogListView, self).get_context_data(**kwargs)
......@@ -3,4 +3,3 @@ from __future__ import absolute_import
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
# ~*~ coding: utf-8 ~*~
from __future__ import absolute_import, unicode_literals
import os
from datetime import timedelta
from celery import Celery
......@@ -16,6 +16,5 @@ app = Celery('jumpserver')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
from __future__ import absolute_import
from celery import shared_task
# from celery import shared_task
from django.core.mail import send_mail
from django.conf import settings
from common import celery_app as app
@shared_task(name='send_mail_async')
@app.task
def send_mail_async(*args, **kwargs):
""" Using celery to send email async
......@@ -26,9 +27,3 @@ def send_mail_async(*args, **kwargs):
args = tuple(args)
send_mail(*args, **kwargs)
# def send_mail_async(subject, message, from_mail, recipient_list, fail_silently=False, html_message=None):
# if settings.CONFIG.MAIL_SUBJECT_PREFIX:
# subject += settings.CONFIG.MAIL_SUBJECT_PREFIX
# send_mail(subject, message, from_mail, recipient_list, fail_silently=fail_silently, html_message=html_message)
......@@ -3,11 +3,15 @@
from __future__ import unicode_literals
from six import string_types
import os
from itertools import chain
import string
import logging
import datetime
import paramiko
import paramiko
import sshpubkeys
from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \
BadSignature, SignatureExpired
from django.shortcuts import reverse as dj_reverse
......@@ -15,6 +19,11 @@ from django.conf import settings
from django.core import signing
from django.utils import timezone
try:
import cStringIO as StringIO
except ImportError:
import StringIO
SECRET_KEY = settings.SECRET_KEY
......@@ -162,4 +171,88 @@ def timesince(dt, since='', default="just now"):
return default
def ssh_key_string_to_obj(text):
key_f = StringIO.StringIO(text)
key = None
try:
key = paramiko.RSAKey.from_private_key(key_f)
except paramiko.SSHException:
pass
try:
key = paramiko.DSSKey.from_private_key(key_f)
except paramiko.SSHException:
pass
return key
def ssh_pubkey_gen(private_key=None, username='jumpserver', hostname='localhost'):
if isinstance(private_key, string_types):
private_key = ssh_key_string_to_obj(private_key)
if not isinstance(private_key, (paramiko.RSAKey, paramiko.DSSKey)):
raise IOError('Invalid private key')
public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % {
'key_type': private_key.get_name(),
'key_content': private_key.get_base64(),
'username': username,
'hostname': hostname,
}
return public_key
def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', hostname=None):
"""Generate user ssh private and public key
Use paramiko RSAKey generate it.
:return private key str and public key str
"""
if hostname is None:
hostname = os.uname()[1]
f = StringIO.StringIO()
try:
if type == 'rsa':
private_key_obj = paramiko.RSAKey.generate(length)
elif type == 'dsa':
private_key_obj = paramiko.DSSKey.generate(length)
else:
raise IOError('SSH private key must be `rsa` or `dsa`')
private_key_obj.write_private_key(f, password=password)
private_key = f.getvalue()
public_key = ssh_pubkey_gen(private_key_obj, username=username, hostname=hostname)
return private_key, public_key
except IOError:
raise IOError('These is error when generate ssh key.')
def validate_ssh_private_key(text):
key = ssh_key_string_to_obj(text)
if key is None:
return False
else:
return True
def validate_ssh_public_key(text):
ssh = sshpubkeys.SSHKey(text)
try:
ssh.parse()
except sshpubkeys.InvalidKeyException:
return False
except NotImplementedError as e:
return False
return True
def setattr_bulk(seq, key, value):
def set_attr(obj):
setattr(obj, key, value)
return obj
return map(set_attr, seq)
signer = Signer()
\ No newline at end of file
......@@ -2,6 +2,3 @@ from __future__ import absolute_import, unicode_literals
from django.shortcuts import render
from django.views.generic import TemplateView
......@@ -14,6 +14,7 @@ import os
import sys
from django.urls import reverse_lazy
from datetime import timedelta
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
......@@ -284,6 +285,31 @@ BROKER_URL = 'redis://%(password)s%(host)s:%(port)s/3' % {
}
CELERY_RESULT_BACKEND = BROKER_URL
# TERMINAL_HEATBEAT_INTERVAL = CONFIG.TERMINAL_HEATBEAT_INTERVAL or 30
# crontab job
# CELERYBEAT_SCHEDULE = {
# Check terminal is alive every 10m
# 'check_terminal_alive': {
# 'task': 'terminal.tasks.check_terminal_alive',
# 'schedule': timedelta(seconds=TERMINAL_HEATBEAT_INTERVAL),
# 'args': (),
# },
# }
# Cache use redis
CACHES = {
'default': {
'BACKEND': 'redis_cache.RedisCache',
'LOCATION': 'redis://%(password)s%(host)s:%(port)s/4' % {
'password': CONFIG.REDIS_PASSWORD + '@' if CONFIG.REDIS_PASSWORD else '',
'host': CONFIG.REDIS_HOST or '127.0.0.1',
'port': CONFIG.REDIS_PORT or 6379,
}
}
}
# Captcha settings, more see https://django-simple-captcha.readthedocs.io/en/latest/advanced.html
CAPTCHA_IMAGE_SIZE = (75, 33)
CAPTCHA_FOREGROUND_COLOR = '#001100'
......
......@@ -22,11 +22,16 @@ from django.views.generic.base import TemplateView
urlpatterns = [
url(r'^captcha/', include('captcha.urls')),
url(r'^$', TemplateView.as_view(template_name='base.html'), name='index'),
url(r'^(api/)?users/', include('users.urls')),
url(r'^(api/)?assets/', include('assets.urls')),
url(r'^(api/)?perms/', include('perms.urls')),
url(r'^(api/)?audits/', include('audits.urls')),
url(r'^(api/)?terminal/', include('terminal.urls')),
url(r'^users/', include('users.urls.views_urls', namespace='users')),
url(r'^assets/', include('assets.urls.views_urls', namespace='assets')),
url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
url(r'^audits/', include('audits.urls.views_urls', namespace='audits')),
url(r'^terminal/', include('terminal.urls.views_urls', namespace='terminal')),
url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')),
url(r'^api/assets/', include('assets.urls.api_urls', namespace='api-assets')),
url(r'^api/perms/', include('perms.urls.api_urls', namespace='api-perms')),
url(r'^api/audits/', include('audits.urls.api_urls', namespace='api-audits')),
url(r'^api/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')),
]
......
......@@ -1610,7 +1610,7 @@ msgid ""
"here reset password</a>\n"
" </br>\n"
" This link is valid for 1 hour. After it expires, <a href=\"%"
"(forget_password_url)s?email=%(email)s\">request new one<</a>\n"
"(forget_password_url)s?email=%(email)s\">request new one</a>\n"
"\n"
" </br>\n"
" ---\n"
......
# coding:utf-8
from django.conf.urls import url
from rest_framework import routers
# coding:utf-8
from django.conf.urls import url
from .. import views
app_name = 'ops'
......@@ -2,49 +2,120 @@
#
from rest_framework.views import APIView, Response
from rest_framework.generics import ListCreateAPIView
from rest_framework.generics import ListAPIView, get_object_or_404
from rest_framework import viewsets
from users.backends import IsValidUser, IsSuperUser
from .utils import get_user_granted_assets, get_user_granted_asset_groups
from common.utils import get_object_or_none
from .utils import get_user_granted_assets, get_user_granted_asset_groups, get_user_asset_permissions, \
get_user_group_asset_permissions, get_user_group_granted_assets, get_user_group_granted_asset_groups
from .models import AssetPermission
from .hands import AssetGrantedSerializer, User, UserGroup, AssetGroup, Asset, AssetGroup, AssetGroupSerializer
from . import serializers
class AssetPermissionListCreateApi(ListCreateAPIView):
class AssetPermissionViewSet(viewsets.ModelViewSet):
queryset = AssetPermission.objects.all()
serializer_class = serializers.AssetPermissionSerializer
permission_classes = (IsSuperUser,)
def get_queryset(self):
queryset = super(AssetPermissionViewSet, self).get_queryset()
user_id = self.request.query_params.get('user', '')
user_group_id = self.request.query_params.get('user_group', '')
class UserAssetsApi(APIView):
permission_classes = (IsValidUser,)
if user_id and user_id.isdigit():
user = get_object_or_404(User, id=int(user_id))
queryset = get_user_asset_permissions(user)
def get(self, request, *args, **kwargs):
assets_json = []
user = request.user
if user_group_id:
user_group = get_object_or_404(UserGroup, id=user_group_id)
queryset = get_user_group_asset_permissions(user_group)
return queryset
def get_serializer_class(self):
if getattr(self, 'user_id', ''):
return serializers.UserAssetPermissionSerializer
return serializers.AssetPermissionSerializer
class RevokeUserAssetPermission(APIView):
permission_classes = (IsSuperUser,)
def put(self, request, *args, **kwargs):
permission_id = str(request.data.get('id', ''))
user_id = str(request.data.get('user_id', ''))
if permission_id and user_id and permission_id.isdigit() and user_id.isdigit():
asset_permission = get_object_or_404(AssetPermission, id=int(permission_id))
user = get_object_or_404(User, id=int(user_id))
if asset_permission and user:
asset_permission.users.remove(user)
return Response({'msg': 'success'})
return Response({'msg': 'failed'}, status=404)
class RevokeUserGroupAssetPermission(APIView):
permission_classes = (IsSuperUser,)
def put(self, request, *args, **kwargs):
permission_id = str(request.data.get('id', ''))
user_group_id = str(request.data.get('user_group_id', ''))
if permission_id and user_group_id and permission_id.isdigit() and user_group_id.isdigit():
asset_permission = get_object_or_404(AssetPermission, id=int(permission_id))
user_group = get_object_or_404(UserGroup, id=int(user_group_id))
if asset_permission and user_group:
asset_permission.user_groups.remove(user_group)
return Response({'msg': 'success'})
return Response({'msg': 'failed'}, status=404)
class UserGrantedAssetsApi(ListAPIView):
permission_classes = (IsSuperUser,)
serializer_class = AssetGrantedSerializer
def get_queryset(self):
user_id = self.kwargs.get('pk', '')
if user_id:
user = get_object_or_404(User, id=user_id)
queryset = get_user_granted_assets(user)
else:
queryset = []
return queryset
class UserGrantedAssetGroupsApi(ListAPIView):
permission_classes = (IsSuperUser,)
serializer_class = AssetGroupSerializer
def get_queryset(self):
user_id = self.kwargs.get('pk', '')
if user_id:
user = get_object_or_404(User, id=user_id)
queryset = get_user_granted_asset_groups(user)
else:
queryset = []
return queryset
class MyGrantedAssetsApi(ListAPIView):
permission_classes = (IsValidUser,)
serializer_class = AssetGrantedSerializer
def get_queryset(self):
user = self.request.user
if user:
assets = get_user_granted_assets(user)
queryset = get_user_granted_assets(user)
else:
queryset = []
return queryset
for asset, system_users in assets.items():
assets_json.append({
'id': asset.id,
'hostname': asset.hostname,
'ip': asset.ip,
'port': asset.port,
'system_users': [
{
'id': system_user.id,
'name': system_user.name,
'username': system_user.username,
} for system_user in system_users
],
'comment': asset.comment
})
return Response(assets_json, status=200)
class UserAssetsGroupsApi(APIView):
class MyGrantedAssetsGroupsApi(APIView):
permission_classes = (IsValidUser,)
def get(self, request, *args, **kwargs):
......@@ -56,46 +127,61 @@ class UserAssetsGroupsApi(APIView):
for asset in assets:
for asset_group in asset.groups.all():
if asset_group.id in asset_groups:
asset_groups[asset_group.id]['asset_num'] += 1
asset_groups[asset_group.id]['asset_amount'] += 1
else:
asset_groups[asset_group.id] = {
'id': asset_group.id,
'name': asset_group.name,
'comment': asset_group.comment,
'asset_num': 1
'assets_amount': 1
}
asset_groups_json = asset_groups.values()
return Response(asset_groups_json, status=200)
class UserAssetsGroupAssetsApi(APIView):
class MyAssetGroupAssetsApi(ListAPIView):
permission_classes = (IsValidUser,)
serializer_class = AssetGrantedSerializer
def get(self, request, *args, **kwargs):
# asset_group_id = request.query_params.get('asset_group_id', -1)
asset_group_id = kwargs.get('pk', -1)
# asset_group_name = request.query_params.get('asset_group_name', '')
user = request.user
assets_json = []
def get_queryset(self):
queryset = []
asset_group_id = self.kwargs.get('pk', -1)
user = self.request.user
asset_group = get_object_or_none(AssetGroup, id=asset_group_id)
if user:
if user and asset_group:
assets = get_user_granted_assets(user)
for asset, system_users in assets.items():
for asset_group in asset.groups.all():
if str(asset_group.id) == asset_group_id: # and asset_group.name == asset_group_name:
assets_json.append({
'id': asset.id,
'hostname': asset.hostname,
'ip': asset.ip,
'port': asset.port,
'system_users': [
{
'id': system_user.id,
'name': system_user.name,
'username': system_user.username,
} for system_user in system_users
],
'comment': asset.comment
})
return Response(assets_json, status=200)
for asset in assets:
if asset_group in asset.groups.all():
queryset.append(asset)
return queryset
class UserGroupGrantedAssetsApi(ListAPIView):
permission_classes = (IsSuperUser,)
serializer_class = AssetGrantedSerializer
def get_queryset(self):
user_group_id = self.kwargs.get('pk', '')
if user_group_id:
user_group = get_object_or_404(UserGroup, id=user_group_id)
queryset = get_user_group_granted_assets(user_group)
else:
queryset = []
return queryset
class UserGroupGrantedAssetGroupsApi(ListAPIView):
permission_classes = (IsSuperUser,)
serializer_class = AssetGroupSerializer
def get_queryset(self):
user_group_id = self.kwargs.get('pk', '')
if user_group_id:
user_group = get_object_or_404(UserGroup, id=user_group_id)
queryset = get_user_group_granted_asset_groups(user_group)
else:
queryset = []
return queryset
\ No newline at end of file
......@@ -4,6 +4,7 @@
from users.utils import AdminUserRequiredMixin
from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser
from assets.serializers import AssetGrantedSerializer, AssetGroupSerializer
def associate_system_users_with_assets(system_users, assets, asset_groups):
......
# -*- coding: utf-8 -*-
#
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.utils import get_object_or_none
from .models import AssetPermission
from .hands import User
class AssetPermissionSerializer(serializers.ModelSerializer):
# users_amount = serializers.SerializerMethodField()
# user_groups_amount = serializers.SerializerMethodField()
# assets_amount = serializers.SerializerMethodField()
# asset_groups_amount = serializers.SerializerMethodField()
class Meta:
model = AssetPermission
fields = ['id', 'name', 'users', 'user_groups', 'assets', 'asset_groups',
'system_users', 'is_active', 'comment', 'date_expired']
# @staticmethod
# def get_users_amount(obj):
# return obj.users.count()
#
# @staticmethod
# def get_user_groups_amount(obj):
# return obj.user_groups.count()
#
# @staticmethod
# def get_assets_amount(obj):
# return obj.assets.count()
#
# @staticmethod
# def get_asset_groups_amount(obj):
# return obj.asset_groups.count()
class UserAssetPermissionSerializer(AssetPermissionSerializer):
is_inherited = serializers.SerializerMethodField()
@staticmethod
def get_is_inherited(obj):
if getattr(obj, 'inherited', ''):
return True
else:
return False
# coding:utf-8
from django.conf.urls import url
from rest_framework import routers
from .. import api
app_name = 'perms'
router = routers.DefaultRouter()
router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
urlpatterns = [
url(r'^v1/user/my/assets/$', api.MyGrantedAssetsApi.as_view(), name='my-assets'),
url(r'^v1/user/my/asset-groups/$', api.MyGrantedAssetsGroupsApi.as_view(), name='my-asset-groups'),
url(r'^v1/user/my/asset-group/(?P<pk>[0-9]+)/assets/$', api.MyAssetGroupAssetsApi.as_view(),
name='user-my-asset-group-assets'),
# Select user permission of asset and asset group
url(r'^v1/user/(?P<pk>[0-9]+)/assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
url(r'^v1/user/(?P<pk>[0-9]+)/asset-groups/$', api.UserGrantedAssetGroupsApi.as_view(),
name='user-asset-groups'),
# Select user group permission of asset and asset group
url(r'^v1/user-group/(?P<pk>[0-9]+)/assets/$', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
url(r'^v1/user-group/(?P<pk>[0-9]+)/asset-groups/$', api.UserGroupGrantedAssetGroupsApi.as_view(),
name='user-group-asset-groups'),
# Revoke permission api
url(r'^v1/asset-permissions/user/revoke/', api.RevokeUserAssetPermission.as_view(),
name='revoke-user-asset-permission'),
url(r'^v1/asset-permissions/user-group/revoke/', api.RevokeUserGroupAssetPermission.as_view(),
name='revoke-user-group-asset-permission'),
]
urlpatterns += router.urls
# coding:utf-8
from django.conf.urls import url
import views
import api
from .. import views
app_name = 'perms'
......@@ -21,14 +20,4 @@ urlpatterns = [
name='asset-permission-asset-list'),
]
urlpatterns += [
url(r'^v1/asset-permission/$', api.AssetPermissionListCreateApi.as_view(),
name='asset-permission-list-create-api'),
url(r'^v1/user/assets/$', api.UserAssetsApi.as_view(),
name='user-assets'),
url(r'^v1/user/asset-groups/$', api.UserAssetsGroupsApi.as_view(),
name='user-asset-groups'),
url(r'^v1/user/asset-groups/(?P<pk>[0-9]+)/assets/$', api.UserAssetsGroupAssetsApi.as_view(),
name='user-asset-groups-assets'),
]
from __future__ import absolute_import, unicode_literals
from common.utils import setattr_bulk
from .hands import User, UserGroup, Asset, AssetGroup, SystemUser
......@@ -7,7 +8,7 @@ def get_user_group_granted_asset_groups(user_group):
"""Return asset groups granted of the user group
:param user_group: Instance of :class: ``UserGroup``
:return: {asset1: {system_user1, }, asset1: {system_user1, system_user2]}
:return: {asset_group1: {system_user1, }, asset_group2: {system_user1, system_user2}}
"""
asset_groups = {}
asset_permissions = user_group.asset_permissions.all()
......@@ -20,7 +21,6 @@ def get_user_group_granted_asset_groups(user_group):
asset_groups[asset_group] |= set(asset_permission.system_users.all())
else:
asset_groups[asset_group] = set(asset_permission.system_users.all())
return asset_groups
......@@ -41,7 +41,6 @@ def get_user_group_granted_assets(user_group):
assets[asset] |= set(asset_permission.system_users.all())
else:
assets[asset] = set(asset_permission.system_users.all())
return assets
......@@ -61,7 +60,7 @@ def get_user_granted_asset_groups_direct(user):
if asset_group in asset_groups:
asset_groups[asset_group] |= set(asset_permission.system_users.all())
else:
setattr(asset_group, 'is_inherit_from_user_group', False)
setattr(asset_group, 'inherited', False)
asset_groups[asset_group] = set(asset_permission.system_users.all())
return asset_groups
......@@ -89,7 +88,7 @@ def get_user_granted_asset_groups_inherit_from_user_groups(user):
if asset_group in asset_groups:
asset_groups[asset_group] |= set(asset_permission.system_users.all())
else:
setattr(asset_group, 'is_inherit_from_user_group', True)
setattr(asset_group, 'inherited', True)
asset_groups[asset_group] = set(asset_permission.system_users.all())
return asset_groups
......@@ -112,7 +111,6 @@ def get_user_granted_asset_groups(user):
asset_groups[asset_group] |= asset_groups_direct[asset_group]
else:
asset_groups[asset_group] = asset_groups_direct[asset_group]
return asset_groups
......@@ -132,10 +130,8 @@ def get_user_granted_assets_direct(user):
if asset in assets:
assets[asset] |= set(asset_permission.system_users.all())
else:
setattr(asset, 'is_inherit_from_user_groups', False)
setattr(asset, 'is_inherit_from_user_groups', False)
setattr(asset, 'inherited', False)
assets[asset] = set(asset_permission.system_users.all())
return assets
......@@ -154,7 +150,7 @@ def get_user_granted_assets_inherit_from_user_groups(user):
if asset in assets:
assets[asset] |= assets_inherited[asset]
else:
setattr(asset, 'is_inherit_from_user_groups', True)
setattr(asset, 'inherited', True)
assets[asset] = assets_inherited[asset]
return assets
......@@ -175,10 +171,25 @@ def get_user_granted_assets(user):
assets[asset] |= assets_direct[asset]
else:
assets[asset] = assets_direct[asset]
return assets
def get_user_group_asset_permissions(user_group):
permissions = user_group.asset_permissions.all()
return permissions
def get_user_asset_permissions(user):
user_group_permissions = set()
direct_permissions = set(setattr_bulk(user.asset_permissions.all(), 'inherited', 0))
for user_group in user.groups.all():
permissions = get_user_group_asset_permissions(user_group)
user_group_permissions |= set(permissions)
user_group_permissions = set(setattr_bulk(user_group_permissions, 'inherited', 1))
return direct_permissions | user_group_permissions
def get_user_groups_granted_in_asset(asset):
pass
......
......@@ -255,3 +255,12 @@ table.dataTable tbody td.selected td i.text-navy
font-size: 12px;
vertical-align: middle;
}
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: right !important;
}
div.dataTables_wrapper div.dataTables_filter {
margin-left: 15px;
}
\ No newline at end of file
......@@ -1585,9 +1585,9 @@ table.dataTable thead .sorting_desc_disabled {
.dataTables_wrapper {
padding-bottom: 30px;
}
.dataTables_length {
float: left;
}
/*.dataTables_length {*/
/*float: left;*/
/*}*/
.dataTables_filter label {
margin-right: 5px;
}
......
......@@ -189,9 +189,8 @@ function activeNav() {
function APIUpdateAttr(props) {
// props = {url: .., body: , success: , error: , method: ,}
props = props || {};
success_message = props.success_message || 'Update Successfully!';
fail_message = props.fail_message || 'Error occurred while updating.';
var success_message = props.success_message || 'Update Successfully!';
var fail_message = props.fail_message || 'Error occurred while updating.';
$.ajax({
url: props.url,
type: props.method || "PATCH",
......@@ -199,19 +198,18 @@ function APIUpdateAttr(props) {
contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json"
}).done(function(data, textStatue, jqXHR) {
toastr.success(success_message);
if (typeof props.success === 'function') {
return props.success(data);
} else {
toastr.success(success_message);
}
}
}).fail(function(jqXHR, textStatue, errorThrown) {
toastr.error(fail_message);
if (typeof props.error === 'function') {
return props.error(errorThrown);
} else {
toastr.error(fail_message);
}
}
});
return true;
// return true;
}
// Sweet Alert for Delete
......@@ -267,6 +265,7 @@ $.fn.serializeObject = function()
};
var jumpserver = {};
jumpserver.checked = false;
jumpserver.selected = {};
jumpserver.initDataTable = function (options) {
// options = {
// ele *: $('#dataTable_id'),
......@@ -285,59 +284,37 @@ jumpserver.initDataTable = function (options) {
{
targets: 0,
orderable: false,
createdCell: function(td) {
$(td).html('<input type="checkbox" class="ipt_check">');
}
},
createdCell: function(td, cellData) {
$(td).html('<input type="checkbox" class="text-center ipt_check" id=99991937>'.replace('99991937', cellData));
}},
{className: 'text-center', targets: '_all'}
];
columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs;
var table = ele.DataTable({
pageLength: options.pageLength || 25,
pageLength: options.pageLength || 15,
dom: options.dom || '<"#uc.pull-left"><"html5buttons"B>flti<"row m-t"<"#op.col-md-6"><"col-md-6"p>>',
language: {
url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json"
},
order: options.order || [[ 1, 'asc' ]],
buttons: options.buttons || [
{extend: 'excel',
exportOptions: {
modifier: {
selected: true
}
}
},
{extend: 'pdf',
exportOptions: {
modifier: {
selected: true
}
}
},
{extend: 'print',
customize: function (win){
$(win.document.body).addClass('white-bg');
$(win.document.body).css('font-size', '10px');
$(win.document.body).find('table')
.addClass('compact')
.css('font-size', 'inherit');
}
}
],
select: options.select || 'multi',
buttons: [],
columnDefs: columnDefs,
select: options.select || {style: 'multi'},
ajax: {
url: options.ajax_url ,
dataSrc: ""
},
columns: options.columns || []
columns: options.columns || [],
lengthMenu: [[15, 25, 50, -1], [15, 25, 50, "All"]]
});
table.on('select', function(e, dt, type, indexes) {
var $node = table[ type ]( indexes ).nodes().to$();
$node.find('input.ipt_check').prop('checked', true);
jumpserver.selected[$node.find('input.ipt_check').prop('id')] = true
}).on('deselect', function(e, dt, type, indexes) {
var $node = table[ type ]( indexes ).nodes().to$();
$node.find('input.ipt_check').prop('checked', false);
jumpserver.selected[$node.find('input.ipt_check').prop('id')] = false
}).on('draw', function(){
$('#op').html(options.op_html || '');
$('#uc').html(options.uc_html || '');
......@@ -353,5 +330,6 @@ jumpserver.initDataTable = function (options) {
table.rows().deselect();
}
});
return table;
};
......@@ -28,10 +28,17 @@
</div>
</div>
<div class="ibox-content">
{% block form %} {% endblock %}
{% if form.errors.all %}
<div class="alert alert-danger" style="margin: 20px auto 0px">
{{ form.errors.all }}
</div>
{% endif %}
{% block form %}
{% endblock %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
<div class="footer fixed">
<div class="pull-right">
Version <strong>0.3.3</strong> GPL.
Version <strong>0.4.0</strong> GPL.
</div>
<div>
<strong>Copyright</strong> Jumpserver.org Team &copy; 2014-2016
......
......@@ -48,10 +48,10 @@
<li id="command-log">
<a href="{% url 'audits:command-log-list' %}">{% trans 'Command log' %}</a>
</li>
<li id="login">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Login log' %}</a>
<li id="login-log">
<a href="{% url 'audits:login-log-list' %}">{% trans 'Login log' %}</a>
</li>
<li id="admin">
<li id="admin-log">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Admin log' %}</a>
</li>
......
# -*- coding: utf-8 -*-
#
from django.core.cache import cache
from django.conf import settings
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework import viewsets
from rest_framework.views import APIView, Response
from rest_framework.permissions import AllowAny
......@@ -11,42 +14,51 @@ from .serializers import TerminalSerializer, TerminalHeatbeatSerializer
from .hands import IsSuperUserOrTerminalUser
class TerminalCreateListApi(ListCreateAPIView):
class TerminalViewSet(viewsets.ModelViewSet):
queryset = Terminal.objects.all()
serializer_class = TerminalSerializer
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
def create(self, request, *args, **kwargs):
name = signer.unsign(request.data.get('name', ''))
if name:
terminal = get_object_or_none(Terminal, name=name)
if terminal:
data = {
'data': {'name': name, 'id': terminal.id},
}
if terminal.is_active:
return Response(data={'data': {'name': name, 'id': terminal.id},
'msg': 'Success'},
status=200)
data['msg'] = 'Success'
return Response(data=data, status=200)
else:
return Response(data={'data': {'name': name, 'ip': terminal.ip},
'msg': 'Need admin active it'},
status=203)
data['msg'] = 'Need admin active this terminal'
return Response(data=data, status=203)
else:
ip = request.META.get('X-Real-IP') or request.META.get('REMOTE_ADDR')
terminal = Terminal.objects.create(name=name, ip=ip)
return Response(data={'data': {'name': name, 'ip': terminal.ip},
'msg': 'Need admin active it'},
status=201)
data = {
'data': {'name': name, 'id': terminal.id},
'msg': 'Need admin active this terminal',
}
return Response(data=data, status=201)
else:
return Response(data={'msg': 'Secrete key invalid'}, status=401)
class TerminalHeatbeatApi(ListCreateAPIView):
model = TerminalHeatbeat
queryset = TerminalHeatbeat.objects.all()
serializer_class = TerminalHeatbeatSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
class TerminalApiDetailUpdateDetailApi(RetrieveUpdateDestroyAPIView):
queryset = Terminal.objects.all()
serializer_class = TerminalSerializer
class TerminalHeatbeatViewSet(viewsets.ModelViewSet):
queryset = TerminalHeatbeat.objects.all()
serializer_class = TerminalHeatbeatSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
def create(self, request, *args, **kwargs):
terminal = request.user
TerminalHeatbeat.objects.create(terminal=terminal)
return Response({'msg': 'Success'})
# -*- coding: utf-8 -*-
#
from django.utils import timezone
from rest_framework import serializers
from .models import Terminal, TerminalHeatbeat
......@@ -8,22 +9,30 @@ from .hands import ProxyLog
class TerminalSerializer(serializers.ModelSerializer):
proxy_amount = serializers.SerializerMethodField()
proxy_online = serializers.SerializerMethodField()
is_alive = serializers.SerializerMethodField()
class Meta:
model = Terminal
fields = ['id', 'name', 'ip', 'type', 'url', 'comment', 'is_active',
'get_type_display', 'proxy_amount']
fields = ['id', 'name', 'ip', 'type', 'url', 'comment',
'is_active', 'get_type_display', 'proxy_online', 'is_alive']
@staticmethod
def get_proxy_amount(obj):
def get_proxy_online(obj):
return ProxyLog.objects.filter(terminal=obj.name, is_finished=False).count()
@staticmethod
def get_is_alive(obj):
log = obj.terminalheatbeat_set.last()
if timezone.now() - log.date_created > timezone.timedelta(seconds=600):
return False
else:
return True
class TerminalHeatbeatSerializer(serializers.ModelSerializer):
class Meta:
model = TerminalHeatbeat
fields = ['terminal']
if __name__ == '__main__':
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
......@@ -27,8 +27,9 @@
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'proxy_amount' %}</th>
<th class="text-center">{% trans 'proxy online' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Alive' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
......@@ -42,6 +43,7 @@
$(document).ready(function(){
var options = {
ele: $('#terminal_list_table'),
buttons: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "users:user-detail" pk=99991937 %}">' + cellData + '</a>';
......@@ -54,7 +56,14 @@ $(document).ready(function(){
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
{targets: 6, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
console.log(rowData.name);
var update_btn = '<a href="{% url "terminal:terminal-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.replace('99991937', cellData);
......@@ -64,9 +73,9 @@ $(document).ready(function(){
$(td).html(update_btn + delete_btn)
}}
],
ajax_url: '{% url "terminal:terminal-list-create-api" %}',
ajax_url: '{% url "api-terminal:terminal-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "ip" }, {data: "get_type_display" },
{data: "proxy_amount"}, {data: "is_active" }, {data: "id"}],
{data: "proxy_online"}, {data: "is_active" }, {data: 'is_active'}, {data: "id"}],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
......@@ -74,7 +83,7 @@ $(document).ready(function(){
var $this = $(this);
var uid = $this.data('uid');
var name = $(this).data('name');
var the_url = '{% url "terminal:terminal-detail-update-delete-api" pk=99991937 %}'.replace('99991937', uid);
var the_url = '{% url "api-terminal:terminal-detail" pk=99991937 %}'.replace('99991937', uid);
objectDelete($this, name, the_url)
})
</script>
......
......@@ -3,6 +3,7 @@
#
from django.conf.urls import url
from rest_framework import routers
import views
import api
......@@ -14,9 +15,11 @@ urlpatterns = [
url(r'^terminal/(?P<pk>\d+)/update$', views.TerminalUpdateView.as_view(), name='terminal-update'),
]
urlpatterns += [
url(r'^v1/terminal/$', api.TerminalCreateListApi.as_view(), name='terminal-list-create-api'),
url(r'^v1/terminal/(?P<pk>\d+)/$', api.TerminalApiDetailUpdateDetailApi.as_view(),
name='terminal-detail-update-delete-api'),
url(r'^v1/terminal-heatbeat/$', api.TerminalHeatbeatApi.as_view(), name='terminal-heatbeat-api'),
]
router = routers.DefaultRouter()
router.register(r'v1/terminal/heatbeat', api.TerminalHeatbeatViewSet, 'terminal-heatbeat')
router.register(r'v1/terminal', api.TerminalViewSet, 'terminal')
# urlpatterns += [
# url(r'v1/terminal/heatbeat/', api.TerminalHeatbeatApi.as_view(), name='api-terminal-heatbeat')
# ]
urlpatterns += router.urls
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from rest_framework import routers
from .. import api
app_name = 'terminal'
router = routers.DefaultRouter()
router.register(r'v1/terminal/heatbeat', api.TerminalHeatbeatViewSet, 'terminal-heatbeat')
router.register(r'v1/terminal', api.TerminalViewSet, 'terminal')
urlpatterns = router.urls
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from django.conf.urls import url
from .. import views
app_name = 'terminal'
urlpatterns = [
url(r'^terminal$', views.TerminalListView.as_view(), name='terminal-list'),
url(r'^terminal/(?P<pk>\d+)/update$', views.TerminalUpdateView.as_view(), name='terminal-update'),
]
......@@ -6,120 +6,130 @@ import base64
from django.shortcuts import get_object_or_404
from django.core.cache import cache
from django.conf import settings
from rest_framework import generics, status
from rest_framework import generics, status, viewsets
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView, BulkModelViewSet
from rest_framework import authentication
from common.mixins import BulkDeleteApiMixin
from common.utils import get_logger
from .utils import check_user_valid, token_gen
from .models import User, UserGroup
from .serializers import UserDetailSerializer, UserAndGroupSerializer, \
GroupDetailSerializer, UserPKUpdateSerializer, UserBulkUpdateSerializer, GroupBulkUpdateSerializer
from .hands import write_login_log_async
from .backends import IsSuperUser, IsTerminalUser, IsValidUser, IsSuperUserOrTerminalUser
from . import serializers
logger = get_logger(__name__)
class UserDetailApi(generics.RetrieveUpdateDestroyAPIView):
class UserViewSet(BulkModelViewSet):
queryset = User.objects.all()
serializer_class = UserDetailSerializer
serializer_class = serializers.UserSerializer
permission_classes = (IsSuperUser,)
class UserAndGroupEditApi(generics.RetrieveUpdateAPIView):
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserAndGroupSerializer
serializer_class = serializers.UserUpdateGroupSerializer
permission_classes = (IsSuperUser,)
class UserResetPasswordApi(generics.UpdateAPIView):
queryset = User.objects.all()
serializer_class = UserDetailSerializer
serializer_class = serializers.UserSerializer
def perform_update(self, serializer):
# Note: we are not updating the user object here.
# We just do the reset-password staff.
user = self.get_object()
import uuid
from .utils import send_reset_password_mail
user = self.get_object()
user.password_raw = str(uuid.uuid4())
user.save()
from .utils import send_reset_password_mail
send_reset_password_mail(user)
class UserResetPKApi(generics.UpdateAPIView):
queryset = User.objects.all()
serializer_class = UserDetailSerializer
serializer_class = serializers.UserSerializer
def perform_update(self, serializer):
from .utils import send_reset_ssh_key_mail
user = self.get_object()
user.is_public_key_valid = False
user.save()
from .utils import send_reset_ssh_key_mail
send_reset_ssh_key_mail(user)
class UserUpdatePKApi(generics.UpdateAPIView):
queryset = User.objects.all()
serializer_class = UserPKUpdateSerializer
serializer_class = serializers.UserPKUpdateSerializer
def perform_update(self, serializer):
user = self.get_object()
user.private_key = serializer.validated_data['_public_key']
user.public_key = serializer.validated_data['_public_key']
user.save()
class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView):
queryset = UserGroup.objects.all()
serializer_class = GroupDetailSerializer
def perform_update(self, serializer):
users = serializer.validated_data.get('users')
if users:
group = self.get_object()
# Note: use `list` method to force hitting the db.
group_users = list(group.users.all())
serializer.save()
group.users.set(users + group_users)
group.save()
return
serializer.save()
class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserBulkUpdateSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
# def get(self, request, *args, **kwargs):
# return super(UserListUpdateApi, self).get(request, *args, **kwargs)
class GroupListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
class UserGroupViewSet(viewsets.ModelViewSet):
queryset = UserGroup.objects.all()
serializer_class = GroupBulkUpdateSerializer
serializer_class = serializers.UserGroupSerializer
class DeleteUserFromGroupApi(generics.DestroyAPIView):
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
queryset = UserGroup.objects.all()
serializer_class = GroupDetailSerializer
def destroy(self, request, *args, **kwargs):
group = self.get_object()
self.perform_destroy(group, **kwargs)
return Response(status=status.HTTP_204_NO_CONTENT)
serializer_class = serializers.UserGroupUpdateMemeberSerializer
permission_classes = (IsSuperUser,)
def perform_destroy(self, instance, **kwargs):
user_id = kwargs.get('uid')
user = get_object_or_404(User, id=user_id)
instance.users.remove(user)
# class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView):
# queryset = UserGroup.objects.all()
# serializer_class = serializers.GroupDetailSerializer
#
# def perform_update(self, serializer):
# users = serializer.validated_data.get('users')
# if users:
# group = self.get_object()
# Note: use `list` method to force hitting the db.
# group_users = list(group.users.all())
# serializer.save()
# group.users.set(users + group_users)
# group.save()
# return
# serializer.save()
# class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
# queryset = User.objects.all()
# serializer_class = serializers.UserBulkUpdateSerializer
# permission_classes = (IsSuperUserOrTerminalUser,)
#
# def get(self, request, *args, **kwargs):
# return super(UserListUpdateApi, self).get(request, *args, **kwargs)
#
# class GroupListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
# queryset = UserGroup.objects.all()
# serializer_class = serializers.GroupBulkUpdateSerializer
#
class UserTokenApi(APIView):
# class DeleteUserFromGroupApi(generics.DestroyAPIView):
# queryset = UserGroup.objects.all()
# serializer_class = serializers.GroupDetailSerializer
#
# def destroy(self, request, *args, **kwargs):
# group = self.get_object()
# self.perform_destroy(group, **kwargs)
# return Response(status=status.HTTP_204_NO_CONTENT)
#
# def perform_destroy(self, instance, **kwargs):
# user_id = kwargs.get('uid')
# user = get_object_or_404(User, id=user_id)
# instance.users.remove(user)
#
#
class UserAuthApi(APIView):
permission_classes = ()
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
......@@ -127,10 +137,11 @@ class UserTokenApi(APIView):
username = request.data.get('username', '')
password = request.data.get('password', '')
public_key = request.data.get('public_key', '')
remote_addr = request.META.get('REMOTE_ADDR', '')
remote_addr = base64.b64encode(remote_addr).replace('=', '')
remote_addr = request.data.get('remote_addr', '')
terminal = request.data.get('terminal', '')
login_type = request.data.get('login_type', 'T')
user = check_user_valid(username=username, password=password, public_key=public_key)
if user:
token = cache.get('%s_%s' % (user.id, remote_addr))
if not token:
......@@ -138,6 +149,8 @@ class UserTokenApi(APIView):
cache.set(token, user.id, self.expiration)
cache.set('%s_%s' % (user.id, remote_addr), token, self.expiration)
write_login_log_async.delay(user.username, name=user.name, terminal=terminal,
login_ip=remote_addr, login_type=login_type)
return Response({'token': token, 'id': user.id, 'username': user.username, 'name': user.name})
else:
return Response({'msg': 'Invalid password or public key or user is not active or expired'})
return Response({'msg': 'Invalid password or public key or user is not active or expired'}, status=401)
......@@ -78,6 +78,7 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
def authenticate_credentials(self, token, request):
user_id = cache.get(token)
print('Auth id: %s' % user_id)
user = get_object_or_none(User, id=user_id)
if not user:
......@@ -87,7 +88,6 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
remote_addr = base64.b16encode(remote_addr).replace('=', '')
cache.set(token, user_id, self.expiration)
cache.set('%s_%s' % (user.id, remote_addr), token, self.expiration)
return user, None
......
......@@ -5,8 +5,9 @@ from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import gettext_lazy as _
from captcha.fields import CaptchaField
from common.utils import validate_ssh_public_key
from perms.models import AssetPermission
from .models import User, UserGroup
from .hands import AssetPermission
class UserLoginForm(AuthenticationForm):
......@@ -17,8 +18,7 @@ class UserLoginForm(AuthenticationForm):
captcha = CaptchaField()
class UserCreateForm(forms.ModelForm):
class UserCreateUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = [
......@@ -36,32 +36,30 @@ class UserCreateForm(forms.ModelForm):
class UserBulkImportForm(forms.ModelForm):
class Meta:
model = User
fields = ['username', 'email', 'enable_otp', 'role']
class UserUpdateForm(forms.ModelForm):
class Meta:
model = User
fields = [
'name', 'email', 'groups', 'wechat',
'phone', 'enable_otp', 'role', 'date_expired', 'comment',
]
help_texts = {
'username': '* required',
'email': '* required',
'groups': '* required'
}
widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}),
}
# class UserUpdateForm(forms.ModelForm):
#
# class Meta:
# model = User
# fields = [
# 'name', 'email', 'groups', 'wechat',
# 'phone', 'enable_otp', 'role', 'date_expired', 'comment',
# ]
# help_texts = {
# 'username': '* required',
# 'email': '* required',
# 'groups': '* required'
# }
# widgets = {
# 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}),
# }
class UserGroupForm(forms.ModelForm):
class Meta:
model = UserGroup
fields = [
......@@ -84,22 +82,14 @@ class UserKeyForm(forms.Form):
public_key = forms.CharField(
label=_('ssh public key'), max_length=5000,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}),
help_text=_('Paste your id_ras.pub here.'))
help_text=_('Paste your id_rsa.pub here.'))
def clean_public_key(self):
public_key = self.cleaned_data['public_key']
if self.user._public_key and public_key == self.user.public_key:
if self.user.public_key and public_key == self.user.public_key:
raise forms.ValidationError(_('Public key should not be the same as your old one.'))
from sshpubkeys import SSHKey
from sshpubkeys.exceptions import InvalidKeyException
ssh = SSHKey(public_key)
try:
ssh.parse()
except InvalidKeyException as e:
print e
raise forms.ValidationError(_('Not a valid ssh public key'))
except NotImplementedError as e:
print e
if not validate_ssh_public_key(public_key):
raise forms.ValidationError(_('Not a valid ssh public key'))
return public_key
......@@ -108,7 +98,6 @@ class UserPrivateAssetPermissionForm(forms.ModelForm):
def save(self, commit=True):
self.instance = super(UserPrivateAssetPermissionForm, self).save(commit=commit)
# self.instance.private_for = 'U'
self.instance.users = [self.user]
self.instance.save()
return self.instance
......@@ -126,3 +115,30 @@ class UserPrivateAssetPermissionForm(forms.ModelForm):
'system_users': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select system users')}),
}
class UserGroupPrivateAssetPermissionForm(forms.ModelForm):
def save(self, commit=True):
self.instance = super(UserGroupPrivateAssetPermissionForm, self).save(commit=commit)
self.instance.user_groups = [self.user_group]
self.instance.save()
return self.instance
class Meta:
model = AssetPermission
fields = [
'assets', 'asset_groups', 'system_users', 'name',
]
widgets = {
'assets': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select assets')}),
'asset_groups': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select asset groups')}),
'system_users': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select system users')}),
}
class FileForm(forms.Form):
excel = forms.FileField()
......@@ -11,5 +11,6 @@
"""
from terminal.models import Terminal
from perms.models import AssetPermission
from perms.utils import get_user_granted_assets, get_user_granted_asset_groups
from audits.tasks import write_login_log_async
# from perms.models import AssetPermission
# from perms.utils import get_user_granted_assets, get_user_granted_asset_groups
......@@ -3,6 +3,7 @@
from __future__ import unicode_literals
from django.conf import settings
from django.contrib.auth import logout
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import AbstractUser
from django.core import signing
......@@ -234,7 +235,7 @@ class User(AbstractUser):
user.groups.add(UserGroup.initial())
def delete(self):
if self.pk == 1:
if self.pk == 1 or self.username == 'admin':
return
return super(User, self).delete()
......
......@@ -5,14 +5,37 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
from common.utils import signer
from common.utils import signer, validate_ssh_public_key
from .models import User, UserGroup
class UserDetailSerializer(serializers.ModelSerializer):
# class UserDetailSerializer(BulkSerializerMixin, serializers.ModelSerializer):
# class Meta:
# model = User
# fields = ['avatar', 'wechat', 'phone', 'enable_otp', 'comment', 'is_active', 'name']
class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
groups_display = serializers.SerializerMethodField()
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all())
class Meta:
model = User
fields = ['avatar', 'wechat', 'phone', 'enable_otp', 'comment', 'is_active', 'name']
list_serializer_class = BulkListSerializer
exclude = ['first_name', 'last_name', 'password', '_private_key', '_public_key']
def get_field_names(self, declared_fields, info):
fields = super(UserSerializer, self).get_field_names(declared_fields, info)
fields.extend(['groups_display', 'get_role_display', 'is_valid'])
return fields
@staticmethod
def get_groups_display(obj):
return " ".join([group.name for group in obj.groups.all()])
# @staticmethod
# def get_active_display(obj):
# return not (obj.is_expired and obj.is_active)
class UserPKUpdateSerializer(serializers.ModelSerializer):
......@@ -22,21 +45,14 @@ class UserPKUpdateSerializer(serializers.ModelSerializer):
@staticmethod
def validate__public_key(value):
from sshpubkeys import SSHKey
from sshpubkeys.exceptions import InvalidKeyException
ssh = SSHKey(value)
try:
ssh.parse()
except InvalidKeyException as e:
print e
raise serializers.ValidationError(_('Not a valid ssh public key'))
except NotImplementedError as e:
print e
if not validate_ssh_public_key(value):
print('Not a valid key')
print(value)
raise serializers.ValidationError(_('Not a valid ssh public key'))
return value
class UserAndGroupSerializer(serializers.ModelSerializer):
class UserUpdateGroupSerializer(serializers.ModelSerializer):
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all())
class Meta:
......@@ -44,43 +60,63 @@ class UserAndGroupSerializer(serializers.ModelSerializer):
fields = ['id', 'groups']
class GroupDetailSerializer(serializers.ModelSerializer):
class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
user_amount = serializers.SerializerMethodField()
class Meta:
model = UserGroup
fields = ['id', 'name', 'comment', 'date_created', 'created_by', 'users']
class UserBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer):
group_display = serializers.SerializerMethodField()
active_display = serializers.SerializerMethodField()
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all())
class Meta(object):
model = User
list_serializer_class = BulkListSerializer
fields = ['id', 'is_active', 'username', 'name', 'email', 'role', 'avatar',
'enable_otp', 'comment', 'groups', 'get_role_display',
'group_display', 'active_display']
@staticmethod
def get_group_display(obj):
return " ".join([group.name for group in obj.groups.all()])
@staticmethod
def get_active_display(obj):
# TODO: user active state
return not (obj.is_expired and obj.is_active)
def get_user_amount(obj):
return obj.users.count()
class GroupBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer):
user_amount = serializers.SerializerMethodField()
class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer):
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all())
class Meta:
model = UserGroup
list_serializer_class = BulkListSerializer
fields = ['id', 'name', 'comment', 'user_amount']
fields = ['id', 'users']
@staticmethod
def get_user_amount(obj):
return obj.users.count()
# class GroupDetailSerializer(serializers.ModelSerializer):
# class Meta:
# model = UserGroup
# fields = ['id', 'name', 'comment', 'date_created', 'created_by', 'users']
# class UserBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer):
# group_display = serializers.SerializerMethodField()
# active_display = serializers.SerializerMethodField()
# groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all())
#
# class Meta(object):
# model = User
# list_serializer_class = BulkListSerializer
# fields = ['id', 'is_active', 'username', 'name', 'email', 'role', 'avatar',
# 'enable_otp', 'comment', 'groups', 'get_role_display',
# 'group_display', 'active_display']
#
# @staticmethod
# def get_group_display(obj):
# return " ".join([group.name for group in obj.groups.all()])
#
# @staticmethod
# def get_active_display(obj):
# TODO: user active state
# return not (obj.is_expired and obj.is_active)
#
#
# class GroupBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer):
# user_amount = serializers.SerializerMethodField()
#
# class Meta:
# model = UserGroup
# list_serializer_class = BulkListSerializer
# fields = ['id', 'name', 'comment', 'user_amount']
#
# @staticmethod
# def get_user_amount(obj):
# return obj.users.count()
#
{% extends 'base.html' %}
{% extends '_base_create_update.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.name|bootstrap_horizontal }}
{{ form.email|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
{% block form %}
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{{ form.username|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.email|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<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>
<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>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
......
......@@ -17,34 +17,21 @@
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'users:user-detail' pk=user_object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
<a href="{% url 'users:user-detail' pk=user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
</li>
<li class="active">
<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>
<a href="{% url 'users:user-asset-permission' pk=user.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>
<a href="{% url 'users:user-granted-asset' pk=user.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>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
<div class="input-group-btn">
<button id="search_btn" type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
</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 permission of ' %} <b>{{ user_object.name }}</b></span>
<span style="float: left">{% trans 'Asset permission of ' %} <b>{{ user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......@@ -60,52 +47,23 @@
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<table class="table table-hover " id="user_permissions_table" >
<thead>
<tr>
<th class="text-center"><a href="{% url 'perms:asset-permission-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'User ' %}</th>
<th class="text-center">{% trans 'User group ' %}</th>
<th class="text-center">{% trans 'Asset ' %}</th>
<th class="text-center">{% trans 'Asset group ' %}</th>
<th class="text-center">{% trans 'System user ' %}</th>
<th class="text-center">
<a href="#">{% trans 'Is valid' %}</a>
</th>
<th></th>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Asset' %}</th>
<th>{% trans 'Asset group' %}</th>
<th>{% trans 'System user' %}</th>
<th>{% trans 'Valid' %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for asset_permission in object_list %}
<tr class="gradeX">
<td class="text-center">
<a href="{% url 'perms:asset-permission-detail' pk=asset_permission.id %}">
{{ asset_permission.name }}
</a>
</td>
<td class="text-center">{{ asset_permission.users.count}}</td>
<td class="text-center">{{ asset_permission.user_groups.count}}</td>
<td class="text-center">{{ asset_permission.assets.count }}</td>
<td class="text-center">{{ asset_permission.asset_groups.count }}</td>
<td class="text-center">{{ asset_permission.system_users.count }}</td>
<td class="text-center">
{% if asset_permission.is_valid %}
<i class="fa fa-check text-navy"></i>
{% else %}
<i class="fa fa-times text-danger"></i>
{% endif %}
</td>
<td>
<button class="btn btn-danger btn-xs btn_delete_user_group {% if asset_permission.is_inherit_from_user_groups %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="row">
{% include '_pagination.html' %}
</div>
</div>
</div>
</div>
......@@ -115,7 +73,7 @@
<i class="fa fa-info-circle"></i> {% trans 'Quick create permission for user' %}
</div>
<div class="panel-body">
<form method="post" action="{% url 'users:user-asset-permission-create' pk=user_object.id %}">
<form method="post" action="{% url 'users:user-asset-permission-create' pk=user.id %}">
<table class="table">
<tbody>
{% csrf_token %}
......@@ -159,25 +117,69 @@
{% 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_object.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
});
var options = {
ele: $('#user_permissions_table'),
buttons: [],
order: [],
select: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "perms:asset-permission-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData, rowData) {
var dataLength = cellData.length;
$(td).html(dataLength);
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var dataLength = cellData.length;
$(td).html(dataLength);
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var dataLength = cellData.length;
$(td).html(dataLength);
}},
{targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var btn = '<button class="btn btn-danger btn-xs btn_del_permission disabled" id=99991937 type="button" style="float: right;"><i class="fa fa-minus"></i></button>';
if (rowData.is_inherited) {
$(td).html(btn)
} else {
btn = btn.replace('99991937', cellData);
$(td).html(btn.replace('disabled', ''));
}
}}
],
ajax_url: '{% url "api-perms:asset-permission-list" %}?user={{ user.id }}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets" }, {data: "asset_groups"},
{data: "system_users"}, {data: "is_active"}, {data: "id"}]
};
jumpserver.initDataTable(options);
}).on('click', '.btn_del_permission', function () {
var $this = $(this);
var body = {
id: $this.attr('id'),
user_id: {{ user.id }}
};
var the_url = "{% url 'perms:revoke-user-asset-permission' %}";
var success = function () {
$this.closest('tr').remove();
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'PUT',
success_message: '{% trans "Revoke Successfully!" %}',
success: success
});
})
</script>
{% endblock %}
\ No newline at end of file
......@@ -2,9 +2,9 @@
{% load i18n %}
{% load bootstrap %}
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
{% block username %}
{{ form.username|bootstrap_horizontal }}
{% endblock %}
{#{% block username %}#}
{# {{ form.username|bootstrap_horizontal }}#}
{#{% endblock %}#}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
......
......@@ -18,20 +18,24 @@
<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>
<a href="{% url 'users:user-detail' pk=user.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>
<a href="{% url 'users:user-asset-permission' pk=user.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.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user.id %}"><i class="fa fa-edit"></i>Update</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>
<span class="label"><b>{{ user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......@@ -51,56 +55,56 @@
<tbody>
<tr class="no-borders-tr">
<td colspan="2">
<img src="{{ user_object | user_avatar_url }}" class="img-circle" width="64" height="64">
<img src="{{ user|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>
<td><b>{{ user.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Username' %}:</td>
<td><b>{{ user_object.username }}</b></td>
<td><b>{{ user.username }}</b></td>
</tr>
<tr>
<td>{% trans 'Email' %}:</td>
<td><b>{{ user_object.email }}</b></td>
<td><b>{{ user.email }}</b></td>
</tr>
{% if user_object.phone %}
{% if user.phone %}
<tr>
<td>{% trans 'Phone' %}:</td>
<td><b>{{ user_object.phone }}</b></td>
<td><b>{{ user.phone }}</b></td>
</tr>
{% endif %}
{% if user_object.wechat %}
{% if user.wechat %}
<tr>
<td>{% trans 'Wechat' %}:</td>
<td><b>{{ user_object.wechat }}</b></td>
<td><b>{{ user.wechat }}</b></td>
</tr>
{% endif %}
<tr>
<td>{% trans 'Role' %}:</td>
<td><b>{{ user_object.get_role_display }}</b></td>
<td><b>{{ user.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>
<td><b>{{ user.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>
<td><b>{{ user.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>
<td><b>{{ user.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>
<td><b>{{ user.last_login|date:"Y-m-j H:i:s" }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ user_object.comment }}</b></td>
<td><b>{{ user.comment }}</b></td>
</tr>
</tbody>
</table>
......@@ -120,7 +124,7 @@
<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">
<input type="checkbox" {% if user.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>
......@@ -134,7 +138,7 @@
<td><span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" class="onoffswitch-checkbox" {% if user_object.enable_otp %} checked {% endif %}
<input type="checkbox" class="onoffswitch-checkbox" {% if user.enable_otp %} checked {% endif %}
id="enable_otp">
<label class="onoffswitch-label" for="enable_otp">
<span class="onoffswitch-inner"></span>
......@@ -183,7 +187,7 @@
<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">
<select data-placeholder="{% trans 'Join user groups' %}" id="groups_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}">{{ group.name }}</option>
{% endfor %}
......@@ -192,16 +196,18 @@
</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>
<button type="button" class="btn btn-info btn-small" id="btn_join_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in user_object.groups.all %}
{% for group in user.groups.all %}
<tr>
<td ><b class="bdg_user_group" data-gid={{ group.id }}>{{ group.name }}</b></td>
<td >
<b class="bdg_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>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
......@@ -219,51 +225,48 @@
{% endblock %}
{% block custom_foot_js %}
<script>
jumpserver.selected_groups = {};
jumpserver.groups_selected = {};
function updateUserGroups(user_groups) {
var the_url = "{% url 'users:group-user-edit-api' pk=user_object.id %}";
function updateUserGroups(groups) {
var the_url = "{% url 'api-users:user-update-group' pk=user.id %}";
var body = {
id: {{ user_object.id }},
groups: Object.assign([], user_groups)
groups: Object.assign([], 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) {
$('#groups_selected').val('');
$.map(jumpserver.groups_selected, 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>' +
'<td><b class="bdg_group" data-gid="' + index + '">' + group_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_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!" %}')
// clear jumpserver.groups_selected
jumpserver.groups_selected = {};
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success: success,
method: 'PUT'
success: success
});
}
$(document).ready(function() {
$('.select2').select2()
.on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.selected_groups[data.id] = data.text;
jumpserver.groups_selected[data.id] = data.text;
}).on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.selected_groups[data.id]
delete jumpserver.groups_selected[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 the_url = "{% url 'api-users:user-detail' pk=user.id %}";
var checked = $(this).prop('checked');
var body = {
'is_active': checked
};
......@@ -274,8 +277,8 @@ $(document).ready(function() {
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 the_url = "{% url 'api-users:user-detail' pk=user.id %}";
var checked = $(this).prop('checked');
var body = {
'enable_otp': checked
};
......@@ -285,38 +288,38 @@ $(document).ready(function() {
body: JSON.stringify(body),
success_message: success
});
}).on('click', '#btn_add_user_group', function() {
if (Object.keys(jumpserver.selected_groups).length === 0) {
}).on('click', '#btn_join_group', function() {
if (Object.keys(jumpserver.groups_selected).length === 0) {
return false;
}
var user_groups = $('.bdg_user_group').map(function() {
var groups = $('.bdg_group').map(function() {
return $(this).data('gid');
}).get();
$.map(jumpserver.selected_groups, function(value, index) {
user_groups.push(parseInt(index));
$.map(jumpserver.groups_selected, function(value, index) {
groups.push(parseInt(index));
$('#opt_' + index).remove();
});
updateUserGroups(user_groups)
}).on('click', '.btn_delete_user_group', function() {
updateUserGroups(groups)
}).on('click', '.btn_leave_group', function() {
var $this = $(this);
var $tr = $this.closest('tr');
var $badge = $tr.find('.bdg_user_group');
var $badge = $tr.find('.bdg_group');
var gid = $badge.data('gid');
var group_name = $badge.html() || $badge.text();
$('#slct_groups').append(
$('#groups_selected').append(
'<option value="' + gid + '" id="opt_' + gid + '">' + group_name + '</option>'
);
$tr.remove();
var user_groups = $('.bdg_user_group').map(function() {
var groups = $('.bdg_group').map(function() {
return $(this).data('gid');
}).get();
updateUserGroups(user_groups)
updateUserGroups(groups)
}).on('click', '#btn_reset_password', function() {
function doReset() {
var the_url = '{% url "users:user-reset-password-api" pk=user_object.id %}';
var the_url = '{% url "api-users:user-reset-password" pk=user.id %}';
var body = {};
var success = function() {
var msg = "{% trans 'E-mail sent successfully. An e-mail has been sent to the user\'s mailbox.' %}";
var msg = "{% trans "An e-mail has been sent to the user\'s mailbox." %}";
swal("{% trans 'Password-Reset' %}", msg, "success");
};
APIUpdateAttr({
......@@ -338,7 +341,7 @@ $(document).ready(function() {
});
}).on('click', '#btn_reset_pk', function() {
function doReset() {
var the_url = '{% url "users:user-reset-pk-api" pk=user_object.id %}';
var the_url = '{% url "api-users:user-reset-pk" pk=user.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.' %}";
......@@ -346,7 +349,7 @@ $(document).ready(function() {
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
body: body,
success: success
});
}
......@@ -364,7 +367,7 @@ $(document).ready(function() {
}).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 the_url = '{% url "api-users:user-update-pk" pk=user.id %}';
var body = {'_public_key': pk};
var success = function() {
$('#txt_pk').val('');
......@@ -372,8 +375,7 @@ $(document).ready(function() {
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.' %}";
var fail = function(msg) {
swal({
title: "{% trans 'User SSH Public Key Update' %}",
text: msg,
......@@ -386,7 +388,7 @@ $(document).ready(function() {
$('#txt_pk').focus();
}
);
}
};
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
});
</script>
......
......@@ -17,34 +17,21 @@
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="{% url 'users:user-detail' pk=user_object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
<a href="{% url 'users:user-detail' pk=user.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>
<a href="{% url 'users:user-asset-permission' pk=user.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
</li>
<li class="active">
<a href="{% url 'users:user-granted-asset' pk=user_object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
<a href="{% url 'users:user-granted-asset' pk=user.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>
<form id="search_form" method="get" action="" class="pull-right mail-search">
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
<div class="input-group-btn">
<button id="search_btn" type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
</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 'Granted assets of ' %} <b>{{ user_object.name }}</b></span>
<span style="float: left">{% trans 'Assets granted of ' %} <b>{{ user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......@@ -60,54 +47,27 @@
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<table class="table table-hover " id="user_assets_table" >
<thead>
<tr>
<th class="text-center"><a href="{% url 'perms:asset-permission-list' %}?sort=name">{% trans 'Hostname' %}</a></th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Port' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">
<a href="#">{% trans 'Is valid' %}</a>
</th>
<th></th>
<th class="text-center"></th>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'System user' %}</th>
<th>{% trans 'Valid' %}</th>
</tr>
</thead>
<tbody>
{% for asset, system_users in object_list %}
<tr class="gradeX">
<td class="text-center">
<a href="{% url 'assets:asset-detail' pk=asset.id %}">
{{ asset.hostname }}
</a>
</td>
<td class="text-center">{{ asset.ip }}</td>
<td class="text-center">{{ asset.port }}</td>
<td class="text-center">{{ system_users|join_attr:"name" }}</td>
<td class="text-center">
{% if asset.is_valid %}
<i class="fa fa-check text-navy"></i>
{% else %}
<i class="fa fa-times text-danger"></i>
{% endif %}
</td>
<td>
<button class="btn btn-danger btn-xs btn_delete_user_group {% if asset.is_inherit_from_user_groups or asset.is_inherit_from_asset_groups %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</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="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'Asset groups granted of ' %} <b>{{ user_object.name }}</b></span>
<span style="float: left">{% trans 'Asset groups granted of ' %} <b>{{ user.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......@@ -123,30 +83,15 @@
</div>
</div>
<div class="ibox-content">
<table class="table table-hover">
<table class="table table-hover" id="user_asset_groups_table" >
<thead>
<tr>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Asset count' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th></th>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Asset' %}</th>
</tr>
</thead>
<tbody>
{% for asset_group, system_users in asset_groups %}
<tr class="gradeX">
<td class="text-center">
<a href="{% url 'assets:asset-group-detail' pk=asset_group.id %}">
{{ asset_group.name }}
</a>
</td>
<td class="text-center">{{ asset_group.assets.count }}</td>
<td class="text-center">{{ system_users|join_attr:"name" }}</td>
<td>
<button class="btn btn-danger btn-xs btn_delete_user_group {% if not asset_group.is_inherit_from_user_groups %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
......@@ -161,25 +106,53 @@
{% 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_object.id %}",#}
{# type: "PUT",#}
{# data: {#}
{# 'is_active': status#}
{# },#}
{# success: function (data, status) {#}
{# console.log(data)#}
{# },#}
{# error: function () {#}
{# console.log('error')#}
{# }#}
{# })#}
{# }#}
$(document).ready(function () {
$('.select2').select2();
var options = {
ele: $('#user_assets_table'),
buttons: [],
order: [],
select: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
if (cellData.length > 10){
$(td).html(cellData.substring(1, 10) + '..')
} else {
$(td).html(cellData)
}
}},
{targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}}
],
ajax_url: '{% url "api-perms:user-assets" pk=user.id %}',
columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port"},
{data: "system_users_join"}, {data: "is_active"}]
};
var options2 = {
ele: $('#user_asset_groups_table'),
buttons: [],
order: [],
select: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}}
],
ajax_url: '{% url "api-perms:user-asset-groups" pk=user.id %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }]
};
jumpserver.initDataTable(options);
jumpserver.initDataTable(options2);
});
</script>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load bootstrap %}
{% 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 'users:user-group-detail' pk=user_group.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
</li>
<li class="active">
<a href="{% url 'users:user-group-asset-permission' pk=user_group.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
</li>
<li>
<a href="{% url 'users:user-group-granted-asset' pk=user_group.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</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 permission of ' %} <b>{{ user_group.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 table-hover " id="user_group_permissions_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Asset' %}</th>
<th>{% trans 'Asset group' %}</th>
<th>{% trans 'System user' %}</th>
<th>{% trans 'Valid' %}</th>
<th></th>
</tr>
</thead>
<tbody>
</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 create permission for user group' %}
</div>
<div class="panel-body">
<form method="post" action="{% url 'users:user-group-asset-permission-create' pk=user_group.id %}">
<table class="table">
<tbody>
{% csrf_token %}
<tr class="no-borders-tr">
<td colspan="1" style="padding-top: 0">
{{ form.name|bootstrap }}
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="1" style="padding-top: 0">
{{ form.assets|bootstrap }}
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="1" style="padding-top: 0">
{{ form.asset_groups|bootstrap }}
</td>
</tr>
<tr class="no-borders-tr">
<td colspan="1" style="padding-top: 0">
{{ form.system_users|bootstrap }}
</td>
</tr>
<tr class="no-borders-tr">
<td>
<button type="submit" class="btn btn-primary btn-sm">{% trans 'Submit' %}</button>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
var options = {
ele: $('#user_group_permissions_table'),
buttons: [],
order: [],
select: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "perms:asset-permission-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData, rowData) {
var dataLength = cellData.length;
$(td).html(dataLength);
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var dataLength = cellData.length;
$(td).html(dataLength);
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var dataLength = cellData.length;
$(td).html(dataLength);
}},
{targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var btn = '<button class="btn btn-danger btn-xs btn_del_permission" id=99991937 type="button" style="float: right;"><i class="fa fa-minus"></i></button>';
btn = btn.replace('99991937', cellData);
$(td).html(btn)
}}
],
ajax_url: '{% url "api-perms:asset-permission-list" %}?user_group={{ user_group.id }}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets" }, {data: "asset_groups"},
{data: "system_users"}, {data: "is_active"}, {data: "id"}]
};
jumpserver.initDataTable(options);
}).on('click', '.btn_del_permission', function () {
var $this = $(this);
var body = {
id: $this.attr('id'),
user_group_id: {{ user_group.id }}
};
var the_url = "{% url 'perms:revoke-user-group-asset-permission' %}";
var success = function () {
$this.closest('tr').remove();
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'PUT',
success_message: '{% trans "Revoke Successfully!" %}',
success: success
});
}).on('click', 'buttons-excel', function () {
console.log('click excel')
})
</script>
{% endblock %}
\ No newline at end of file
......@@ -19,20 +19,21 @@
<form method="post" class="form-horizontal" action="" >
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}
<div class="form-group">
<label for="users" class="col-sm-2 control-label">{% trans 'Users' %}</label>
<div class="col-sm-9">
<select name="users" id="users" data-placeholder="{% trans 'Select User' %}" class="select2 form-control m-b" multiple tabindex="2">
<select name="users" id="id_users" data-placeholder="{% trans 'Select User' %}" class="select2 form-control m-b" multiple tabindex="2">
{% for user in users %}
<option value="{{ user.id }}">{{ user.name }}</option>
{% if user.id in group_users %}
<option value="{{ user.id }}" selected>{{ user.name }}</option>
{% else %}
<option value="{{ user.id }}">{{ user.name }}</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
{{ 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 'Cancel' %}</button>
......@@ -45,11 +46,12 @@
</div>
</div>
</div>
{% include "users/_select_user_modal.html" %}
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2().val([{{ group_users }}]).trigger("change");
$('.select2').select2();
})
</script>
{% endblock %}
......@@ -10,54 +10,6 @@
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
<script src="{% static "js/plugins/dataTables/dataTables.min.js" %}"></script>
<style>
.user_div {
color: #5e5e5e;
font-family: "Open Sans";
padding: 3px 8px;
text-shadow: none;
}
.user_div .ui_container {
background-color: #d1dade;
width: 100%;
}
.user_div a {
color: #5e5e5e;
}
.user_div .remove {
color: #fff;
}
dl {
width: 100%;
overflow: hidden;
padding: 0;
margin: 10px;
border-bottom: 1px solid #e7eaec;
}
dt {
float: left;
width: 30%;
padding: 0;
margin: 0
}
dd {
float: left;
width: 70%;
padding: 0;
margin: 0
}
#group_user_row dt {
border-bottom: 1px solid #e7eaec;
width: 100%;
margin-bottom: 20px;
}
#group_user_row dd {
width: 100%;
overflow: auto;
}
</style>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -67,7 +19,16 @@ dd {
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'users:user-group-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User Group Detail' %} </a>
<a href="{% url 'users:user-group-detail' pk=user_group.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User Group Detail' %} </a>
</li>
<li>
<a href="{% url 'users:user-group-asset-permission' pk=user_group.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
</li>
<li>
<a href="{% url 'users:user-group-granted-asset' pk=user.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'users:user-group-update' pk=user_group.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
</ul>
</div>
......@@ -75,7 +36,7 @@ dd {
<div class="col-sm-7" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ object.name }}</b></span>
<span class="label"><b>{{ user_group.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......@@ -86,53 +47,62 @@ dd {
</div>
</div>
<div class="ibox-content">
<dl>
<dt width="20%">{% trans 'Name' %}:</dt>
<dd><b>{{ object.name }}</b></dd>
</dl>
<dl>
<dt>{% trans 'Comment' %}:</dt>
<dd><b>{{ object.comment }}</b></dd>
</dl>
<dl>
<dt>{% trans 'Created at' %}:</dt>
<dd><b>{{ object.date_created }}</b></dd>
</dl>
<dl id="group_user_row">
<dt>{% trans 'Users' %}:</dt>
<dd style="line-height: 2" id="group_user_container">
{% for user in object.users.all %}
<div class="col-sm-4 user_div"><div class="ui_container row"><div class="col-xs-9"><a href="{{ user.get_absolute_url }}" title="{{ user.name }}" data-toggle="tooltip">{{ user.name|truncatechars:15 }}</a></div><div class="col-xs-3"><a data-uid="{{ user.id }}" class="btn_remove m-l-5"><i class="remove fa fa-times-circle"></i></a></div></div></div>
{% endfor %}
</dd>
</dl>
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="20%">{% trans 'Name' %}:</td>
<td><b>{{ user_group.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Create by' %}:</td>
<td><b>{{ user_group.created_by }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ user_group.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ user_group.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' %}
<i class="fa fa-info-circle"></i> {% trans 'User' %}
</div>
<div class="panel-body">
<table class="table">
<table class="table user_edit">
<tbody>
<form>
<tr>
<td>{% trans 'Add User' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_group_add_user" style="width: 54px" data-toggle="modal" data-target="#select_user_modal">{% trans 'Add' %}</button>
</span>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Add user' %}" id="slct_users" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for user in users %}
<option value="{{ user.id }}" id="opt_{{ user.id }}">{{ user.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td>{% trans 'Delete' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-danger btn-xs" id="btn_group_delete" style="width: 54px">{% trans 'Delete' %}</button>
</span>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-primary btn-small" id="btn_add_user">{% trans 'Add' %}</button>
</td>
</tr>
</form>
{% for user in user_group.users.all %}
<tr>
<td ><b class="bdg_user" data-uid={{ user.id }}>{{ user.name }}</b></td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_remove_user" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
......@@ -143,101 +113,77 @@ dd {
</div>
</div>
</div>
{% include "users/_select_user_modal.html" %}
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).on('click', '.btn_remove', function(){
var $this = $(this);
var uid = $this.data('uid');
var the_url = '{% url "users:delete-user-from-group-api" pk=object.id uid=99991937 %}'.replace('99991937', uid);
var success = function(){
$this.closest('.user_div').remove();
jumpserver.users_selected = {};
function updateGroupMember(users) {
var the_url = "{% url 'api-users:user-group-update-user' pk=user_group.id %}";
var body = {
users: Object.assign([], users)
};
var error = function(){};
APIUpdateAttr({url: the_url, body: "{}", method: "DELETE", success: success, error: error});
return false;
}).on('click', '#btn_group_delete', function() {
function doDelete() {
var the_url = '{% url "users:user-group-detail-api" pk=object.id %}';
var success = function() {
window.location.href = '{% url "users:user-group-list" %}';
};
APIUpdateAttr({url: the_url, body: "{}", method: "DELETE", success: success});
}
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the current group, but will not delete any user of it.' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
doDelete();
});
}).on('shown.bs.modal', '#select_user_modal', function() {
if ($.fn.dataTable.isDataTable('#select_user_table')) {
return true;
}
var options = {
ele: $('#select_user_table'),
pageLength: 10,
buttons: [],
columnDefs: [
{targets: 6, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 4, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 8 ? cellData.substring(0, 8) + '...': cellData;
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
}}
],
ajax_url: '{% url "users:user-bulk-update-api" %}',
columns: [{data: function(){return ""}}, {data: "username" }, {data: "name" }, {data: "get_role_display" }, {data: "group_display" },
{data: function(){return 999}}, {data: "active_display" }],
var success = function(data) {
// remove all the selected groups from select > option and rendered ul element;
$('.select2-selection__rendered').empty();
$('#slct_users').val('');
$.map(jumpserver.users_selected, function(user_name, index) {
$('#opt_' + index).remove();
// change tr html of users
$('.user_edit tbody').append(
'<tr>' +
'<td><b class="bdg_user" data-uid="' + index + '">' + user_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn_remove_user" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
// clear jumpserver.selected_groups
jumpserver.users_selected = {};
};
jumpserver.initDataTable(options);
}).on('click', '#btn_select_user', function() {
var $data_table = $('#select_user_table').DataTable();
var plain_id_list = [];
var selected_users = [];
$data_table.rows({selected: true}).every(function(){
plain_id_list.push(this.data().id);
selected_users.push({id: this.data().id, name: this.data().name});
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success: success
});
if (plain_id_list === []) {
}
$(document).ready(function () {
$('.select2').select2()
.on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.users_selected[data.id] = data.text;
}).on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.users_selected[data.id]
})
}).on('click', '.btn_remove_user', function() {
var $this = $(this);
var $tr = $this.closest('tr');
var $badge = $tr.find('.bdg_user');
var uid = $badge.data('uid');
var user_name = $badge.html() || $badge.text();
$('#slct_users').append(
'<option value="' + uid + '" id="opt_' + uid + '">' + user_name + '</option>'
);
$tr.remove();
var users = $('.bdg_user').map(function() {
return $(this).data('uid');
}).get();
console.log(users);
updateGroupMember(users)
}).on('click', '#btn_add_user', function() {
if (Object.keys(jumpserver.users_selected).length === 0) {
return false;
};
var body = {
id: {{ object.id }},
users: plain_id_list.map(Number)
};
$('#select_user_modal').modal('hide');
var the_url = "{% url 'users:user-group-detail-api' pk=object.id %}";
var success = function() {
toastr.success('{% trans "The selected users has been added to current group." %}');
var html = "";
$.each(selected_users, function(index, user) {
html += [
'<div class="col-sm-4 user_div"><div class="ui_container row"><div class="col-xs-9"><a title="',
user.name,
'" data-toggle="tooltip" href="',
'{% url "users:user-detail" pk=99991937 %}'.replace(99991937, user.id),
'">',
user.name.length >=13 ? user.name.substring(0, 12) + '...' : user.name,
'</a></div><div class="col-xs-3"><a data-uid="',
user.id,
'" class="btn_remove m-l-5"><i class="remove fa fa-times-circle"></i></a></div></div></div>\n',
].join("");
});
$(html).appendTo($('#group_user_container'));
}
APIUpdateAttr({url: the_url, body: JSON.stringify(body), success: success});
var users = $('.bdg_user').map(function() {
return $(this).data('uid');
}).get();
$.map(jumpserver.users_selected, function(value, index) {
users.push(parseInt(index));
$('#opt_' + index).remove();
});
console.log(users);
updateGroupMember(users)
})
</script>
{% endblock %}
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load bootstrap %}
{% 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 'users:user-group-detail' pk=user_group.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
</li>
<li>
<a href="{% url 'users:user-group-asset-permission' pk=user_group.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
</li>
<li class="active">
<a href="{% url 'users:user-group-granted-asset' pk=user_group.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</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 'Assets granted of ' %} <b>{{ user_group.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 table-hover " id="user_assets_table" >
<thead>
<tr>
<th class="text-center"></th>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'System user' %}</th>
<th>{% trans 'Valid' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'Asset groups granted of ' %} <b>{{ user_group.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 table-hover" id="user_asset_groups_table" >
<thead>
<tr>
<th></th>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Asset' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
var options = {
ele: $('#user_assets_table'),
buttons: [],
order: [],
select: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
if (cellData.length > 10){
$(td).html(cellData.substring(1, 10) + '..')
} else {
$(td).html(cellData)
}
}},
{targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}}
],
ajax_url: '{% url "perms:api-user-group-assets" pk=user_group.id %}',
columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port"},
{data: "system_users_join"}, {data: "is_active"}]
};
var options2 = {
ele: $('#user_asset_groups_table'),
buttons: [],
order: [],
select: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}}
],
ajax_url: '{% url "api-perms:user-group-asset-groups" pk=user_group.id %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }]
};
jumpserver.initDataTable(options);
jumpserver.initDataTable(options2);
});
</script>
{% endblock %}
\ No newline at end of file
{% extends '_base_list.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
{{ block.super }}
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: right !important;
}
div.dataTables_wrapper div.dataTables_filter {
margin-left: 15px;
}
</style>
{% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="pull-left m-r-5"><a href="{% url 'users:user-group-create' %}" class="btn btn-sm btn-primary ">{% trans "Add User Group" %}</a></div>
......@@ -20,11 +7,10 @@ div.dataTables_wrapper div.dataTables_filter {
<thead>
<tr>
<th class="text-center">
<div class="checkbox checkbox-default"><input id="" type="checkbox" class="ipt_check_all"><label></label></div>
<input id="" type="checkbox" class="ipt_check_all">
</th>
<th class="text-center">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'User Amount' %}</a></th>
<th class="text-center">{% trans 'Asset Amount' %}</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'User Amount' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
......@@ -56,11 +42,11 @@ $(document).ready(function() {
var detail_btn = '<a href="{% url "users:user-group-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 18 ? cellData.substring(0, 18) + '...': cellData;
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
{targets: 3, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData;
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
{targets: 4, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "users:user-group-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_delete_user_group" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
if (rowData.id === 1) {
......@@ -69,9 +55,9 @@ $(document).ready(function() {
$(td).html(update_btn + del_btn)
}
}}],
ajax_url: '{% url "users:user-group-bulk-update-api" %}',
ajax_url: '{% url "api-users:user-group-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "user_amount"},
{data: function(){return 999}}, {data: "comment"}, {data: "id" }],
{data: "comment"}, {data: "id" }],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
......@@ -79,7 +65,7 @@ $(document).ready(function() {
var $this = $(this);
function doDelete() {
var group_id = $this.data('gid');
var the_url = "{% url 'users:user-group-detail-api' 99991937 %}".replace('99991937', group_id);
var the_url = "{% url 'api-users:user-group-detail' pk=99991937 %}".replace('99991937', group_id);
var body = {};
var success = function() {
var msg = "{% trans 'Group Deleted.' %}";
......@@ -89,7 +75,7 @@ $(document).ready(function() {
var fail = function() {
var msg = "{% trans 'Group Deleting failed.' %}";
swal("{% trans 'Group Delete' %}", msg, "error");
}
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
......@@ -118,8 +104,8 @@ $(document).ready(function() {
});
if (plain_id_list === []) {
return false;
};
var the_url = "{% url 'users:user-group-bulk-update-api' %}";
}
var the_url = "{% url 'api-users:user-group-list' %}";
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
......
{% extends '_base_list.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
{{ block.super }}
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: right !important;
}
div.dataTables_wrapper div.dataTables_filter {
margin-left: 15px;
}
</style>
{% block table_search %}
<div class="html5buttons">
<div class="dt-buttons btn-group">
<a class="btn btn-default buttons-pdf" tabindex="0" href="#">
<span>PDF</span></a>
<a class="btn btn-default buttons-excel" tabindex="0" href="#">
<span>Excel</span>
</a>
</div>
</div>
{% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left"><a href="javascript:void(0);" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>
<div class="uc pull-left"><a href="javascript:void(0);" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>
<table class="table table-striped table-bordered table-hover " id="user_list_table" >
<thead>
<tr>
......@@ -28,7 +25,6 @@ div.dataTables_wrapper div.dataTables_filter {
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Role' %}</th>
<th class="text-center">{% trans 'User group' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
......@@ -66,31 +62,39 @@ $(document).ready(function(){
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 8 ? cellData.substring(0, 8) + '...': cellData;
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
var innerHtml = cellData.length > 20 ? cellData.substring(0, 20) + '...': cellData;
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}},
{targets: 6, createdCell: function (td, cellData) {
{targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
{targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
if (rowData.id === 1) {
if (rowData.id === 1 || rowData.username == "admin") {
$(td).html(update_btn)
} else {
$(td).html(update_btn + del_btn)
}
}}],
ajax_url: '{% url "users:user-bulk-update-api" %}',
columns: [{data: function(){return ""}}, {data: "username" }, {data: "name" }, {data: "get_role_display" }, {data: "group_display" },
{data: function(){return 999}}, {data: "active_display" }, {data: "id" }],
op_html: $('#actions').html()
ajax_url: '{% url "api-users:user-list" %}',
columns: [{data: "id"}, {data: "username" }, {data: "name" }, {data: "get_role_display" },
{data: "groups_display" }, {data: "is_valid" }, {data: "id" }]
};
jumpserver.initDataTable(options);
var table = jumpserver.initDataTable(options);
$('.buttons-pdf').click(function () {
var users = [];
var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) {
users.push(obj.id)
})
});
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#user_list_table').DataTable();
......@@ -103,7 +107,7 @@ $(document).ready(function(){
if (id_list === []) {
return false;
}
var the_url = "{% url 'users:user-bulk-update-api' %}";
var the_url = "{% url 'api-users:user-list' %}";
function doDeactive() {
var body = $.each(id_list, function(index, user_object) {
user_object['is_active'] = false;
......@@ -156,7 +160,7 @@ $(document).ready(function(){
var $this = $(this);
function doDelete() {
var uid = $this.data('uid');
var the_url = '{% url "users:user-patch-api" pk=99991937 %}'.replace('99991937', uid);
var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid);
var body = {};
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
......@@ -211,7 +215,7 @@ $(document).ready(function(){
if (post_list === []) {
return false
}
var the_url = "{% url 'users:user-bulk-update-api' %}";
var the_url = "{% url 'api-users:user-list' %}";
var success = function() {
var msg = "{% trans 'The selected users has been updated successfully.' %}";
swal("{% trans 'User Updated' %}", msg, "success");
......@@ -230,13 +234,11 @@ $(document).ready(function(){
} else {
$('#user_import_modal').modal('hide');
var $data_table = $('#user_list_table').DataTable();
toastr.success("{% trans 'Import User Success.' %}")
toastr.success("{% trans 'Import User Success.' %}");
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
}).on('change', '#id_excel', function() {
$(this).siblings('.help-block').remove();
})
</script>
{% endblock %}
......
{% extends 'users/_user.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">
......
#!/usr/bin/env python
# ~*~ coding: utf-8 ~*~
#
from __future__ import absolute_import
from django.conf.urls import url
from rest_framework_bulk.routes import BulkRouter
from .. import api
app_name = 'users'
router = BulkRouter()
router.register(r'v1/users', api.UserViewSet, 'user')
router.register(r'v1/user-groups', api.UserGroupViewSet, 'user-group')
# router.register(r'v1/user-groups', api.AssetViewSet, 'api-groups')
urlpatterns = [
# url(r'^v1/users/$', api.UserListUpdateApi.as_view(), name='user-bulk-update-api'),
url(r'^v1/users/token/$', api.UserAuthApi.as_view(), name='user-token'),
url(r'^v1/users/(?P<pk>\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'),
url(r'^v1/users/(?P<pk>\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='user-reset-pk'),
url(r'^v1/users/(?P<pk>\d+)/update-pk/$', api.UserUpdatePKApi.as_view(), name='user-update-pk'),
# url(r'^v1/user-groups/$', api.GroupListUpdateApi.as_view(), name='user-group-bulk-update-api'),
# url(r'^v1/user-groups/(?P<pk>\d+)/$', api.GroupDetailApi.as_view(), name='user-group-detail-api'),
# url(r'^v1/user-groups/(?P<pk>\d+)/user/(?P<uid>\d+)/$',
# api.DeleteUserFromGroupApi.as_view(), name='delete-user-from-group-api'),
url(r'^v1/users/(?P<pk>\d+)/groups/$',
api.UserUpdateGroupApi.as_view(), name='user-update-group'),
url(r'^v1/user-groups/(?P<pk>\d+)/users/$',
api.UserGroupUpdateUserApi.as_view(), name='user-group-update-user'),
]
urlpatterns += router.urls
from django.conf.urls import url
from __future__ import absolute_import
import views
import api
from django.conf.urls import url
from .. import views
app_name = 'users'
......@@ -14,37 +14,30 @@ urlpatterns = [
url(r'^password/reset$', views.UserResetPasswordView.as_view(), name='reset-password'),
url(r'^password/reset/success$', views.UserResetPasswordSuccessView.as_view(),
name='reset-password-success'),
# User view
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'^user/(?P<pk>[0-9]+)/asset-permission$', views.UserAssetPermissionView.as_view(),
name='user-asset-permission'),
url(r'^user/(?P<pk>[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(),
name='user-asset-permission-create'),
url(r'^user/(?P<pk>[0-9]+)/granted-asset', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
url(r'^user/(?P<pk>[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
url(r'^import/$', views.BulkImportUserView.as_view(), name='user-import'),
url(r'^user/(?P<pk>[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'),
# 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'),
# User group view
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'),
url(r'^user-group/(?P<pk>[0-9]+)$', views.UserGroupDetailView.as_view(), name='user-group-detail'),
url(r'^user-group/create$', views.UserGroupCreateView.as_view(), name='user-group-create'),
url(r'^user-group/(?P<pk>[0-9]+)/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'),
]
urlpatterns += [
url(r'^v1/users/$', api.UserListUpdateApi.as_view(), name='user-bulk-update-api'),
url(r'^v1/users/token/$', api.UserTokenApi.as_view(), name='user-token-api'),
url(r'^v1/users/(?P<pk>\d+)/$', api.UserDetailApi.as_view(), name='user-patch-api'),
url(r'^v1/users/(?P<pk>\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password-api'),
url(r'^v1/users/(?P<pk>\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='user-reset-pk-api'),
url(r'^v1/users/(?P<pk>\d+)/update-pk/$', api.UserUpdatePKApi.as_view(), name='user-update-pk-api'),
url(r'^v1/user-groups/$', api.GroupListUpdateApi.as_view(), name='user-group-bulk-update-api'),
url(r'^v1/user-groups/(?P<pk>\d+)/$', api.GroupDetailApi.as_view(), name='user-group-detail-api'),
url(r'^v1/user-groups/(?P<pk>\d+)/user/(?P<uid>\d+)/$',
api.DeleteUserFromGroupApi.as_view(), name='delete-user-from-group-api'),
url(r'^v1/user-groups/(?P<pk>\d+)/users/$',
api.UserAndGroupEditApi.as_view(), name='group-user-edit-api'),
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission$', views.UserGroupAssetPermissionView.as_view(),
name='user-group-asset-permission'),
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission/create$', views.UserGroupAssetPermissionCreateView.as_view(),
name='user-group-asset-permission-create'),
url(r'^user-group/(?P<pk>[0-9]+)/assets', views.UserGroupGrantedAssetView.as_view(),
name='user-group-granted-asset'),
]
......@@ -34,38 +34,6 @@ class AdminUserRequiredMixin(UserPassesTestMixin):
return self.request.user.is_staff
def ssh_key_gen(length=2048, password=None, username='root', hostname=None):
"""Generate user ssh private and public key
Use paramiko RSAKey generate it.
"""
if hostname is None:
hostname = os.uname()[1]
f = StringIO.StringIO()
try:
logger.debug(_('Begin to generate ssh private key ...'))
private_key_obj = RSAKey.generate(length)
private_key_obj.write_private_key(f, password=password)
private_key = f.getvalue()
public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % {
'key_type': private_key_obj.get_name(),
'key_content': private_key_obj.get_base64(),
'username': username,
'hostname': hostname,
}
logger.debug(_('Finish to generate ssh private key ...'))
return private_key, public_key
except IOError:
raise IOError(_('These is error when generate ssh key.'))
def user_add_success_next(user):
subject = _('Create account successfully')
recipient_list = [user.email]
......@@ -107,7 +75,7 @@ def send_reset_password_mail(user):
</br>
<a href="%(rest_password_url)s?token=%(rest_password_token)s">Click here reset password</a>
</br>
This link is valid for 1 hour. After it expires, <a href="%(forget_password_url)s?email=%(email)s">request new one<</a>
This link is valid for 1 hour. After it expires, <a href="%(forget_password_url)s?email=%(email)s">request new one</a>
</br>
---
......@@ -152,60 +120,60 @@ def send_reset_ssh_key_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()])
# 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()])
#
def check_user_valid(**kwargs):
password = kwargs.pop('password', None)
......@@ -216,8 +184,14 @@ def check_user_valid(**kwargs):
return None
if password and user.check_password(password):
return user
if public_key and user.public_key == public_key:
return user
if public_key:
public_key_saved = user.public_key.split()
if len(public_key_saved) == 1:
if public_key == public_key_saved[0]:
return user
elif len(public_key_saved) > 1:
if public_key == public_key_saved[1]:
return user
return None
......
......@@ -2,13 +2,14 @@
from __future__ import unicode_literals
import csv
from django import forms
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.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import reverse, redirect
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _
......@@ -21,16 +22,15 @@ from django.views.generic.list import ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView, FormView, SingleObjectMixin, \
FormMixin
from django.views.generic.detail import DetailView
from formtools.wizard.views import SessionWizardView
from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger
from perms.models import AssetPermission
from .models import User, UserGroup
from .forms import UserCreateForm, UserUpdateForm, UserGroupForm, UserLoginForm, UserInfoForm, UserKeyForm, \
UserPrivateAssetPermissionForm, UserBulkImportForm
from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_password_mail
from .hands import AssetPermission, get_user_granted_asset_groups, get_user_granted_assets
from .hands import write_login_log_async
from . import forms
logger = get_logger(__name__)
......@@ -41,7 +41,7 @@ logger = get_logger(__name__)
@method_decorator(never_cache, name='dispatch')
class UserLoginView(FormView):
template_name = 'users/login.html'
form_class = UserLoginForm
form_class = forms.UserLoginForm
redirect_field_name = 'next'
def get(self, request, *args, **kwargs):
......@@ -51,6 +51,10 @@ class UserLoginView(FormView):
def form_valid(self, form):
auth_login(self.request, form.get_user())
login_ip = self.request.META.get('REMOTE_ADDR', '')
user_agent = self.request.META.get('HTTP_USER_AGENT', '')
write_login_log_async.delay(self.request.user.username, self.request.user.name,
login_type='W', login_ip=login_ip, user_agent=user_agent)
return redirect(self.get_success_url())
def get_success_url(self):
......@@ -64,7 +68,7 @@ class UserLoginView(FormView):
@method_decorator(never_cache, name='dispatch')
class UserLogoutView(TemplateView):
template_name = 'common/flash_message_standalone.html'
template_name = 'flash_message_standalone.html'
def get(self, request, *args, **kwargs):
auth_logout(request)
......@@ -92,7 +96,7 @@ class UserListView(AdminUserRequiredMixin, TemplateView):
class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = User
form_class = UserCreateForm
form_class = forms.UserCreateUpdateForm
template_name = 'users/user_create.html'
success_url = reverse_lazy('users:user-list')
success_message = _('Create user <a href="%s">%s</a> successfully.')
......@@ -118,7 +122,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
class UserUpdateView(AdminUserRequiredMixin, UpdateView):
model = User
form_class = UserUpdateForm
form_class = forms.UserCreateUpdateForm
template_name = 'users/user_update.html'
context_object_name = 'user_object'
success_url = reverse_lazy('users:user-list')
......@@ -142,7 +146,7 @@ class UserUpdateView(AdminUserRequiredMixin, UpdateView):
class UserDetailView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_detail.html'
context_object_name = "user_object"
context_object_name = "user"
def get_context_data(self, **kwargs):
groups = UserGroup.objects.exclude(id__in=self.object.groups.all())
......@@ -162,8 +166,8 @@ class UserGroupListView(AdminUserRequiredMixin, TemplateView):
class UserGroupCreateView(AdminUserRequiredMixin, CreateView):
model = UserGroup
form_class = UserGroupForm
template_name = 'users/user_group_create.html'
form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list')
def get_context_data(self, **kwargs):
......@@ -184,15 +188,15 @@ class UserGroupCreateView(AdminUserRequiredMixin, CreateView):
class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView):
model = UserGroup
form_class = UserGroupForm
template_name = 'users/user_group_create.html'
form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list')
def get_context_data(self, **kwargs):
self.object = self.get_object()
# self.object = self.get_object()
context = super(UserGroupUpdateView, self).get_context_data(**kwargs)
users = User.objects.all()
group_users = ",".join([str(u.id) for u in self.object.users.all()])
group_users = [user.id for user in self.object.users.all()]
context.update({
'app': _('Users'),
'action': _('Update User Group'),
......@@ -213,10 +217,16 @@ class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView):
class UserGroupDetailView(AdminUserRequiredMixin, DetailView):
model = UserGroup
context_object_name = 'user_group'
template_name = 'users/user_group_detail.html'
def get_context_data(self, **kwargs):
context = {'app': _('Users'), 'action': _('User Group Detail')}
users = User.objects.exclude(id__in=self.object.users.all())
context = {
'app': _('Users'),
'action': _('User Group Detail'),
'users': users,
}
kwargs.update(context)
return super(UserGroupDetailView, self).get_context_data(**kwargs)
......@@ -239,7 +249,7 @@ class UserForgotPasswordView(TemplateView):
class UserForgotPasswordSendmailSuccessView(TemplateView):
template_name = 'common/flash_message_standalone.html'
template_name = 'flash_message_standalone.html'
def get_context_data(self, **kwargs):
context = {
......@@ -252,7 +262,7 @@ class UserForgotPasswordSendmailSuccessView(TemplateView):
class UserResetPasswordSuccessView(TemplateView):
template_name = 'common/flash_message_standalone.html'
template_name = 'flash_message_standalone.html'
def get_context_data(self, **kwargs):
context = {
......@@ -294,7 +304,7 @@ class UserResetPasswordView(TemplateView):
class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
template_name = 'users/first_login.html'
form_list = [UserInfoForm, UserKeyForm]
form_list = [forms.UserInfoForm, forms.UserKeyForm]
file_storage = default_storage
def dispatch(self, request, *args, **kwargs):
......@@ -343,101 +353,129 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = User
template_name = 'users/user_asset_permission.html'
context_object_name = 'user_object'
form_class = UserPrivateAssetPermissionForm
context_object_name = 'user'
form_class = forms.UserPrivateAssetPermissionForm
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=User.objects.all())
return super(UserAssetPermissionView, self).get(request, *args, **kwargs)
def get_asset_permission_inherit_from_user_group(self):
asset_permissions = set()
user_groups = self.object.groups.all()
def get_context_data(self, **kwargs):
context = {
'app': 'Users',
'action': 'User asset permissions',
}
kwargs.update(context)
return super(UserAssetPermissionView, self).get_context_data(**kwargs)
for user_group in user_groups:
for asset_permission in user_group.asset_permissions.all():
setattr(asset_permission, 'is_inherit_from_user_groups', True)
setattr(asset_permission, 'inherit_from_user_groups',
getattr(asset_permission, b'inherit_from_user_groups', set()).add(user_group))
asset_permissions.add(asset_permission)
return asset_permissions
class UserGroupAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView):
model = UserGroup
template_name = 'users/user_group_asset_permission.html'
context_object_name = 'user_group'
form_class = forms.UserPrivateAssetPermissionForm
def get_queryset(self):
asset_permissions = set(self.object.asset_permissions.all()) \
| self.get_asset_permission_inherit_from_user_group()
return list(asset_permissions)
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupAssetPermissionView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'Users',
'action': 'User asset permissions',
'action': 'User group asset permissions',
}
kwargs.update(context)
return super(UserAssetPermissionView, self).get_context_data(**kwargs)
return super(UserGroupAssetPermissionView, self).get_context_data(**kwargs)
class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
form_class = UserPrivateAssetPermissionForm
form_class = forms.UserPrivateAssetPermissionForm
model = AssetPermission
def get(self, request, *args, **kwargs):
user_object = self.get_object(queryset=User.objects.all())
return redirect(reverse('users:user-asset-permission', kwargs={'pk': user_object.id}))
user = self.get_object(queryset=User.objects.all())
return redirect(reverse('users:user-asset-permission', kwargs={'pk': user.id}))
def post(self, request, *args, **kwargs):
self.user_object = self.get_object(queryset=User.objects.all())
self.user = self.get_object(queryset=User.objects.all())
return super(UserAssetPermissionCreateView, self).post(request, *args, **kwargs)
def get_form(self, form_class=None):
form = super(UserAssetPermissionCreateView, self).get_form(form_class=form_class)
form.user = self.user_object
form.user = self.user
return form
def form_invalid(self, form):
print(form.errors)
return redirect(reverse('users:user-asset-permission', kwargs={'pk': self.user_object.id}))
return redirect(reverse('users:user-asset-permission', kwargs={'pk': self.user.id}))
def get_success_url(self):
return reverse('users:user-asset-permission', kwargs={'pk': self.user_object.id})
return reverse('users:user-asset-permission', kwargs={'pk': self.user.id})
class UserGrantedAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
class UserGroupAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
form_class = forms.UserGroupPrivateAssetPermissionForm
model = AssetPermission
def get(self, request, *args, **kwargs):
user_group = self.get_object(queryset=UserGroup.objects.all())
return redirect(reverse('users:user-group-asset-permission', kwargs={'pk': user_group.id}))
def post(self, request, *args, **kwargs):
self.user_group = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupAssetPermissionCreateView, self).post(request, *args, **kwargs)
def get_form(self, form_class=None):
form = super(UserGroupAssetPermissionCreateView, self).get_form(form_class=form_class)
form.user_group = self.user_group
return form
def form_invalid(self, form):
return redirect(reverse('users:user-group-asset-permission', kwargs={'pk': self.user_group.id}))
def get_success_url(self):
return reverse('users:user-group-asset-permission', kwargs={'pk': self.user_group.id})
class UserGrantedAssetView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_granted_asset.html'
context_object_name = 'user_object'
context_object_name = 'user'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=User.objects.all())
return super(UserGrantedAssetView, self).get(request, *args, **kwargs)
def get_queryset(self):
# Convert format from {'asset': ['system_users'], ..} to
# [('asset', ['system_users']), ('asset', ['system_users']))
assets_granted = [(asset, system_users) for asset, system_users in
get_user_granted_assets(self.object).items()]
return assets_granted
def get_context_data(self, **kwargs):
asset_groups = [(asset_group, system_users) for asset_group, system_users in
get_user_granted_asset_groups(self.object).items()]
context = {
'app': 'User',
'action': 'User granted asset',
'asset_groups': asset_groups,
}
kwargs.update(context)
return super(UserGrantedAssetView, self).get_context_data(**kwargs)
class FileForm(forms.Form):
excel = forms.FileField()
class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_group_granted_asset.html'
context_object_name = 'user_group'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupGrantedAssetView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'User',
'action': 'User group granted asset',
}
kwargs.update(context)
return super(UserGroupGrantedAssetView, self).get_context_data(**kwargs)
class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
form_class = FileForm
form_class = forms.FileForm
def form_invalid(self, form):
try:
......@@ -478,7 +516,7 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
'enable_otp': True if enable_otp in ['T', '1', 1, True] else False,
'role': role
}
form = UserBulkImportForm(data, auto_id=False)
form = forms.UserBulkImportForm(data, auto_id=False)
if form.is_valid():
form.save()
else:
......@@ -493,3 +531,14 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
'msg': 'ok' if not errors else '<br />'.join(errors)
}
return self.render_json_response(data)
def down_csv(request, xx):
print(xx)
response = HttpResponse(content_type='application/csv')
response['Content-Disposition'] = 'attachment; filename="somefile.csv"'
writer = csv.writer(response)
writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"])
return response
......@@ -11,3 +11,7 @@ django-simple-captcha==0.5.2
django-formtools==1.0
sshpubkeys==2.2.0
djangorestframework-bulk==0.2.1
paramiko==2.0.2
django-redis-cache==1.7.1
requests==2.11.1
itsdangerous==0.24
\ No newline at end of file
......@@ -28,8 +28,9 @@ def start_django():
def start_celery():
os.chdir(apps_dir)
os.environ.setdefault('C_FORCE_ROOT', '1')
print('start celery')
subprocess.call('celery -A common worker -l info', shell=True)
subprocess.call('celery -A common worker -B -s /tmp/celerybeat-schedule -l info ', shell=True)
def main():
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment