Commit 6e69c018 authored by ibuler's avatar ibuler

Merge branch 'audits'

parents 8731e081 aff37092
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from rest_framework import serializers from rest_framework import viewsets, generics, mixins
from rest_framework import viewsets, serializers, generics
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateDestroyAPIView from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateDestroyAPIView
from django.shortcuts import get_object_or_404
from common.mixins import BulkDeleteApiMixin from common.mixins import BulkDeleteApiMixin
from common.utils import get_object_or_none, signer from common.utils import get_object_or_none, signer
from .hands import IsSuperUserOrTerminalUser, IsSuperUser from .hands import IsSuperUserOrTerminalUser, IsSuperUser
from .models import AssetGroup, Asset, IDC, SystemUser from .models import AssetGroup, Asset, IDC, SystemUser, AdminUser
from .serializers import AssetBulkUpdateSerializer from . import serializers
class AssetGroupSerializer(serializers.ModelSerializer): class AssetViewSet(viewsets.ModelViewSet):
class Meta: """API endpoint that allows Asset to be viewed or edited."""
model = AssetGroup queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
# fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
class IDCSerializer(serializers.ModelSerializer): def get_queryset(self):
class Meta: queryset = super(AssetViewSet, self).get_queryset()
model = IDC idc = self.request.query_params.get('idc', '')
# fields = ('id', 'title', 'code', 'linenos', 'language', 'style') if idc:
queryset = queryset.filter(idc__id=idc)
return queryset
class AssetGroupViewSet(viewsets.ModelViewSet): class AssetGroupViewSet(viewsets.ModelViewSet):
...@@ -35,26 +31,44 @@ class AssetGroupViewSet(viewsets.ModelViewSet): ...@@ -35,26 +31,44 @@ class AssetGroupViewSet(viewsets.ModelViewSet):
some other comment some other comment
""" """
queryset = AssetGroup.objects.all() queryset = AssetGroup.objects.all()
serializer_class = AssetGroupSerializer serializer_class = serializers.AssetGroupSerializer
class AssetViewSet(viewsets.ModelViewSet):
"""API endpoint that allows Asset to be viewed or edited."""
queryset = Asset.objects.all()
serializer_class = AssetSerializer
class IDCViewSet(viewsets.ReadOnlyModelViewSet): class IDCViewSet(viewsets.ModelViewSet):
"""API endpoint that allows IDC to be viewed or edited.""" """API endpoint that allows IDC to be viewed or edited."""
queryset = IDC.objects.all() 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,) 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): class AssetListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
queryset = Asset.objects.all() queryset = Asset.objects.all()
serializer_class = AssetBulkUpdateSerializer serializer_class = serializers.AssetSerializer
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
......
# coding:utf-8 # coding:utf-8
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _
from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser, Tag 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): # class AssetForm(forms.ModelForm):
...@@ -23,12 +24,10 @@ from django.utils.translation import gettext_lazy as _ ...@@ -23,12 +24,10 @@ from django.utils.translation import gettext_lazy as _
# #
class AssetCreateForm(forms.ModelForm): class AssetCreateForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None) instance = kwargs.get('instance', None)
if instance: if instance:
initial = kwargs.get('initial', {}) initial = kwargs.get('initial', {})
#tags = instance.tags.all()
initial['tags'] = [t.pk for t in kwargs['instance'].tags.all()] initial['tags'] = [t.pk for t in kwargs['instance'].tags.all()]
super(AssetCreateForm, self).__init__(*args, **kwargs) super(AssetCreateForm, self).__init__(*args, **kwargs)
...@@ -141,7 +140,6 @@ class AdminUserForm(forms.ModelForm): ...@@ -141,7 +140,6 @@ class AdminUserForm(forms.ModelForm):
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}) 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, # Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True, 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) help_text=_('If also set private key, use that first'), required=False)
...@@ -166,21 +164,36 @@ class AdminUserForm(forms.ModelForm): ...@@ -166,21 +164,36 @@ class AdminUserForm(forms.ModelForm):
# Because we define custom field, so we need rewrite :method: `save` # Because we define custom field, so we need rewrite :method: `save`
admin_user = super(AdminUserForm, self).save(commit=commit) admin_user = super(AdminUserForm, self).save(commit=commit)
password = self.cleaned_data['password'] 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: if password:
admin_user.password = password admin_user.password = password
print(password) if private_key:
# Todo: Validate private key file, and generate public key admin_user.private_key = private_key
# Todo: Auto generate private key and public key admin_user.public_key = public_key
if private_key_file:
admin_user.private_key = private_key_file.read()
admin_user.save() 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: class Meta:
model = AdminUser model = AdminUser
fields = ['name', 'username', 'auto_generate_key', 'password', 'private_key_file', 'as_default', 'comment'] fields = ['name', 'username', 'password', 'private_key_file', 'comment']
widgets = { widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}), 'username': forms.TextInput(attrs={'placeholder': _('Username')}),
......
...@@ -6,8 +6,9 @@ from django.db import models ...@@ -6,8 +6,9 @@ from django.db import models
from django.core import serializers from django.core import serializers
import logging import logging
from django.utils.translation import ugettext_lazy as _ 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__) logger = logging.getLogger(__name__)
...@@ -91,13 +92,21 @@ class AssetExtend(models.Model): ...@@ -91,13 +92,21 @@ class AssetExtend(models.Model):
unique_together = ('key', 'value') 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): class AdminUser(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username')) username = models.CharField(max_length=16, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password')) _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key')) _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')) _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')) comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True) date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by')) created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
...@@ -107,7 +116,7 @@ class AdminUser(models.Model): ...@@ -107,7 +116,7 @@ class AdminUser(models.Model):
@property @property
def password(self): def password(self):
return decrypt(self._password) return signer.unsign(self._password)
@password.setter @password.setter
def password(self, password_raw): def password(self, password_raw):
...@@ -129,6 +138,10 @@ class AdminUser(models.Model): ...@@ -129,6 +138,10 @@ class AdminUser(models.Model):
def public_key(self, public_key_raw): def public_key(self, public_key_raw):
self._public_key = signer.sign(public_key_raw) self._public_key = signer.sign(public_key_raw)
@property
def assets_amount(self):
return self.assets.count()
class Meta: class Meta:
db_table = 'admin_user' db_table = 'admin_user'
...@@ -216,6 +229,10 @@ class SystemUser(models.Model): ...@@ -216,6 +229,10 @@ class SystemUser(models.Model):
assets = set(self.assets.all()) | self.get_assets_inherit_from_asset_groups() assets = set(self.assets.all()) | self.get_assets_inherit_from_asset_groups()
return list(assets) return list(assets)
@property
def assets_amount(self):
return self.assets.count()
class Meta: class Meta:
db_table = 'system_user' db_table = 'system_user'
...@@ -298,9 +315,8 @@ class Asset(models.Model): ...@@ -298,9 +315,8 @@ class Asset(models.Model):
admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets', admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets',
on_delete=models.SET_NULL, verbose_name=_("Admin user")) on_delete=models.SET_NULL, verbose_name=_("Admin user"))
system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System 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'),) 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")) 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')) 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')) cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU'))
...@@ -329,6 +345,7 @@ class Asset(models.Model): ...@@ -329,6 +345,7 @@ class Asset(models.Model):
def __unicode__(self): def __unicode__(self):
return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port} return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port}
@property
def is_valid(self): def is_valid(self):
warning = '' warning = ''
if not self.is_active: if not self.is_active:
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import viewsets, serializers,generics 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 common.mixins import BulkDeleteApiMixin
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
class AssetBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer): class AssetGroupSerializer(serializers.ModelSerializer):
# group_display = serializers.SerializerMethodField() assets_amount = serializers.SerializerMethodField()
# active_display = serializers.SerializerMethodField() # assets = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
#groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all())
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): class Meta(object):
model = Asset model = Asset
list_serializer_class = BulkListSerializer list_serializer_class = BulkListSerializer
fields = ['id', 'port', 'idc']
# def get_group_display(self, obj): class AssetGrantedSerializer(serializers.ModelSerializer):
# return " ".join([group.name for group in obj.groups.all()]) system_users = SystemUserSerializer(many=True, read_only=True)
# is_inherited = serializers.SerializerMethodField()
# def get_active_display(self, obj): system_users_join = serializers.SerializerMethodField()
# # TODO: user ative state
# return not (obj.is_expired and obj.is_active) class Meta(object):
\ No newline at end of file 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 @@ ...@@ -31,20 +31,8 @@
{% csrf_token %} {% csrf_token %}
{{ form.name|bootstrap_horizontal }} {{ form.name|bootstrap_horizontal }}
{{ form.username|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.password|bootstrap_horizontal }}
{{ form.private_key_file|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.assets|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }} {{ form.comment|bootstrap_horizontal }}
......
{% extends '_base_list.html' %} {% extends '_base_list.html' %}
{% load i18n %} {% load i18n static %}
{% load common_tags %} {% block table_search %}
{% block content_left_head %}
<a href="{% url 'assets:admin-user-create' %}" class="btn btn-sm btn-primary "> {% trans "Create admin user" %} </a>
{% endblock %} {% endblock %}
{% block table_container %}
{% block table_head %} <div class="uc pull-left m-l-5 m-r-5">
<th class="text-center">{% trans 'ID' %}</th> <a href="{% url "assets:admin-user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create admin user" %} </a>
<th class="text-center"><a href="{% url 'assets:admin-user-list' %}?sort=name">{% trans 'Name' %}</a></th> </div>
<th class="text-center"><a href="{% url 'assets:admin-user-list' %}?sort=username">{% trans 'Username' %}</a></th> <table class="table table-striped table-bordered table-hover " id="admin_user_list_table" >
<th class="text-center">{% trans 'Asset num' %}</th> <thead>
<th class="text-center">{% trans 'Lost connection' %}</th> <tr>
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">
<th class="text-center"></th> <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 %} {% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block table_body %} {% block custom_foot_js %}
{% for admin_user in admin_user_list %} <script>
<tr class="gradeX"> $(document).ready(function(){
<td class="text-center">{{ admin_user.id }}</td> var options = {
<td> ele: $('#admin_user_list_table'),
<a href="{% url 'assets:admin-user-detail' pk=admin_user.id %}"> columnDefs: [
{{ admin_user.name }} {targets: 1, createdCell: function (td, cellData, rowData) {
</a> var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=99991937 %}">' + cellData + '</a>';
</td> $(td).html(detail_btn.replace('99991937', rowData.id));
<td class="text-center">{{ admin_user.username }}</td> }},
<td class="text-center">{{ admin_user.assets.count }}</td> {targets: 5, createdCell: function (td, cellData) {
<td class="text-center">{{ admin_user.assets.count }}</td> var innerHtml = cellData.length > 8 ? cellData.substring(0, 24) + '...': cellData;
<td class="text-center">{{ admin_user.comment|truncatewords:8 }}</td> $(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
<td class="text-center"> }},
<!-- Todo: Click script button will paste a url to clipboard like: curl http://url/admin_user_create.sh | bash --> {# {targets: 6, createdCell: function (td, cellData) {#}
<a href="{% url 'assets:admin-user-update' pk=admin_user.id %}" class="btn btn-xs btn-primary">{% trans 'Script' %}</a> {# if (!cellData) {#}
<!-- Todo: Click refresh button will run a task to test admin user could connect asset or not immediately --> {# $(td).html('<i class="fa fa-times text-danger"></i>')#}
<a href="{% url 'assets:admin-user-update' pk=admin_user.id %}" class="btn btn-xs btn-warning">{% trans 'Refresh' %}</a> {# } else {#}
<a href="{% url 'assets:admin-user-update' pk=admin_user.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a> {# $(td).html('<i class="fa fa-check text-navy"></i>')#}
<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> {targets: 6, createdCell: function (td, cellData, rowData) {
{% endfor %} 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 %} {% endblock %}
...@@ -30,12 +30,11 @@ ...@@ -30,12 +30,11 @@
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <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> <button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div> </div>
</div> </div>
</form>
</form>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
...@@ -44,10 +43,9 @@ ...@@ -44,10 +43,9 @@
$('.select2').select2(); $('.select2').select2();
$("#id_tags").select2({ $("#id_tags").select2({
tags: true, tags: true,
maximumSelectionLength: 8, //最多能够选择的个数 maximumSelectionLength: 8 //最多能够选择的个数
//closeOnSelect: false //closeOnSelect: false
}); });
}) })
</script> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -5,13 +5,6 @@ ...@@ -5,13 +5,6 @@
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: left;
}
</style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
...@@ -94,7 +87,7 @@ div.dataTables_wrapper div.dataTables_filter, ...@@ -94,7 +87,7 @@ div.dataTables_wrapper div.dataTables_filter,
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2();
$('.select2-system-user').select2(); $('.select2-system-user').select2();
}) });
$('#add_asset').on('click',function(){ $('#add_asset').on('click',function(){
$('#modal').modal('show'); $('#modal').modal('show');
...@@ -104,7 +97,7 @@ div.dataTables_wrapper div.dataTables_filter, ...@@ -104,7 +97,7 @@ div.dataTables_wrapper div.dataTables_filter,
show: false, show: false,
backdrop: 'static', backdrop: 'static',
keyboard: 'false', 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(){ $('#modal').on('show.bs.modal',function(){
......
...@@ -7,19 +7,11 @@ ...@@ -7,19 +7,11 @@
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<style> <style>
div.dataTables_wrapper div.dataTables_filter { .custom{
margin-left: 15px; /*float:left;*/
float: right !important; margin-right:5px;
} }
div.dataTables_wrapper div.dataTables_filter, #modal .modal-body { max-height: 200px; }
.dataTables_length {
float: right !important;
}
.custom{
/*float:left;*/
margin-right:5px;
}
#modal .modal-body { max-height: 200px; }
</style> </style>
{% endblock %} {% endblock %}
{% block content_left_head %}{% endblock %} {% block content_left_head %}{% endblock %}
...@@ -80,7 +72,8 @@ div.dataTables_wrapper div.dataTables_filter, ...@@ -80,7 +72,8 @@ div.dataTables_wrapper div.dataTables_filter,
</td> </td>
<td class="text-center"> <td class="text-center">
<a href="{% url 'assets:asset-update' pk=asset.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a> <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> </td>
</tr> </tr>
{% endfor %} {% endfor %}
...@@ -190,18 +183,18 @@ div.dataTables_wrapper div.dataTables_filter, ...@@ -190,18 +183,18 @@ div.dataTables_wrapper div.dataTables_filter,
}else{ }else{
$(this).addClass('selected'); $(this).addClass('selected');
this.children[0].children[0].checked=1; this.children[0].children[0].checked=1;
}; }
}); });
$('#btn_bulk_update').on('click',function(){ $('#btn_bulk_update').on('click',function(){
var column2 = table.rows('.selected').data(); var column2 = table.rows('.selected').data();
var id_list = []; var id_list = [];
var plain_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++){ for(var i=0;i<column2.length;i++){
id_list.push({id: column2[i].id,hostname:column2[i].ip}); id_list.push({id: column2[i].id,hostname:column2[i].ip});
plain_id_list.push(parseInt(column2[i].id)); plain_id_list.push(parseInt(column2[i].id));
}; }
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list); var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
//APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail}); //APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
...@@ -227,18 +220,18 @@ div.dataTables_wrapper div.dataTables_filter, ...@@ -227,18 +220,18 @@ div.dataTables_wrapper div.dataTables_filter,
} }
}); });
}); });
}; }
function doUpdate() { function doUpdate() {
// alert(plain_id_list); // alert(plain_id_list);
// $('#asset_bulk_update_modal').modal('show'); // $('#asset_bulk_update_modal').modal('show');
window.location.href="{% url 'assets:asset-modal-update' %}?plain_id_lists="+plain_id_list window.location.href="{% url 'assets:asset-modal-update' %}?plain_id_lists="+plain_id_list
}; }
var action = $('#slct_bulk_update option:selected').val(); var action = $('#slct_bulk_update option:selected').val();
if (id_list.length === 0) { if (id_list.length === 0) {
action = 'default'; action = 'default';
}; }
switch(action) { switch(action) {
case 'deactive': case 'deactive':
alert(action+"未完成"); alert(action+"未完成");
......
...@@ -46,9 +46,7 @@ ...@@ -46,9 +46,7 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function(){ $(document).ready(function(){
var table = $('#editable').DataTable({ var table = $('#editable').DataTable({
"aLengthMenu": [[10, 25, 50, -1], ["10", "25", "50", "all"]], "aLengthMenu": [[10, 25, 50, -1], ["10", "25", "50", "all"]],
"iDisplayLength":25, "iDisplayLength":25,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<body> <body>
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% 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" /> <input type="submit" value="Confirm" />
</form> </form>
</body> </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' %} {% extends '_base_list.html' %}
{% load i18n %} {% load i18n static %}
{% load common_tags %} {% block table_search %}{% endblock %}
{% block content_left_head %} {% block table_container %}
<a href="{% url 'assets:idc-create' %}" class="btn btn-sm btn-primary "> {% trans "Create IDC" %} </a> <div class="uc pull-left m-l-5 m-r-5">
{% endblock %} <a href="{% url "assets:idc-create" %}" class="btn btn-sm btn-primary"> {% trans "Create IDC" %} </a>
</div>
{% block table_head %} <table class="table table-striped table-bordered table-hover " id="idc_list_table" >
<th class="text-center"> <thead>
<input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')"> <tr>
</th> <th class="text-center">
<th class="text-center"><a href="{% url 'assets:idc-list' %}?sort=name">{% trans 'Name' %}</a></th> <input type="checkbox" id="check_all" class="ipt_check_all" >
<th class="text-center">{% trans 'Asset num' %}</th> </th>
<th class="text-center">{% trans 'Contact' %}</th> <th class="text-center"><a href="{% url 'assets:idc-list' %}?sort=name">{% trans 'Name' %}</a></th>
<th class="text-center">{% trans 'Phone' %}</th> <th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'operation' %}</th> <th class="text-center">{% trans 'Contact' %}</th>
{% endblock %} <th class="text-center">{% trans 'Phone' %}</th>
<th class="text-center">{% trans 'Operator' %}</th>
{% block table_body %} <th class="text-center">{% trans 'Action' %}</th>
{% for idc in idc_list %} </tr>
<tr class="gradeX"> </thead>
<td class="text-center"> <tbody>
<input type="checkbox" name="checked" value="{{ idc.id }}"> </tbody>
</td> </table>
<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 %}
{% endblock %} {% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <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> </script>
{% endblock %} {% 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): ...@@ -30,21 +30,21 @@ class AssetListView(AdminUserRequiredMixin, ListView):
@staticmethod @staticmethod
def sorted_by_valid_and_ip(asset): def sorted_by_valid_and_ip(asset):
ip_list = int_seq(asset.ip.split('.')) 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 return ip_list
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': 'Assets', 'app': 'Assets',
'action': 'asset list', '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) kwargs.update(context)
return super(AssetListView, self).get_context_data(**kwargs) return super(AssetListView, self).get_context_data(**kwargs)
class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView): class AssetCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, CreateView):
model = Asset model = Asset
tag_type = 'asset' tag_type = 'asset'
form_class = AssetCreateForm form_class = AssetCreateForm
...@@ -58,7 +58,8 @@ class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView): ...@@ -58,7 +58,8 @@ class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView):
return super(AssetCreateView, self).form_valid(form) return super(AssetCreateView, self).form_valid(form)
def form_invalid(self, 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) return super(AssetCreateView, self).form_invalid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
...@@ -207,7 +208,7 @@ class AssetModalListView(AdminUserRequiredMixin, ListView): ...@@ -207,7 +208,7 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
plain_id_lists = self.request.GET.get('plain_id_lists') plain_id_lists = self.request.GET.get('plain_id_lists')
self.s = self.request.GET.get('plain_id_lists') self.s = self.request.GET.get('plain_id_lists')
if "," in str(self.s): 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: else:
self.plain_id_lists = [self.s] self.plain_id_lists = [self.s]
...@@ -217,19 +218,19 @@ class AssetModalListView(AdminUserRequiredMixin, ListView): ...@@ -217,19 +218,19 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
else: else:
plain_id_lists = [int(self.s)] plain_id_lists = [int(self.s)]
context = { context = {
'all_assets':plain_id_lists 'all_assets' :plain_id_lists
} }
kwargs.update(context) kwargs.update(context)
if group_id: if group_id:
group = AssetGroup.objects.get(id=group_id) group = AssetGroup.objects.get(id=group_id)
context = { context = {
'all_assets':[x.id for x in group.assets.all()] 'all_assets': [x.id for x in group.assets.all()]
} }
kwargs.update(context) kwargs.update(context)
if tag_id: if tag_id:
tag = Tag.objects.get(id=tag_id) tag = Tag.objects.get(id=tag_id)
context = { 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) kwargs.update(context)
return super(AssetModalListView, self).get_context_data(**kwargs) return super(AssetModalListView, self).get_context_data(**kwargs)
...@@ -341,33 +342,33 @@ class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView): ...@@ -341,33 +342,33 @@ class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:asset-group-list') success_url = reverse_lazy('assets:asset-group-list')
class IDCListView(AdminUserRequiredMixin, ListView): class IDCListView(AdminUserRequiredMixin, TemplateView):
model = IDC # model = IDC
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE # paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'idc_list' # context_object_name = 'idc_list'
template_name = 'assets/idc_list.html' template_name = 'assets/idc_list.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('IDC list'), 'action': _('IDC list'),
'keyword': self.request.GET.get('keyword', '') # 'keyword': self.request.GET.get('keyword', '')
} }
kwargs.update(context) kwargs.update(context)
return super(IDCListView, self).get_context_data(**kwargs) return super(IDCListView, self).get_context_data(**kwargs)
def get_queryset(self): # def get_queryset(self):
self.queryset = super(IDCListView, self).get_queryset() # self.queryset = super(IDCListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '') # self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-date_created') # self.sort = sort = self.request.GET.get('sort', '-date_created')
#
if keyword: # if keyword:
self.queryset = self.queryset.filter(Q(name__icontains=keyword) | # self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
Q(comment__icontains=keyword)) # Q(comment__icontains=keyword))
#
if sort: # if sort:
self.queryset = self.queryset.order_by(sort) # self.queryset = self.queryset.order_by(sort)
return self.queryset # return self.queryset
class IDCCreateView(AdminUserRequiredMixin, CreateView): class IDCCreateView(AdminUserRequiredMixin, CreateView):
...@@ -414,7 +415,15 @@ class IDCUpdateView(AdminUserRequiredMixin, UpdateView): ...@@ -414,7 +415,15 @@ class IDCUpdateView(AdminUserRequiredMixin, UpdateView):
class IDCDetailView(AdminUserRequiredMixin, DetailView): 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): class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
...@@ -423,34 +432,34 @@ class IDCDeleteView(AdminUserRequiredMixin, DeleteView): ...@@ -423,34 +432,34 @@ class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:idc-list') success_url = reverse_lazy('assets:idc-list')
class AdminUserListView(AdminUserRequiredMixin, ListView): class AdminUserListView(AdminUserRequiredMixin, TemplateView):
model = AdminUser model = AdminUser
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE # paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
context_object_name = 'admin_user_list' # context_object_name = 'admin_user_list'
template_name = 'assets/admin_user_list.html' template_name = 'assets/admin_user_list.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Admin user list'), 'action': _('Admin user list'),
'keyword': self.request.GET.get('keyword', '') # 'keyword': self.request.GET.get('keyword', '')
} }
kwargs.update(context) kwargs.update(context)
return super(AdminUserListView, self).get_context_data(**kwargs) return super(AdminUserListView, self).get_context_data(**kwargs)
def get_queryset(self): # def get_queryset(self):
# Todo: Default order by lose asset connection num # Todo: Default order by lose asset connection num
self.queryset = super(AdminUserListView, self).get_queryset() # self.queryset = super(AdminUserListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '') # self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-date_created') # self.sort = sort = self.request.GET.get('sort', '-date_created')
#
if keyword: # if keyword:
self.queryset = self.queryset.filter(Q(name__icontains=keyword) | # self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
Q(comment__icontains=keyword)) # Q(comment__icontains=keyword))
#
if sort: # if sort:
self.queryset = self.queryset.order_by(sort) # self.queryset = self.queryset.order_by(sort)
return self.queryset # return self.queryset
class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
...@@ -475,6 +484,9 @@ class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie ...@@ -475,6 +484,9 @@ class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
)) ))
return success_message return success_message
def form_invalid(self, form):
return super(AdminUserCreateView, self).form_invalid(form)
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView): class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser model = AdminUser
......
...@@ -3,14 +3,14 @@ ...@@ -3,14 +3,14 @@
from __future__ import absolute_import, unicode_literals 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 rest_framework.views import APIView, Response
from . import models, serializers from . import models, serializers
from .hands import IsSuperUserOrTerminalUser, Terminal from .hands import IsSuperUserOrTerminalUser, Terminal
class ProxyLogListCreateApi(generics.ListCreateAPIView): class ProxyLogViewSet(viewsets.ModelViewSet):
"""User proxy to backend server need call this api. """User proxy to backend server need call this api.
params: { params: {
...@@ -34,18 +34,8 @@ class ProxyLogListCreateApi(generics.ListCreateAPIView): ...@@ -34,18 +34,8 @@ class ProxyLogListCreateApi(generics.ListCreateAPIView):
serializer_class = serializers.ProxyLogSerializer serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,) permission_classes = (IsSuperUserOrTerminalUser,)
def perform_create(self, serializer):
# Todo: May be save log_file
super(ProxyLogListCreateApi, self).perform_create(serializer)
class CommandLogViewSet(viewsets.ModelViewSet):
class ProxyLogDetailApi(generics.RetrieveUpdateDestroyAPIView):
queryset = models.ProxyLog.objects.all()
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
class CommandLogListCreateApi(generics.ListCreateAPIView):
queryset = models.CommandLog.objects.all() queryset = models.CommandLog.objects.all()
serializer_class = serializers.CommandLogSerializer serializer_class = serializers.CommandLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,) permission_classes = (IsSuperUserOrTerminalUser,)
......
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
# #
from users.utils import AdminUserRequiredMixin
from users.models import User from users.models import User
from assets.models import Asset, SystemUser from assets.models import Asset, SystemUser
from users.backends import IsSuperUserOrTerminalUser from users.backends import IsSuperUserOrTerminalUser
......
...@@ -22,9 +22,7 @@ class LoginLog(models.Model): ...@@ -22,9 +22,7 @@ class LoginLog(models.Model):
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip')) login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
login_city = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Login city')) 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')) 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_login = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
date_logout = models.DateTimeField(null=True, verbose_name=_('Date logout'))
class Meta: class Meta:
db_table = 'login_log' 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 from django.conf.urls import url
from .. import views
import api
import views
app_name = 'audits' app_name = 'audits'
...@@ -11,11 +8,7 @@ urlpatterns = [ ...@@ -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+)$', 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'^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'^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 ~*~ # ~*~ 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 ...@@ -10,13 +10,13 @@ from django.urls import reverse_lazy
from django.conf import settings from django.conf import settings
from django.db.models import Q from django.db.models import Q
from .models import ProxyLog, CommandLog from .models import ProxyLog, CommandLog, LoginLog
from .utils import AdminUserRequiredMixin from .hands import User, Asset, SystemUser, AdminUserRequiredMixin
from .hands import User, Asset, SystemUser
seven_days_ago_s = (datetime.datetime.now()-datetime.timedelta(7)).strftime('%m/%d/%Y') date_now = timezone.localtime(timezone.now())
now_s = datetime.datetime.now().strftime('%m/%d/%Y') 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): class ProxyLogListView(AdminUserRequiredMixin, ListView):
...@@ -54,6 +54,7 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView): ...@@ -54,6 +54,7 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
return self.queryset return self.queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
print(self.date_to_s)
context = { context = {
'app': _('Audits'), 'app': _('Audits'),
'action': _('Proxy log list'), 'action': _('Proxy log list'),
...@@ -152,3 +153,43 @@ class CommandLogListView(AdminUserRequiredMixin, ListView): ...@@ -152,3 +153,43 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
} }
kwargs.update(context) kwargs.update(context)
return super(CommandLogListView, self).get_context_data(**kwargs) 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 ...@@ -3,4 +3,3 @@ from __future__ import absolute_import
# This will make sure the app is always imported when # This will make sure the app is always imported when
# Django starts so that shared_task will use this app. # Django starts so that shared_task will use this app.
from .celery import app as celery_app from .celery import app as celery_app
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
import os import os
from datetime import timedelta
from celery import Celery from celery import Celery
...@@ -16,6 +16,5 @@ app = Celery('jumpserver') ...@@ -16,6 +16,5 @@ app = Celery('jumpserver')
# Using a string here means the worker will not have to # Using a string here means the worker will not have to
# pickle the object when using Windows. # pickle the object when using Windows.
app.config_from_object('django.conf:settings') app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS]) app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
from __future__ import absolute_import from __future__ import absolute_import
from celery import shared_task # from celery import shared_task
from django.core.mail import send_mail from django.core.mail import send_mail
from django.conf import settings 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): def send_mail_async(*args, **kwargs):
""" Using celery to send email async """ Using celery to send email async
...@@ -26,9 +27,3 @@ def send_mail_async(*args, **kwargs): ...@@ -26,9 +27,3 @@ def send_mail_async(*args, **kwargs):
args = tuple(args) args = tuple(args)
send_mail(*args, **kwargs) 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 @@ ...@@ -3,11 +3,15 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from six import string_types from six import string_types
import os
from itertools import chain from itertools import chain
import string import string
import logging import logging
import datetime import datetime
import paramiko
import paramiko
import sshpubkeys
from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \ from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \
BadSignature, SignatureExpired BadSignature, SignatureExpired
from django.shortcuts import reverse as dj_reverse from django.shortcuts import reverse as dj_reverse
...@@ -15,6 +19,11 @@ from django.conf import settings ...@@ -15,6 +19,11 @@ from django.conf import settings
from django.core import signing from django.core import signing
from django.utils import timezone from django.utils import timezone
try:
import cStringIO as StringIO
except ImportError:
import StringIO
SECRET_KEY = settings.SECRET_KEY SECRET_KEY = settings.SECRET_KEY
...@@ -162,4 +171,88 @@ def timesince(dt, since='', default="just now"): ...@@ -162,4 +171,88 @@ def timesince(dt, since='', default="just now"):
return default 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() signer = Signer()
\ No newline at end of file
...@@ -2,6 +2,3 @@ from __future__ import absolute_import, unicode_literals ...@@ -2,6 +2,3 @@ from __future__ import absolute_import, unicode_literals
from django.shortcuts import render from django.shortcuts import render
from django.views.generic import TemplateView from django.views.generic import TemplateView
...@@ -14,6 +14,7 @@ import os ...@@ -14,6 +14,7 @@ import os
import sys import sys
from django.urls import reverse_lazy from django.urls import reverse_lazy
from datetime import timedelta
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 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' % { ...@@ -284,6 +285,31 @@ BROKER_URL = 'redis://%(password)s%(host)s:%(port)s/3' % {
} }
CELERY_RESULT_BACKEND = BROKER_URL 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 settings, more see https://django-simple-captcha.readthedocs.io/en/latest/advanced.html
CAPTCHA_IMAGE_SIZE = (75, 33) CAPTCHA_IMAGE_SIZE = (75, 33)
CAPTCHA_FOREGROUND_COLOR = '#001100' CAPTCHA_FOREGROUND_COLOR = '#001100'
......
...@@ -22,11 +22,16 @@ from django.views.generic.base import TemplateView ...@@ -22,11 +22,16 @@ from django.views.generic.base import TemplateView
urlpatterns = [ urlpatterns = [
url(r'^captcha/', include('captcha.urls')), url(r'^captcha/', include('captcha.urls')),
url(r'^$', TemplateView.as_view(template_name='base.html'), name='index'), url(r'^$', TemplateView.as_view(template_name='base.html'), name='index'),
url(r'^(api/)?users/', include('users.urls')), url(r'^users/', include('users.urls.views_urls', namespace='users')),
url(r'^(api/)?assets/', include('assets.urls')), url(r'^assets/', include('assets.urls.views_urls', namespace='assets')),
url(r'^(api/)?perms/', include('perms.urls')), url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
url(r'^(api/)?audits/', include('audits.urls')), url(r'^audits/', include('audits.urls.views_urls', namespace='audits')),
url(r'^(api/)?terminal/', include('terminal.urls')), 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 "" ...@@ -1610,7 +1610,7 @@ msgid ""
"here reset password</a>\n" "here reset password</a>\n"
" </br>\n" " </br>\n"
" This link is valid for 1 hour. After it expires, <a href=\"%" " 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" "\n"
" </br>\n" " </br>\n"
" ---\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 @@ ...@@ -2,49 +2,120 @@
# #
from rest_framework.views import APIView, Response 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 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 .models import AssetPermission
from .hands import AssetGrantedSerializer, User, UserGroup, AssetGroup, Asset, AssetGroup, AssetGroupSerializer
from . import serializers from . import serializers
class AssetPermissionListCreateApi(ListCreateAPIView): class AssetPermissionViewSet(viewsets.ModelViewSet):
queryset = AssetPermission.objects.all() queryset = AssetPermission.objects.all()
serializer_class = serializers.AssetPermissionSerializer serializer_class = serializers.AssetPermissionSerializer
permission_classes = (IsSuperUser,) 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): if user_id and user_id.isdigit():
permission_classes = (IsValidUser,) user = get_object_or_404(User, id=int(user_id))
queryset = get_user_asset_permissions(user)
def get(self, request, *args, **kwargs): if user_group_id:
assets_json = [] user_group = get_object_or_404(UserGroup, id=user_group_id)
user = request.user 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: 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({ class MyGrantedAssetsGroupsApi(APIView):
'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):
permission_classes = (IsValidUser,) permission_classes = (IsValidUser,)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
...@@ -56,46 +127,61 @@ class UserAssetsGroupsApi(APIView): ...@@ -56,46 +127,61 @@ class UserAssetsGroupsApi(APIView):
for asset in assets: for asset in assets:
for asset_group in asset.groups.all(): for asset_group in asset.groups.all():
if asset_group.id in asset_groups: if asset_group.id in asset_groups:
asset_groups[asset_group.id]['asset_num'] += 1 asset_groups[asset_group.id]['asset_amount'] += 1
else: else:
asset_groups[asset_group.id] = { asset_groups[asset_group.id] = {
'id': asset_group.id, 'id': asset_group.id,
'name': asset_group.name, 'name': asset_group.name,
'comment': asset_group.comment, 'comment': asset_group.comment,
'asset_num': 1 'assets_amount': 1
} }
asset_groups_json = asset_groups.values() asset_groups_json = asset_groups.values()
return Response(asset_groups_json, status=200) return Response(asset_groups_json, status=200)
class UserAssetsGroupAssetsApi(APIView): class MyAssetGroupAssetsApi(ListAPIView):
permission_classes = (IsValidUser,) permission_classes = (IsValidUser,)
serializer_class = AssetGrantedSerializer
def get(self, request, *args, **kwargs): def get_queryset(self):
# asset_group_id = request.query_params.get('asset_group_id', -1) queryset = []
asset_group_id = kwargs.get('pk', -1) asset_group_id = self.kwargs.get('pk', -1)
# asset_group_name = request.query_params.get('asset_group_name', '') user = self.request.user
user = request.user asset_group = get_object_or_none(AssetGroup, id=asset_group_id)
assets_json = []
if user: if user and asset_group:
assets = get_user_granted_assets(user) assets = get_user_granted_assets(user)
for asset, system_users in assets.items(): for asset in assets:
for asset_group in asset.groups.all(): if asset_group in asset.groups.all():
if str(asset_group.id) == asset_group_id: # and asset_group.name == asset_group_name: queryset.append(asset)
assets_json.append({ return queryset
'id': asset.id,
'hostname': asset.hostname,
'ip': asset.ip, class UserGroupGrantedAssetsApi(ListAPIView):
'port': asset.port, permission_classes = (IsSuperUser,)
'system_users': [ serializer_class = AssetGrantedSerializer
{
'id': system_user.id, def get_queryset(self):
'name': system_user.name, user_group_id = self.kwargs.get('pk', '')
'username': system_user.username,
} for system_user in system_users if user_group_id:
], user_group = get_object_or_404(UserGroup, id=user_group_id)
'comment': asset.comment queryset = get_user_group_granted_assets(user_group)
}) else:
return Response(assets_json, status=200) 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 @@ ...@@ -4,6 +4,7 @@
from users.utils import AdminUserRequiredMixin from users.utils import AdminUserRequiredMixin
from users.models import User, UserGroup from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser from assets.models import Asset, AssetGroup, SystemUser
from assets.serializers import AssetGrantedSerializer, AssetGroupSerializer
def associate_system_users_with_assets(system_users, assets, asset_groups): def associate_system_users_with_assets(system_users, assets, asset_groups):
......
# -*- coding: utf-8 -*- # -*- 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 .models import AssetPermission
from .hands import User
class AssetPermissionSerializer(serializers.ModelSerializer): class AssetPermissionSerializer(serializers.ModelSerializer):
# users_amount = serializers.SerializerMethodField()
# user_groups_amount = serializers.SerializerMethodField()
# assets_amount = serializers.SerializerMethodField()
# asset_groups_amount = serializers.SerializerMethodField()
class Meta: class Meta:
model = AssetPermission model = AssetPermission
fields = ['id', 'name', 'users', 'user_groups', 'assets', 'asset_groups',
'system_users', 'is_active', 'comment', 'date_expired']
class UserAssetPermissionSerializer(AssetPermissionSerializer):
# @staticmethod is_inherited = serializers.SerializerMethodField()
# def get_users_amount(obj):
# return obj.users.count() @staticmethod
# def get_is_inherited(obj):
# @staticmethod if getattr(obj, 'inherited', ''):
# def get_user_groups_amount(obj): return True
# return obj.user_groups.count() else:
# return False
# @staticmethod
# def get_assets_amount(obj):
# return obj.assets.count()
#
# @staticmethod
# def get_asset_groups_amount(obj):
# return obj.asset_groups.count()
# 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 # coding:utf-8
from django.conf.urls import url from django.conf.urls import url
import views from .. import views
import api
app_name = 'perms' app_name = 'perms'
...@@ -21,14 +20,4 @@ urlpatterns = [ ...@@ -21,14 +20,4 @@ urlpatterns = [
name='asset-permission-asset-list'), 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 __future__ import absolute_import, unicode_literals
from common.utils import setattr_bulk
from .hands import User, UserGroup, Asset, AssetGroup, SystemUser from .hands import User, UserGroup, Asset, AssetGroup, SystemUser
...@@ -7,7 +8,7 @@ def get_user_group_granted_asset_groups(user_group): ...@@ -7,7 +8,7 @@ def get_user_group_granted_asset_groups(user_group):
"""Return asset groups granted of the user group """Return asset groups granted of the user group
:param user_group: Instance of :class: ``UserGroup`` :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_groups = {}
asset_permissions = user_group.asset_permissions.all() asset_permissions = user_group.asset_permissions.all()
...@@ -20,7 +21,6 @@ def get_user_group_granted_asset_groups(user_group): ...@@ -20,7 +21,6 @@ def get_user_group_granted_asset_groups(user_group):
asset_groups[asset_group] |= set(asset_permission.system_users.all()) asset_groups[asset_group] |= set(asset_permission.system_users.all())
else: else:
asset_groups[asset_group] = set(asset_permission.system_users.all()) asset_groups[asset_group] = set(asset_permission.system_users.all())
return asset_groups return asset_groups
...@@ -41,7 +41,6 @@ def get_user_group_granted_assets(user_group): ...@@ -41,7 +41,6 @@ def get_user_group_granted_assets(user_group):
assets[asset] |= set(asset_permission.system_users.all()) assets[asset] |= set(asset_permission.system_users.all())
else: else:
assets[asset] = set(asset_permission.system_users.all()) assets[asset] = set(asset_permission.system_users.all())
return assets return assets
...@@ -61,7 +60,7 @@ def get_user_granted_asset_groups_direct(user): ...@@ -61,7 +60,7 @@ def get_user_granted_asset_groups_direct(user):
if asset_group in asset_groups: if asset_group in asset_groups:
asset_groups[asset_group] |= set(asset_permission.system_users.all()) asset_groups[asset_group] |= set(asset_permission.system_users.all())
else: 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()) asset_groups[asset_group] = set(asset_permission.system_users.all())
return asset_groups return asset_groups
...@@ -89,7 +88,7 @@ def get_user_granted_asset_groups_inherit_from_user_groups(user): ...@@ -89,7 +88,7 @@ def get_user_granted_asset_groups_inherit_from_user_groups(user):
if asset_group in asset_groups: if asset_group in asset_groups:
asset_groups[asset_group] |= set(asset_permission.system_users.all()) asset_groups[asset_group] |= set(asset_permission.system_users.all())
else: 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()) asset_groups[asset_group] = set(asset_permission.system_users.all())
return asset_groups return asset_groups
...@@ -112,7 +111,6 @@ def get_user_granted_asset_groups(user): ...@@ -112,7 +111,6 @@ def get_user_granted_asset_groups(user):
asset_groups[asset_group] |= asset_groups_direct[asset_group] asset_groups[asset_group] |= asset_groups_direct[asset_group]
else: else:
asset_groups[asset_group] = asset_groups_direct[asset_group] asset_groups[asset_group] = asset_groups_direct[asset_group]
return asset_groups return asset_groups
...@@ -132,10 +130,8 @@ def get_user_granted_assets_direct(user): ...@@ -132,10 +130,8 @@ def get_user_granted_assets_direct(user):
if asset in assets: if asset in assets:
assets[asset] |= set(asset_permission.system_users.all()) assets[asset] |= set(asset_permission.system_users.all())
else: else:
setattr(asset, 'is_inherit_from_user_groups', False) setattr(asset, 'inherited', False)
setattr(asset, 'is_inherit_from_user_groups', False)
assets[asset] = set(asset_permission.system_users.all()) assets[asset] = set(asset_permission.system_users.all())
return assets return assets
...@@ -154,7 +150,7 @@ def get_user_granted_assets_inherit_from_user_groups(user): ...@@ -154,7 +150,7 @@ def get_user_granted_assets_inherit_from_user_groups(user):
if asset in assets: if asset in assets:
assets[asset] |= assets_inherited[asset] assets[asset] |= assets_inherited[asset]
else: else:
setattr(asset, 'is_inherit_from_user_groups', True) setattr(asset, 'inherited', True)
assets[asset] = assets_inherited[asset] assets[asset] = assets_inherited[asset]
return assets return assets
...@@ -175,10 +171,25 @@ def get_user_granted_assets(user): ...@@ -175,10 +171,25 @@ def get_user_granted_assets(user):
assets[asset] |= assets_direct[asset] assets[asset] |= assets_direct[asset]
else: else:
assets[asset] = assets_direct[asset] assets[asset] = assets_direct[asset]
return assets 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): def get_user_groups_granted_in_asset(asset):
pass pass
......
...@@ -255,3 +255,12 @@ table.dataTable tbody td.selected td i.text-navy ...@@ -255,3 +255,12 @@ table.dataTable tbody td.selected td i.text-navy
font-size: 12px; font-size: 12px;
vertical-align: middle; 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 { ...@@ -1585,9 +1585,9 @@ table.dataTable thead .sorting_desc_disabled {
.dataTables_wrapper { .dataTables_wrapper {
padding-bottom: 30px; padding-bottom: 30px;
} }
.dataTables_length { /*.dataTables_length {*/
float: left; /*float: left;*/
} /*}*/
.dataTables_filter label { .dataTables_filter label {
margin-right: 5px; margin-right: 5px;
} }
......
...@@ -189,9 +189,8 @@ function activeNav() { ...@@ -189,9 +189,8 @@ function activeNav() {
function APIUpdateAttr(props) { function APIUpdateAttr(props) {
// props = {url: .., body: , success: , error: , method: ,} // props = {url: .., body: , success: , error: , method: ,}
props = props || {}; props = props || {};
success_message = props.success_message || 'Update Successfully!'; var success_message = props.success_message || 'Update Successfully!';
fail_message = props.fail_message || 'Error occurred while updating.'; var fail_message = props.fail_message || 'Error occurred while updating.';
$.ajax({ $.ajax({
url: props.url, url: props.url,
type: props.method || "PATCH", type: props.method || "PATCH",
...@@ -199,19 +198,18 @@ function APIUpdateAttr(props) { ...@@ -199,19 +198,18 @@ function APIUpdateAttr(props) {
contentType: props.content_type || "application/json; charset=utf-8", contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json" dataType: props.data_type || "json"
}).done(function(data, textStatue, jqXHR) { }).done(function(data, textStatue, jqXHR) {
toastr.success(success_message);
if (typeof props.success === 'function') { if (typeof props.success === 'function') {
return props.success(data); return props.success(data);
} else { }
toastr.success(success_message);
}
}).fail(function(jqXHR, textStatue, errorThrown) { }).fail(function(jqXHR, textStatue, errorThrown) {
toastr.error(fail_message);
if (typeof props.error === 'function') { if (typeof props.error === 'function') {
return props.error(errorThrown); return props.error(errorThrown);
} else { }
toastr.error(fail_message);
}
}); });
return true; // return true;
} }
// Sweet Alert for Delete // Sweet Alert for Delete
...@@ -267,6 +265,7 @@ $.fn.serializeObject = function() ...@@ -267,6 +265,7 @@ $.fn.serializeObject = function()
}; };
var jumpserver = {}; var jumpserver = {};
jumpserver.checked = false; jumpserver.checked = false;
jumpserver.selected = {};
jumpserver.initDataTable = function (options) { jumpserver.initDataTable = function (options) {
// options = { // options = {
// ele *: $('#dataTable_id'), // ele *: $('#dataTable_id'),
...@@ -285,59 +284,37 @@ jumpserver.initDataTable = function (options) { ...@@ -285,59 +284,37 @@ jumpserver.initDataTable = function (options) {
{ {
targets: 0, targets: 0,
orderable: false, orderable: false,
createdCell: function(td) { createdCell: function(td, cellData) {
$(td).html('<input type="checkbox" class="ipt_check">'); $(td).html('<input type="checkbox" class="text-center ipt_check" id=99991937>'.replace('99991937', cellData));
} }},
},
{className: 'text-center', targets: '_all'} {className: 'text-center', targets: '_all'}
]; ];
columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs; columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs;
var table = ele.DataTable({ 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>>', dom: options.dom || '<"#uc.pull-left"><"html5buttons"B>flti<"row m-t"<"#op.col-md-6"><"col-md-6"p>>',
language: { language: {
url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json" url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json"
}, },
order: options.order || [[ 1, 'asc' ]], order: options.order || [[ 1, 'asc' ]],
buttons: options.buttons || [ select: options.select || 'multi',
{extend: 'excel', buttons: [],
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');
}
}
],
columnDefs: columnDefs, columnDefs: columnDefs,
select: options.select || {style: 'multi'},
ajax: { ajax: {
url: options.ajax_url , url: options.ajax_url ,
dataSrc: "" dataSrc: ""
}, },
columns: options.columns || [] columns: options.columns || [],
lengthMenu: [[15, 25, 50, -1], [15, 25, 50, "All"]]
}); });
table.on('select', function(e, dt, type, indexes) { table.on('select', function(e, dt, type, indexes) {
var $node = table[ type ]( indexes ).nodes().to$(); var $node = table[ type ]( indexes ).nodes().to$();
$node.find('input.ipt_check').prop('checked', true); $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) { }).on('deselect', function(e, dt, type, indexes) {
var $node = table[ type ]( indexes ).nodes().to$(); var $node = table[ type ]( indexes ).nodes().to$();
$node.find('input.ipt_check').prop('checked', false); $node.find('input.ipt_check').prop('checked', false);
jumpserver.selected[$node.find('input.ipt_check').prop('id')] = false
}).on('draw', function(){ }).on('draw', function(){
$('#op').html(options.op_html || ''); $('#op').html(options.op_html || '');
$('#uc').html(options.uc_html || ''); $('#uc').html(options.uc_html || '');
...@@ -353,5 +330,6 @@ jumpserver.initDataTable = function (options) { ...@@ -353,5 +330,6 @@ jumpserver.initDataTable = function (options) {
table.rows().deselect(); table.rows().deselect();
} }
}); });
return table; return table;
}; };
...@@ -28,10 +28,17 @@ ...@@ -28,10 +28,17 @@
</div> </div>
</div> </div>
<div class="ibox-content"> <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> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
<div class="footer fixed"> <div class="footer fixed">
<div class="pull-right"> <div class="pull-right">
Version <strong>0.3.3</strong> GPL. Version <strong>0.4.0</strong> GPL.
</div> </div>
<div> <div>
<strong>Copyright</strong> Jumpserver.org Team &copy; 2014-2016 <strong>Copyright</strong> Jumpserver.org Team &copy; 2014-2016
......
...@@ -48,10 +48,10 @@ ...@@ -48,10 +48,10 @@
<li id="command-log"> <li id="command-log">
<a href="{% url 'audits:command-log-list' %}">{% trans 'Command log' %}</a> <a href="{% url 'audits:command-log-list' %}">{% trans 'Command log' %}</a>
</li> </li>
<li id="login"> <li id="login-log">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Login log' %}</a> <a href="{% url 'audits:login-log-list' %}">{% trans 'Login log' %}</a>
</li> </li>
<li id="admin"> <li id="admin-log">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Admin log' %}</a> <a href="{% url 'perms:asset-permission-list' %}">{% trans 'Admin log' %}</a>
</li> </li>
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.core.cache import cache
from django.conf import settings
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework import viewsets
from rest_framework.views import APIView, Response from rest_framework.views import APIView, Response
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
...@@ -11,42 +14,51 @@ from .serializers import TerminalSerializer, TerminalHeatbeatSerializer ...@@ -11,42 +14,51 @@ from .serializers import TerminalSerializer, TerminalHeatbeatSerializer
from .hands import IsSuperUserOrTerminalUser from .hands import IsSuperUserOrTerminalUser
class TerminalCreateListApi(ListCreateAPIView): class TerminalViewSet(viewsets.ModelViewSet):
queryset = Terminal.objects.all() queryset = Terminal.objects.all()
serializer_class = TerminalSerializer serializer_class = TerminalSerializer
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
name = signer.unsign(request.data.get('name', '')) name = signer.unsign(request.data.get('name', ''))
if name: if name:
terminal = get_object_or_none(Terminal, name=name) terminal = get_object_or_none(Terminal, name=name)
if terminal: if terminal:
data = {
'data': {'name': name, 'id': terminal.id},
}
if terminal.is_active: if terminal.is_active:
return Response(data={'data': {'name': name, 'id': terminal.id}, data['msg'] = 'Success'
'msg': 'Success'}, return Response(data=data, status=200)
status=200)
else: else:
return Response(data={'data': {'name': name, 'ip': terminal.ip}, data['msg'] = 'Need admin active this terminal'
'msg': 'Need admin active it'}, return Response(data=data, status=203)
status=203)
else: else:
ip = request.META.get('X-Real-IP') or request.META.get('REMOTE_ADDR') ip = request.META.get('X-Real-IP') or request.META.get('REMOTE_ADDR')
terminal = Terminal.objects.create(name=name, ip=ip) terminal = Terminal.objects.create(name=name, ip=ip)
return Response(data={'data': {'name': name, 'ip': terminal.ip}, data = {
'msg': 'Need admin active it'}, 'data': {'name': name, 'id': terminal.id},
status=201) 'msg': 'Need admin active this terminal',
}
return Response(data=data, status=201)
else: else:
return Response(data={'msg': 'Secrete key invalid'}, status=401) return Response(data={'msg': 'Secrete key invalid'}, status=401)
class TerminalHeatbeatApi(ListCreateAPIView): class TerminalHeatbeatApi(ListCreateAPIView):
model = TerminalHeatbeat queryset = TerminalHeatbeat.objects.all()
serializer_class = TerminalHeatbeatSerializer serializer_class = TerminalHeatbeatSerializer
permission_classes = (IsSuperUserOrTerminalUser,) permission_classes = (IsSuperUserOrTerminalUser,)
class TerminalApiDetailUpdateDetailApi(RetrieveUpdateDestroyAPIView): class TerminalHeatbeatViewSet(viewsets.ModelViewSet):
queryset = Terminal.objects.all() queryset = TerminalHeatbeat.objects.all()
serializer_class = TerminalSerializer serializer_class = TerminalHeatbeatSerializer
permission_classes = (IsSuperUserOrTerminalUser,) permission_classes = (IsSuperUserOrTerminalUser,)
def create(self, request, *args, **kwargs):
terminal = request.user
TerminalHeatbeat.objects.create(terminal=terminal)
return Response({'msg': 'Success'})
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.utils import timezone
from rest_framework import serializers from rest_framework import serializers
from .models import Terminal, TerminalHeatbeat from .models import Terminal, TerminalHeatbeat
...@@ -8,22 +9,30 @@ from .hands import ProxyLog ...@@ -8,22 +9,30 @@ from .hands import ProxyLog
class TerminalSerializer(serializers.ModelSerializer): class TerminalSerializer(serializers.ModelSerializer):
proxy_amount = serializers.SerializerMethodField() proxy_online = serializers.SerializerMethodField()
is_alive = serializers.SerializerMethodField()
class Meta: class Meta:
model = Terminal model = Terminal
fields = ['id', 'name', 'ip', 'type', 'url', 'comment', 'is_active', fields = ['id', 'name', 'ip', 'type', 'url', 'comment',
'get_type_display', 'proxy_amount'] 'is_active', 'get_type_display', 'proxy_online', 'is_alive']
@staticmethod @staticmethod
def get_proxy_amount(obj): def get_proxy_online(obj):
return ProxyLog.objects.filter(terminal=obj.name, is_finished=False).count() 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 TerminalHeatbeatSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = TerminalHeatbeat model = TerminalHeatbeat
fields = ['terminal']
if __name__ == '__main__': if __name__ == '__main__':
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
...@@ -27,8 +27,9 @@ ...@@ -27,8 +27,9 @@
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'IP' %}</th> <th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Type' %}</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 'Active' %}</th>
<th class="text-center">{% trans 'Alive' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
</thead> </thead>
...@@ -42,6 +43,7 @@ ...@@ -42,6 +43,7 @@
$(document).ready(function(){ $(document).ready(function(){
var options = { var options = {
ele: $('#terminal_list_table'), ele: $('#terminal_list_table'),
buttons: [],
columnDefs: [ columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) { {targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "users:user-detail" pk=99991937 %}">' + cellData + '</a>'; var detail_btn = '<a href="{% url "users:user-detail" pk=99991937 %}">' + cellData + '</a>';
...@@ -54,7 +56,14 @@ $(document).ready(function(){ ...@@ -54,7 +56,14 @@ $(document).ready(function(){
$(td).html('<i class="fa fa-check text-navy"></i>') $(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); console.log(rowData.name);
var update_btn = '<a href="{% url "terminal:terminal-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>' var update_btn = '<a href="{% url "terminal:terminal-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.replace('99991937', cellData); .replace('99991937', cellData);
...@@ -64,9 +73,9 @@ $(document).ready(function(){ ...@@ -64,9 +73,9 @@ $(document).ready(function(){
$(td).html(update_btn + delete_btn) $(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" }, 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() op_html: $('#actions').html()
}; };
jumpserver.initDataTable(options); jumpserver.initDataTable(options);
...@@ -74,7 +83,7 @@ $(document).ready(function(){ ...@@ -74,7 +83,7 @@ $(document).ready(function(){
var $this = $(this); var $this = $(this);
var uid = $this.data('uid'); var uid = $this.data('uid');
var name = $(this).data('name'); 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) objectDelete($this, name, the_url)
}) })
</script> </script>
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# #
from django.conf.urls import url from django.conf.urls import url
from rest_framework import routers
import views import views
import api import api
...@@ -14,9 +15,11 @@ urlpatterns = [ ...@@ -14,9 +15,11 @@ urlpatterns = [
url(r'^terminal/(?P<pk>\d+)/update$', views.TerminalUpdateView.as_view(), name='terminal-update'), url(r'^terminal/(?P<pk>\d+)/update$', views.TerminalUpdateView.as_view(), name='terminal-update'),
] ]
urlpatterns += [ router = routers.DefaultRouter()
url(r'^v1/terminal/$', api.TerminalCreateListApi.as_view(), name='terminal-list-create-api'), router.register(r'v1/terminal/heatbeat', api.TerminalHeatbeatViewSet, 'terminal-heatbeat')
url(r'^v1/terminal/(?P<pk>\d+)/$', api.TerminalApiDetailUpdateDetailApi.as_view(), router.register(r'v1/terminal', api.TerminalViewSet, 'terminal')
name='terminal-detail-update-delete-api'), # urlpatterns += [
url(r'^v1/terminal-heatbeat/$', api.TerminalHeatbeatApi.as_view(), name='terminal-heatbeat-api'), # 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 ...@@ -6,120 +6,130 @@ import base64
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.core.cache import cache from django.core.cache import cache
from django.conf import settings 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.response import Response
from rest_framework.views import APIView 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 rest_framework import authentication
from common.mixins import BulkDeleteApiMixin from common.mixins import BulkDeleteApiMixin
from common.utils import get_logger from common.utils import get_logger
from .utils import check_user_valid, token_gen from .utils import check_user_valid, token_gen
from .models import User, UserGroup from .models import User, UserGroup
from .serializers import UserDetailSerializer, UserAndGroupSerializer, \ from .hands import write_login_log_async
GroupDetailSerializer, UserPKUpdateSerializer, UserBulkUpdateSerializer, GroupBulkUpdateSerializer
from .backends import IsSuperUser, IsTerminalUser, IsValidUser, IsSuperUserOrTerminalUser from .backends import IsSuperUser, IsTerminalUser, IsValidUser, IsSuperUserOrTerminalUser
from . import serializers
logger = get_logger(__name__) logger = get_logger(__name__)
class UserDetailApi(generics.RetrieveUpdateDestroyAPIView): class UserViewSet(BulkModelViewSet):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserDetailSerializer serializer_class = serializers.UserSerializer
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
class UserAndGroupEditApi(generics.RetrieveUpdateAPIView): class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserAndGroupSerializer serializer_class = serializers.UserUpdateGroupSerializer
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
class UserResetPasswordApi(generics.UpdateAPIView): class UserResetPasswordApi(generics.UpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserDetailSerializer serializer_class = serializers.UserSerializer
def perform_update(self, serializer): def perform_update(self, serializer):
# Note: we are not updating the user object here. # Note: we are not updating the user object here.
# We just do the reset-password staff. # We just do the reset-password staff.
user = self.get_object()
import uuid import uuid
from .utils import send_reset_password_mail
user = self.get_object()
user.password_raw = str(uuid.uuid4()) user.password_raw = str(uuid.uuid4())
user.save() user.save()
from .utils import send_reset_password_mail
send_reset_password_mail(user) send_reset_password_mail(user)
class UserResetPKApi(generics.UpdateAPIView): class UserResetPKApi(generics.UpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserDetailSerializer serializer_class = serializers.UserSerializer
def perform_update(self, serializer): def perform_update(self, serializer):
from .utils import send_reset_ssh_key_mail
user = self.get_object() user = self.get_object()
user.is_public_key_valid = False user.is_public_key_valid = False
user.save() user.save()
from .utils import send_reset_ssh_key_mail
send_reset_ssh_key_mail(user) send_reset_ssh_key_mail(user)
class UserUpdatePKApi(generics.UpdateAPIView): class UserUpdatePKApi(generics.UpdateAPIView):
queryset = User.objects.all() queryset = User.objects.all()
serializer_class = UserPKUpdateSerializer serializer_class = serializers.UserPKUpdateSerializer
def perform_update(self, serializer): def perform_update(self, serializer):
user = self.get_object() user = self.get_object()
user.private_key = serializer.validated_data['_public_key'] user.public_key = serializer.validated_data['_public_key']
user.save() user.save()
class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView): class UserGroupViewSet(viewsets.ModelViewSet):
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):
queryset = UserGroup.objects.all() queryset = UserGroup.objects.all()
serializer_class = GroupBulkUpdateSerializer serializer_class = serializers.UserGroupSerializer
class DeleteUserFromGroupApi(generics.DestroyAPIView): class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
queryset = UserGroup.objects.all() queryset = UserGroup.objects.all()
serializer_class = GroupDetailSerializer serializer_class = serializers.UserGroupUpdateMemeberSerializer
permission_classes = (IsSuperUser,)
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): # class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView):
user_id = kwargs.get('uid') # queryset = UserGroup.objects.all()
user = get_object_or_404(User, id=user_id) # serializer_class = serializers.GroupDetailSerializer
instance.users.remove(user) #
# 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 = () permission_classes = ()
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600 expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
...@@ -127,10 +137,11 @@ class UserTokenApi(APIView): ...@@ -127,10 +137,11 @@ class UserTokenApi(APIView):
username = request.data.get('username', '') username = request.data.get('username', '')
password = request.data.get('password', '') password = request.data.get('password', '')
public_key = request.data.get('public_key', '') public_key = request.data.get('public_key', '')
remote_addr = request.META.get('REMOTE_ADDR', '') remote_addr = request.data.get('remote_addr', '')
terminal = request.data.get('terminal', '')
remote_addr = base64.b64encode(remote_addr).replace('=', '') login_type = request.data.get('login_type', 'T')
user = check_user_valid(username=username, password=password, public_key=public_key) user = check_user_valid(username=username, password=password, public_key=public_key)
if user: if user:
token = cache.get('%s_%s' % (user.id, remote_addr)) token = cache.get('%s_%s' % (user.id, remote_addr))
if not token: if not token:
...@@ -138,6 +149,8 @@ class UserTokenApi(APIView): ...@@ -138,6 +149,8 @@ class UserTokenApi(APIView):
cache.set(token, user.id, self.expiration) cache.set(token, user.id, self.expiration)
cache.set('%s_%s' % (user.id, remote_addr), token, 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}) return Response({'token': token, 'id': user.id, 'username': user.username, 'name': user.name})
else: 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): ...@@ -78,6 +78,7 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
def authenticate_credentials(self, token, request): def authenticate_credentials(self, token, request):
user_id = cache.get(token) user_id = cache.get(token)
print('Auth id: %s' % user_id)
user = get_object_or_none(User, id=user_id) user = get_object_or_none(User, id=user_id)
if not user: if not user:
...@@ -87,7 +88,6 @@ class AccessTokenAuthentication(authentication.BaseAuthentication): ...@@ -87,7 +88,6 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
remote_addr = base64.b16encode(remote_addr).replace('=', '') remote_addr = base64.b16encode(remote_addr).replace('=', '')
cache.set(token, user_id, self.expiration) cache.set(token, user_id, self.expiration)
cache.set('%s_%s' % (user.id, remote_addr), token, self.expiration) cache.set('%s_%s' % (user.id, remote_addr), token, self.expiration)
return user, None return user, None
......
...@@ -5,8 +5,9 @@ from django.contrib.auth.forms import AuthenticationForm ...@@ -5,8 +5,9 @@ from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from captcha.fields import CaptchaField from captcha.fields import CaptchaField
from common.utils import validate_ssh_public_key
from perms.models import AssetPermission
from .models import User, UserGroup from .models import User, UserGroup
from .hands import AssetPermission
class UserLoginForm(AuthenticationForm): class UserLoginForm(AuthenticationForm):
...@@ -17,8 +18,7 @@ class UserLoginForm(AuthenticationForm): ...@@ -17,8 +18,7 @@ class UserLoginForm(AuthenticationForm):
captcha = CaptchaField() captcha = CaptchaField()
class UserCreateForm(forms.ModelForm): class UserCreateUpdateForm(forms.ModelForm):
class Meta: class Meta:
model = User model = User
fields = [ fields = [
...@@ -36,32 +36,30 @@ class UserCreateForm(forms.ModelForm): ...@@ -36,32 +36,30 @@ class UserCreateForm(forms.ModelForm):
class UserBulkImportForm(forms.ModelForm): class UserBulkImportForm(forms.ModelForm):
class Meta: class Meta:
model = User model = User
fields = ['username', 'email', 'enable_otp', 'role'] fields = ['username', 'email', 'enable_otp', 'role']
class UserUpdateForm(forms.ModelForm): # class UserUpdateForm(forms.ModelForm):
#
class Meta: # class Meta:
model = User # model = User
fields = [ # fields = [
'name', 'email', 'groups', 'wechat', # 'name', 'email', 'groups', 'wechat',
'phone', 'enable_otp', 'role', 'date_expired', 'comment', # 'phone', 'enable_otp', 'role', 'date_expired', 'comment',
] # ]
help_texts = { # help_texts = {
'username': '* required', # 'username': '* required',
'email': '* required', # 'email': '* required',
'groups': '* required' # 'groups': '* required'
} # }
widgets = { # widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}), # 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}),
} # }
class UserGroupForm(forms.ModelForm): class UserGroupForm(forms.ModelForm):
class Meta: class Meta:
model = UserGroup model = UserGroup
fields = [ fields = [
...@@ -84,22 +82,14 @@ class UserKeyForm(forms.Form): ...@@ -84,22 +82,14 @@ class UserKeyForm(forms.Form):
public_key = forms.CharField( public_key = forms.CharField(
label=_('ssh public key'), max_length=5000, label=_('ssh public key'), max_length=5000,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), 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): def clean_public_key(self):
public_key = self.cleaned_data['public_key'] 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.')) raise forms.ValidationError(_('Public key should not be the same as your old one.'))
from sshpubkeys import SSHKey
from sshpubkeys.exceptions import InvalidKeyException if not validate_ssh_public_key(public_key):
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
raise forms.ValidationError(_('Not a valid ssh public key')) raise forms.ValidationError(_('Not a valid ssh public key'))
return public_key return public_key
...@@ -108,7 +98,6 @@ class UserPrivateAssetPermissionForm(forms.ModelForm): ...@@ -108,7 +98,6 @@ class UserPrivateAssetPermissionForm(forms.ModelForm):
def save(self, commit=True): def save(self, commit=True):
self.instance = super(UserPrivateAssetPermissionForm, self).save(commit=commit) self.instance = super(UserPrivateAssetPermissionForm, self).save(commit=commit)
# self.instance.private_for = 'U'
self.instance.users = [self.user] self.instance.users = [self.user]
self.instance.save() self.instance.save()
return self.instance return self.instance
...@@ -126,3 +115,30 @@ class UserPrivateAssetPermissionForm(forms.ModelForm): ...@@ -126,3 +115,30 @@ class UserPrivateAssetPermissionForm(forms.ModelForm):
'system_users': forms.SelectMultiple(attrs={'class': 'select2', 'system_users': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select system users')}), '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 @@ ...@@ -11,5 +11,6 @@
""" """
from terminal.models import Terminal from terminal.models import Terminal
from perms.models import AssetPermission from audits.tasks import write_login_log_async
from perms.utils import get_user_granted_assets, get_user_granted_asset_groups # from perms.models import AssetPermission
# from perms.utils import get_user_granted_assets, get_user_granted_asset_groups
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.contrib.auth import logout
from django.contrib.auth.hashers import make_password from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import AbstractUser
from django.core import signing from django.core import signing
...@@ -234,7 +235,7 @@ class User(AbstractUser): ...@@ -234,7 +235,7 @@ class User(AbstractUser):
user.groups.add(UserGroup.initial()) user.groups.add(UserGroup.initial())
def delete(self): def delete(self):
if self.pk == 1: if self.pk == 1 or self.username == 'admin':
return return
return super(User, self).delete() return super(User, self).delete()
......
...@@ -5,14 +5,37 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -5,14 +5,37 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin 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 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: class Meta:
model = User 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): class UserPKUpdateSerializer(serializers.ModelSerializer):
...@@ -22,21 +45,14 @@ class UserPKUpdateSerializer(serializers.ModelSerializer): ...@@ -22,21 +45,14 @@ class UserPKUpdateSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def validate__public_key(value): def validate__public_key(value):
from sshpubkeys import SSHKey if not validate_ssh_public_key(value):
from sshpubkeys.exceptions import InvalidKeyException print('Not a valid key')
ssh = SSHKey(value) print(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
raise serializers.ValidationError(_('Not a valid ssh public key')) raise serializers.ValidationError(_('Not a valid ssh public key'))
return value return value
class UserAndGroupSerializer(serializers.ModelSerializer): class UserUpdateGroupSerializer(serializers.ModelSerializer):
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all()) groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all())
class Meta: class Meta:
...@@ -44,43 +60,63 @@ class UserAndGroupSerializer(serializers.ModelSerializer): ...@@ -44,43 +60,63 @@ class UserAndGroupSerializer(serializers.ModelSerializer):
fields = ['id', 'groups'] fields = ['id', 'groups']
class GroupDetailSerializer(serializers.ModelSerializer): class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
user_amount = serializers.SerializerMethodField()
class Meta: class Meta:
model = UserGroup 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 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 @staticmethod
def get_active_display(obj): def get_user_amount(obj):
# TODO: user active state return obj.users.count()
return not (obj.is_expired and obj.is_active)
class GroupBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer): class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer):
user_amount = serializers.SerializerMethodField() users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all())
class Meta: class Meta:
model = UserGroup model = UserGroup
list_serializer_class = BulkListSerializer fields = ['id', 'users']
fields = ['id', 'name', 'comment', 'user_amount']
@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 i18n %}
{% load static %} {% load static %}
{% load bootstrap %} {% load bootstrap %}
{% block custom_head_css_js %} {% block form %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> {% csrf_token %}
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet"> <h3>{% trans 'Account' %}</h3>
{% endblock %} {{ form.username|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{% block content %} {{ form.email|bootstrap_horizontal }}
<div class="wrapper wrapper-content animated fadeInRight"> {{ form.groups|bootstrap_horizontal }}
<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 }}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
{% block password %} {% endblock %} {% block password %} {% endblock %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3> <h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }} {{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5"> <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> <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="col-sm-9">
<div class="input-group date"> <div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span> <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' }}"> <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> </div>
<span class="help-block ">{{ form.date_expired.errors }}</span> <span class="help-block ">{{ form.date_expired.errors }}</span>
</div> </div>
</div> </div>
{# {{ form.date_expired|bootstrap_horizontal }}#} {{ form.date_expired|bootstrap_horizontal }}
<div class="form-group"> <div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label> <label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8"> <div class="col-sm-8">
{{ form.enable_otp }} {{ form.enable_otp }}
</div> </div>
</div> </div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3> <h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }} {{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }} {{ form.wechat|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }} {{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button> <button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button> <button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div> </div>
</div> </div>
</form> </form>
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script> <script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
{% load i18n %} {% load i18n %}
{% load bootstrap %} {% load bootstrap %}
{% block user_template_title %}{% trans "Create user" %}{% endblock %} {% block user_template_title %}{% trans "Create user" %}{% endblock %}
{% block username %} {#{% block username %}#}
{{ form.username|bootstrap_horizontal }} {# {{ form.username|bootstrap_horizontal }}#}
{% endblock %} {#{% endblock %}#}
{% block password %} {% block password %}
<h3>{% trans 'Password' %}</h3> <h3>{% trans 'Password' %}</h3>
<div class="form-group"> <div class="form-group">
......
This diff is collapsed.
This diff is collapsed.
...@@ -19,20 +19,21 @@ ...@@ -19,20 +19,21 @@
<form method="post" class="form-horizontal" action="" > <form method="post" class="form-horizontal" action="" >
{% csrf_token %} {% csrf_token %}
{{ form.name|bootstrap_horizontal }} {{ form.name|bootstrap_horizontal }}
<div class="form-group"> <div class="form-group">
<label for="users" class="col-sm-2 control-label">{% trans 'Users' %}</label> <label for="users" class="col-sm-2 control-label">{% trans 'Users' %}</label>
<div class="col-sm-9"> <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 %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>
</div> </div>
{{ form.comment|bootstrap_horizontal }} {{ form.comment|bootstrap_horizontal }}
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Cancel' %}</button> <button class="btn btn-white" type="reset">{% trans 'Cancel' %}</button>
...@@ -45,11 +46,12 @@ ...@@ -45,11 +46,12 @@
</div> </div>
</div> </div>
</div> </div>
{% include "users/_select_user_modal.html" %}
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2().val([{{ group_users }}]).trigger("change"); $('.select2').select2();
}) })
</script> </script>
{% endblock %} {% 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' %} {% extends '_base_list.html' %}
{% load i18n static %} {% 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_search %}{% endblock %}
{% block table_container %} {% 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> <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 { ...@@ -20,11 +7,10 @@ div.dataTables_wrapper div.dataTables_filter {
<thead> <thead>
<tr> <tr>
<th class="text-center"> <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>
<th class="text-center">{% trans 'Name' %}</a></th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'User Amount' %}</a></th> <th class="text-center">{% trans 'User Amount' %}</th>
<th class="text-center">{% trans 'Asset Amount' %}</th>
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
...@@ -56,11 +42,11 @@ $(document).ready(function() { ...@@ -56,11 +42,11 @@ $(document).ready(function() {
var detail_btn = '<a href="{% url "users:user-group-detail" pk=99991937 %}">' + cellData + '</a>'; var detail_btn = '<a href="{% url "users:user-group-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id)); $(td).html(detail_btn.replace('99991937', rowData.id));
}}, }},
{targets: 4, createdCell: function (td, cellData) { {targets: 3, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 18 ? cellData.substring(0, 18) + '...': cellData; var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData;
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>'); $(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 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); 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) { if (rowData.id === 1) {
...@@ -69,9 +55,9 @@ $(document).ready(function() { ...@@ -69,9 +55,9 @@ $(document).ready(function() {
$(td).html(update_btn + del_btn) $(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"}, 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() op_html: $('#actions').html()
}; };
jumpserver.initDataTable(options); jumpserver.initDataTable(options);
...@@ -79,7 +65,7 @@ $(document).ready(function() { ...@@ -79,7 +65,7 @@ $(document).ready(function() {
var $this = $(this); var $this = $(this);
function doDelete() { function doDelete() {
var group_id = $this.data('gid'); 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 body = {};
var success = function() { var success = function() {
var msg = "{% trans 'Group Deleted.' %}"; var msg = "{% trans 'Group Deleted.' %}";
...@@ -89,7 +75,7 @@ $(document).ready(function() { ...@@ -89,7 +75,7 @@ $(document).ready(function() {
var fail = function() { var fail = function() {
var msg = "{% trans 'Group Deleting failed.' %}"; var msg = "{% trans 'Group Deleting failed.' %}";
swal("{% trans 'Group Delete' %}", msg, "error"); swal("{% trans 'Group Delete' %}", msg, "error");
} };
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
body: JSON.stringify(body), body: JSON.stringify(body),
...@@ -118,8 +104,8 @@ $(document).ready(function() { ...@@ -118,8 +104,8 @@ $(document).ready(function() {
}); });
if (plain_id_list === []) { if (plain_id_list === []) {
return false; return false;
}; }
var the_url = "{% url 'users:user-group-bulk-update-api' %}"; var the_url = "{% url 'api-users:user-group-list' %}";
function doDelete() { function doDelete() {
swal({ swal({
title: "{% trans 'Are you sure?' %}", title: "{% trans 'Are you sure?' %}",
......
This diff is collapsed.
{% extends 'users/_user.html' %} {% extends 'users/_user.html' %}
{% load i18n %} {% load i18n %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %} {% 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 %} {% block password %}
<h3>{% trans 'Password' %}</h3> <h3>{% trans 'Password' %}</h3>
<div class="form-group"> <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
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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