diff --git a/apps/assets/api.py b/apps/assets/api.py
index 2aa8c564c..f6d6926bb 100644
--- a/apps/assets/api.py
+++ b/apps/assets/api.py
@@ -1,33 +1,29 @@
# ~*~ coding: utf-8 ~*~
-from rest_framework import serializers
-from rest_framework import viewsets, serializers, generics
+from rest_framework import viewsets, generics, mixins
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateDestroyAPIView
+from django.shortcuts import get_object_or_404
from common.mixins import BulkDeleteApiMixin
from common.utils import get_object_or_none, signer
from .hands import IsSuperUserOrTerminalUser, IsSuperUser
-from .models import AssetGroup, Asset, IDC, SystemUser
-from .serializers import AssetBulkUpdateSerializer
+from .models import AssetGroup, Asset, IDC, SystemUser, AdminUser
+from . import serializers
-class AssetGroupSerializer(serializers.ModelSerializer):
- class Meta:
- model = AssetGroup
+class AssetViewSet(viewsets.ModelViewSet):
+ """API endpoint that allows Asset to be viewed or edited."""
+ 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):
- class Meta:
- model = IDC
- # fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
+ def get_queryset(self):
+ queryset = super(AssetViewSet, self).get_queryset()
+ idc = self.request.query_params.get('idc', '')
+ if idc:
+ queryset = queryset.filter(idc__id=idc)
+ return queryset
class AssetGroupViewSet(viewsets.ModelViewSet):
@@ -35,26 +31,44 @@ class AssetGroupViewSet(viewsets.ModelViewSet):
some other comment
"""
queryset = AssetGroup.objects.all()
- serializer_class = AssetGroupSerializer
+ 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."""
queryset = IDC.objects.all()
- serializer_class = IDCSerializer
+ serializer_class = serializers.IDCSerializer
permission_classes = (IsSuperUser,)
+class AdminUserViewSet(viewsets.ModelViewSet):
+ queryset = AdminUser.objects.all()
+ serializer_class = serializers.AdminUserSerializer
+ permission_classes = (IsSuperUser,)
+
+
+class SystemUserViewSet(viewsets.ModelViewSet):
+ queryset = SystemUser.objects.all()
+ serializer_class = serializers.SystemUserSerializer
+ permission_classes = (IsSuperUser,)
+
+
+# class IDCAssetsApi(generics.ListAPIView):
+# model = IDC
+# serializer_class = serializers.AssetSerializer
+#
+# def get(self, request, *args, **kwargs):
+# filter_kwargs = {self.lookup_field: self.kwargs[self.lookup_field]}
+# self.object = get_object_or_404(self.model, **filter_kwargs)
+# return super(IDCAssetsApi, self).get(request, *args, **kwargs)
+#
+# def get_queryset(self):
+# return self.object.assets.all()
+
class AssetListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
queryset = Asset.objects.all()
- serializer_class = AssetBulkUpdateSerializer
+ serializer_class = serializers.AssetSerializer
permission_classes = (IsSuperUser,)
diff --git a/apps/assets/forms.py b/apps/assets/forms.py
index 5ec97d042..09fcc9a24 100644
--- a/apps/assets/forms.py
+++ b/apps/assets/forms.py
@@ -1,8 +1,9 @@
# coding:utf-8
from django import forms
+from django.utils.translation import gettext_lazy as _
from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser, Tag
-from django.utils.translation import gettext_lazy as _
+from common.utils import validate_ssh_private_key, ssh_pubkey_gen
# class AssetForm(forms.ModelForm):
@@ -23,12 +24,10 @@ from django.utils.translation import gettext_lazy as _
#
class AssetCreateForm(forms.ModelForm):
-
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None)
if instance:
initial = kwargs.get('initial', {})
- #tags = instance.tags.all()
initial['tags'] = [t.pk for t in kwargs['instance'].tags.all()]
super(AssetCreateForm, self).__init__(*args, **kwargs)
@@ -141,7 +140,6 @@ class AdminUserForm(forms.ModelForm):
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
)
- auto_generate_key = forms.BooleanField(required=True, initial=True)
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
help_text=_('If also set private key, use that first'), required=False)
@@ -166,21 +164,36 @@ class AdminUserForm(forms.ModelForm):
# Because we define custom field, so we need rewrite :method: `save`
admin_user = super(AdminUserForm, self).save(commit=commit)
password = self.cleaned_data['password']
- private_key_file = self.cleaned_data['private_key_file']
+ private_key = self.cleaned_data['private_key_file']
+ public_key = ssh_pubkey_gen(private_key)
if password:
admin_user.password = password
- print(password)
- # Todo: Validate private key file, and generate public key
- # Todo: Auto generate private key and public key
- if private_key_file:
- admin_user.private_key = private_key_file.read()
+ if private_key:
+ admin_user.private_key = private_key
+ admin_user.public_key = public_key
admin_user.save()
- return self.instance
+ return admin_user
+
+ def clean_private_key_file(self):
+ private_key_file = self.cleaned_data['private_key_file']
+ if private_key_file:
+ private_key = private_key_file.read()
+ if not validate_ssh_private_key(private_key):
+ raise forms.ValidationError(_('Invalid private key'))
+ return private_key
+ return private_key_file
+
+ def clean(self):
+ password = self.cleaned_data['password']
+ private_key_file = self.cleaned_data.get('private_key_file', '')
+
+ if not (password or private_key_file):
+ raise forms.ValidationError(_('Password and private key file must be input one'))
class Meta:
model = AdminUser
- fields = ['name', 'username', 'auto_generate_key', 'password', 'private_key_file', 'as_default', 'comment']
+ fields = ['name', 'username', 'password', 'private_key_file', 'comment']
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
diff --git a/apps/assets/models.py b/apps/assets/models.py
index 1103d0b4c..1b13b8621 100644
--- a/apps/assets/models.py
+++ b/apps/assets/models.py
@@ -6,8 +6,9 @@ from django.db import models
from django.core import serializers
import logging
from django.utils.translation import ugettext_lazy as _
+from django.core.exceptions import ValidationError
-from common.utils import signer
+from common.utils import signer, validate_ssh_private_key
logger = logging.getLogger(__name__)
@@ -91,13 +92,21 @@ class AssetExtend(models.Model):
unique_together = ('key', 'value')
+def private_key_validator(value):
+ if not validate_ssh_private_key(value):
+ raise ValidationError(
+ _('%(value)s is not an even number'),
+ params={'value': value},
+ )
+
+
class AdminUser(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
- _password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
- _private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
+ _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
+ _private_key = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'),
+ validators=[private_key_validator,])
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
- as_default = models.BooleanField(default=False, verbose_name=_('As default'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
@@ -107,7 +116,7 @@ class AdminUser(models.Model):
@property
def password(self):
- return decrypt(self._password)
+ return signer.unsign(self._password)
@password.setter
def password(self, password_raw):
@@ -129,6 +138,10 @@ class AdminUser(models.Model):
def public_key(self, public_key_raw):
self._public_key = signer.sign(public_key_raw)
+ @property
+ def assets_amount(self):
+ return self.assets.count()
+
class Meta:
db_table = 'admin_user'
@@ -216,6 +229,10 @@ class SystemUser(models.Model):
assets = set(self.assets.all()) | self.get_assets_inherit_from_asset_groups()
return list(assets)
+ @property
+ def assets_amount(self):
+ return self.assets.count()
+
class Meta:
db_table = 'system_user'
@@ -298,9 +315,8 @@ class Asset(models.Model):
admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets',
on_delete=models.SET_NULL, verbose_name=_("Admin user"))
system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User"))
- idc = models.ForeignKey(IDC, null=True, related_name='assets',
+ idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets',
on_delete=models.SET_NULL, verbose_name=_('IDC'),)
- # default=get_default_idc)
mac_address = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address"))
brand = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Brand'))
cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU'))
@@ -329,6 +345,7 @@ class Asset(models.Model):
def __unicode__(self):
return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port}
+ @property
def is_valid(self):
warning = ''
if not self.is_active:
diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py
index f5b53fb68..86acbff37 100644
--- a/apps/assets/serializers.py
+++ b/apps/assets/serializers.py
@@ -1,24 +1,86 @@
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
from rest_framework import viewsets, serializers,generics
-from .models import AssetGroup, Asset, IDC, AssetExtend
+from .models import AssetGroup, Asset, IDC, AssetExtend, AdminUser, SystemUser
from common.mixins import BulkDeleteApiMixin
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
-class AssetBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer):
- # group_display = serializers.SerializerMethodField()
- # active_display = serializers.SerializerMethodField()
- #groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all())
+class AssetGroupSerializer(serializers.ModelSerializer):
+ assets_amount = serializers.SerializerMethodField()
+ # assets = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
+
+ class Meta:
+ model = AssetGroup
+
+ @staticmethod
+ def get_assets_amount(obj):
+ return obj.assets.count()
+
+
+class AdminUserSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = AdminUser
+
+ def get_field_names(self, declared_fields, info):
+ fields = super(AdminUserSerializer, self).get_field_names(declared_fields, info)
+ fields.append('assets_amount')
+ return fields
+
+
+class SystemUserSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = SystemUser
+ exclude = ('_password', '_private_key', '_public_key')
+
+ def get_field_names(self, declared_fields, info):
+ fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info)
+ fields.append('assets_amount')
+ return fields
+
+
+class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
+ system_users = SystemUserSerializer(many=True, read_only=True)
+ admin_user = AdminUserSerializer(many=False, read_only=True)
class Meta(object):
model = Asset
list_serializer_class = BulkListSerializer
- fields = ['id', 'port', 'idc']
- # def get_group_display(self, obj):
- # return " ".join([group.name for group in obj.groups.all()])
- #
- # def get_active_display(self, obj):
- # # TODO: user ative state
- # return not (obj.is_expired and obj.is_active)
\ No newline at end of file
+
+class AssetGrantedSerializer(serializers.ModelSerializer):
+ system_users = SystemUserSerializer(many=True, read_only=True)
+ is_inherited = serializers.SerializerMethodField()
+ system_users_join = serializers.SerializerMethodField()
+
+ class Meta(object):
+ model = Asset
+ fields = ("id", "hostname", "ip", "port", "system_users", "is_inherited",
+ "is_active", "system_users_join", "comment")
+
+ @staticmethod
+ def get_is_inherited(obj):
+ if getattr(obj, 'inherited', ''):
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def get_system_users_join(obj):
+ return ', '.join([system_user.username for system_user in obj.system_users.all()])
+
+
+class IDCSerializer(serializers.ModelSerializer):
+ assets_amount = serializers.SerializerMethodField()
+
+ class Meta:
+ model = IDC
+
+ @staticmethod
+ def get_assets_amount(obj):
+ return obj.assets.count()
+
+ def get_field_names(self, declared_fields, info):
+ fields = super(IDCSerializer, self).get_field_names(declared_fields, info)
+ fields.append('assets_amount')
+ return fields
diff --git a/apps/assets/templates/assets/admin_user_create_update.html b/apps/assets/templates/assets/admin_user_create_update.html
index 7738adec0..575ab667a 100644
--- a/apps/assets/templates/assets/admin_user_create_update.html
+++ b/apps/assets/templates/assets/admin_user_create_update.html
@@ -31,20 +31,8 @@
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}
{{ form.username|bootstrap_horizontal }}
-
{{ form.password|bootstrap_horizontal }}
{{ form.private_key_file|bootstrap_horizontal }}
-
{{ form.assets|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html
index ed41a5cfe..ea35972a0 100644
--- a/apps/assets/templates/assets/admin_user_list.html
+++ b/apps/assets/templates/assets/admin_user_list.html
@@ -1,41 +1,66 @@
{% extends '_base_list.html' %}
-{% load i18n %}
-{% load common_tags %}
-{% block content_left_head %}
- {% trans "Create admin user" %}
+{% load i18n static %}
+{% block table_search %}
+{% endblock %}
+{% block table_container %}
+
+
+{% endblock %}
+{% block content_bottom_left %}{% endblock %}
+{% block custom_foot_js %}
+
{% endblock %}
-{% block table_head %}
- {% trans 'ID' %} |
- {% trans 'Name' %} |
- {% trans 'Username' %} |
- {% trans 'Asset num' %} |
- {% trans 'Lost connection' %} |
- {% trans 'Comment' %} |
- |
-{% endblock %}
-{% block table_body %}
- {% for admin_user in admin_user_list %}
-
- {{ admin_user.id }} |
-
-
- {{ admin_user.name }}
-
- |
- {{ admin_user.username }} |
- {{ admin_user.assets.count }} |
- {{ admin_user.assets.count }} |
- {{ admin_user.comment|truncatewords:8 }} |
-
-
- {% trans 'Script' %}
-
- {% trans 'Refresh' %}
- {% trans 'Update' %}
- {% trans 'Delete' %}
- |
-
- {% endfor %}
-{% endblock %}
+
diff --git a/apps/assets/templates/assets/asset_create.html b/apps/assets/templates/assets/asset_create.html
index e49540738..1a4aa38d5 100644
--- a/apps/assets/templates/assets/asset_create.html
+++ b/apps/assets/templates/assets/asset_create.html
@@ -30,12 +30,11 @@
-
-
+
{% endblock %}
{% block custom_foot_js %}
@@ -44,10 +43,9 @@
$('.select2').select2();
$("#id_tags").select2({
tags: true,
- maximumSelectionLength: 8, //最多能够选择的个数
+ maximumSelectionLength: 8 //最多能够选择的个数
//closeOnSelect: false
});
})
-
{% endblock %}
\ No newline at end of file
diff --git a/apps/assets/templates/assets/asset_group_create.html b/apps/assets/templates/assets/asset_group_create.html
index f1df67d53..f2dbfd3ee 100644
--- a/apps/assets/templates/assets/asset_group_create.html
+++ b/apps/assets/templates/assets/asset_group_create.html
@@ -5,13 +5,6 @@
{% block custom_head_css_js %}
-
{% endblock %}
{% block content %}
@@ -94,7 +87,7 @@ div.dataTables_wrapper div.dataTables_filter,
$(document).ready(function () {
$('.select2').select2();
$('.select2-system-user').select2();
- })
+ });
$('#add_asset').on('click',function(){
$('#modal').modal('show');
@@ -104,7 +97,7 @@ div.dataTables_wrapper div.dataTables_filter,
show: false,
backdrop: 'static',
keyboard: 'false',
- remote:"{% url 'assets:asset-modal-list' %}?group_id={{ group_id }}",
+ remote:"{% url 'assets:asset-modal-list' %}?group_id={{ group_id }}"
});
$('#modal').on('show.bs.modal',function(){
diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html
index cf2fa8e90..aadd5c388 100644
--- a/apps/assets/templates/assets/asset_list.html
+++ b/apps/assets/templates/assets/asset_list.html
@@ -7,19 +7,11 @@
{% endblock %}
{% block content_left_head %}{% endblock %}
@@ -80,7 +72,8 @@ div.dataTables_wrapper div.dataTables_filter,
{% trans 'Update' %}
- {% trans 'Delete' %}
+
+ {% trans 'Delete' %}
|
{% endfor %}
@@ -190,18 +183,18 @@ div.dataTables_wrapper div.dataTables_filter,
}else{
$(this).addClass('selected');
this.children[0].children[0].checked=1;
- };
+ }
});
$('#btn_bulk_update').on('click',function(){
var column2 = table.rows('.selected').data();
var id_list = [];
var plain_id_list = [];
- var the_url = "{% url 'assets:asset-bulk-update-api' %}";
+ var the_url = "{% url 'api-assets:asset-bulk-update' %}";
for(var i=0;i
+{% endblock %}
+{% block content %}
+
+
+
+
+
+
+
+
+
+
{% trans 'IDC assets' %} {{ idc.name }}
+
+
+
+
+
+
+
+
+ {% trans 'Attach to assets ' %}
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+{% block custom_foot_js %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/apps/assets/templates/assets/idc_detail.html b/apps/assets/templates/assets/idc_detail.html
new file mode 100644
index 000000000..dc3143213
--- /dev/null
+++ b/apps/assets/templates/assets/idc_detail.html
@@ -0,0 +1,138 @@
+{% extends 'base.html' %}
+{% load common_tags %}
+{% load users_tags %}
+{% load static %}
+{% load i18n %}
+
+{% block custom_head_css_js %}
+
+
+{% endblock %}
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% trans 'Name' %}: |
+ {{ idc.name }} |
+
+
+ {% trans 'Bandwidth' %}: |
+ {{ idc.bandwidth }} |
+
+
+ {% trans 'Contact' %}: |
+ {{ idc.contact }} |
+
+
+ {% trans 'Phone' %}: |
+ {{ idc.phone }} |
+
+
+ {% trans 'Address' %}: |
+ {{ idc.address }} |
+
+
+ {% trans 'Intranet' %}: |
+ {{ idc.Intranet }} |
+
+
+ {% trans 'Extranet' %}: |
+ {{ idc.extranet }} |
+
+
+ {% trans 'Operator' %}: |
+ {{ idc.operator }} |
+
+
+ {% trans 'Date created' %}: |
+ {{ system_user.date_created }} |
+
+
+ {% trans 'Created by' %}: |
+ {{ asset_group.created_by }} |
+
+
+ {% trans 'Comment' %}: |
+ {{ system_user.comment }} |
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
+{% block custom_foot_js %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/apps/assets/templates/assets/idc_list.html b/apps/assets/templates/assets/idc_list.html
index 810b2a102..f9ee96bb7 100644
--- a/apps/assets/templates/assets/idc_list.html
+++ b/apps/assets/templates/assets/idc_list.html
@@ -1,43 +1,64 @@
{% extends '_base_list.html' %}
-{% load i18n %}
-{% load common_tags %}
-{% block content_left_head %}
- {% trans "Create IDC" %}
-{% endblock %}
-
-{% block table_head %}
-
-
- |
- {% trans 'Name' %} |
- {% trans 'Asset num' %} |
- {% trans 'Contact' %} |
- {% trans 'Phone' %} |
- {% trans 'operation' %} |
-{% endblock %}
-
-{% block table_body %}
- {% for idc in idc_list %}
-
-
-
- |
- {{ idc.name }} |
- {{ idc.assets.count }} |
-{# {{ idc.bandwidth }} | #}
- {{ idc.contact }} |
- {{ idc.phone }} |
-{# {{ idc.address }} | #}
-
- {% trans 'Update' %}
- {% trans 'Delete' %}
- |
-
- {% endfor %}
+{% load i18n static %}
+{% block table_search %}{% endblock %}
+{% block table_container %}
+
+
{% endblock %}
+{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
{% endblock %}
+
+
diff --git a/apps/assets/urls.py b/apps/assets/urls.py
deleted file mode 100644
index 825b3e4ab..000000000
--- a/apps/assets/urls.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# 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[0-9]+)$', views.AssetDetailView.as_view(), name='asset-detail'),
- url(r'^asset/(?P[0-9]+)/update', views.AssetUpdateView.as_view(), name='asset-update'),
- url(r'^asset/(?P[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[0-9]+)$', views.AssetGroupDetailView.as_view(), name='asset-group-detail'),
- url(r'^asset-group/(?P[0-9]+)/update$', views.AssetGroupUpdateView.as_view(), name='asset-group-update'),
- url(r'^asset-group/(?P[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[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[0-9]+)$', views.AssetTagDetailView.as_view(), name='asset-tag-detail'),
- url(r'^asset-tag/(?P[0-9]+)/update$', views.AssetTagUpdateView.as_view(), name='asset-tag-update'),
- url(r'^asset-tag/(?P[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[0-9]+)$', views.IDCDetailView.as_view(), name='idc-detail'),
- url(r'^idc/(?P[0-9]+)/update', views.IDCUpdateView.as_view(), name='idc-update'),
- url(r'^idc/(?P[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[0-9]+)$', views.AdminUserDetailView.as_view(), name='admin-user-detail'),
- url(r'^admin-user/(?P[0-9]+)/update', views.AdminUserUpdateView.as_view(), name='admin-user-update'),
- url(r'^admin-user/(?P[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[0-9]+)$', views.SystemUserDetailView.as_view(), name='system-user-detail'),
- url(r'^system-user/(?P[0-9]+)/update', views.SystemUserUpdateView.as_view(), name='system-user-update'),
- url(r'^system-user/(?P[0-9]+)/delete$', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
- url(r'^system-user/(?P[0-9]+)/asset$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
- # url(r'^system-user/(?P[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'),
-]
-
-
diff --git a/apps/assets/urls/__init__.py b/apps/assets/urls/__init__.py
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/apps/assets/urls/__init__.py
@@ -0,0 +1 @@
+
diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py
new file mode 100644
index 000000000..ca1760404
--- /dev/null
+++ b/apps/assets/urls/api_urls.py
@@ -0,0 +1,23 @@
+# 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[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
+
diff --git a/apps/assets/urls/views_urls.py b/apps/assets/urls/views_urls.py
new file mode 100644
index 000000000..7b1f4e1bc
--- /dev/null
+++ b/apps/assets/urls/views_urls.py
@@ -0,0 +1,58 @@
+# 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[0-9]+)/$', views.AssetDetailView.as_view(), name='asset-detail'),
+ url(r'^asset/(?P[0-9]+)/update/$', views.AssetUpdateView.as_view(), name='asset-update'),
+ url(r'^asset/(?P[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[0-9]+)/$', views.AssetGroupDetailView.as_view(), name='asset-group-detail'),
+ url(r'^asset-group/(?P[0-9]+)/update/$', views.AssetGroupUpdateView.as_view(), name='asset-group-update'),
+ url(r'^asset-group/(?P[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[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[0-9]+)/$', views.AssetTagDetailView.as_view(), name='asset-tag-detail'),
+ url(r'^asset-tag/(?P[0-9]+)/update/$', views.AssetTagUpdateView.as_view(), name='asset-tag-update'),
+ url(r'^asset-tag/(?P[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[0-9]+)/$', views.IDCDetailView.as_view(), name='idc-detail'),
+ url(r'^idc/(?P[0-9]+)/update/', views.IDCUpdateView.as_view(), name='idc-update'),
+ url(r'^idc/(?P[0-9]+)/delete/$', views.IDCDeleteView.as_view(), name='idc-delete'),
+ url(r'^idc/(?P[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[0-9]+)/$', views.AdminUserDetailView.as_view(), name='admin-user-detail'),
+ url(r'^admin-user/(?P[0-9]+)/update/$', views.AdminUserUpdateView.as_view(), name='admin-user-update'),
+ url(r'^admin-user/(?P[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[0-9]+)/$', views.SystemUserDetailView.as_view(), name='system-user-detail'),
+ url(r'^system-user/(?P[0-9]+)/update/$', views.SystemUserUpdateView.as_view(), name='system-user-update'),
+ url(r'^system-user/(?P[0-9]+)/delete/$', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
+ url(r'^system-user/(?P[0-9]+)/asset/$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
+ # url(r'^system-user/(?P[0-9]+)/asset-group$', views.SystemUserAssetGroupView.as_view(),
+ # name='system-user-asset-group'),
+
+]
+
diff --git a/apps/assets/views.py b/apps/assets/views.py
index f15612de8..ea8981ba9 100644
--- a/apps/assets/views.py
+++ b/apps/assets/views.py
@@ -30,21 +30,21 @@ class AssetListView(AdminUserRequiredMixin, ListView):
@staticmethod
def sorted_by_valid_and_ip(asset):
ip_list = int_seq(asset.ip.split('.'))
- ip_list.insert(0, asset.is_valid()[0])
+ ip_list.insert(0, asset.is_valid[0])
return ip_list
def get_context_data(self, **kwargs):
context = {
'app': 'Assets',
'action': 'asset list',
- 'tag_list': [(i.id,i.name,i.asset_set.all().count())for i in Tag.objects.all().order_by('name')]
+ 'tag_list': [(i.id, i.name, i.asset_set.all().count())for i in Tag.objects.all().order_by('name')]
}
kwargs.update(context)
return super(AssetListView, self).get_context_data(**kwargs)
-class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView):
+class AssetCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, CreateView):
model = Asset
tag_type = 'asset'
form_class = AssetCreateForm
@@ -58,7 +58,8 @@ class AssetCreateView(AdminUserRequiredMixin,CreateAssetTagsMiXin,CreateView):
return super(AssetCreateView, self).form_valid(form)
def form_invalid(self, form):
- print(form.errors)
+ if form.errors.get('__all__'):
+ form.errors['all'] = form.errors.get('__all__')
return super(AssetCreateView, self).form_invalid(form)
def get_context_data(self, **kwargs):
@@ -207,7 +208,7 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
plain_id_lists = self.request.GET.get('plain_id_lists')
self.s = self.request.GET.get('plain_id_lists')
if "," in str(self.s):
- self.plain_id_lists = [int(x) for x in self.s.split(',')]
+ self.plain_id_lists = [int(x) for x in self.s.split(',')]
else:
self.plain_id_lists = [self.s]
@@ -217,19 +218,19 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
else:
plain_id_lists = [int(self.s)]
context = {
- 'all_assets':plain_id_lists
+ 'all_assets' :plain_id_lists
}
kwargs.update(context)
if group_id:
group = AssetGroup.objects.get(id=group_id)
context = {
- 'all_assets':[x.id for x in group.assets.all()]
+ 'all_assets': [x.id for x in group.assets.all()]
}
kwargs.update(context)
if tag_id:
tag = Tag.objects.get(id=tag_id)
context = {
- 'all_assets':[x.id for x in tag.asset_set.all()]
+ 'all_assets': [x.id for x in tag.asset_set.all()]
}
kwargs.update(context)
return super(AssetModalListView, self).get_context_data(**kwargs)
@@ -341,33 +342,33 @@ class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:asset-group-list')
-class IDCListView(AdminUserRequiredMixin, ListView):
- model = IDC
- paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
- context_object_name = 'idc_list'
+class IDCListView(AdminUserRequiredMixin, TemplateView):
+ # model = IDC
+ # paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
+ # context_object_name = 'idc_list'
template_name = 'assets/idc_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('IDC list'),
- 'keyword': self.request.GET.get('keyword', '')
+ # 'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(IDCListView, self).get_context_data(**kwargs)
- def get_queryset(self):
- self.queryset = super(IDCListView, self).get_queryset()
- self.keyword = keyword = self.request.GET.get('keyword', '')
- self.sort = sort = self.request.GET.get('sort', '-date_created')
-
- if keyword:
- self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
- Q(comment__icontains=keyword))
-
- if sort:
- self.queryset = self.queryset.order_by(sort)
- return self.queryset
+ # def get_queryset(self):
+ # self.queryset = super(IDCListView, self).get_queryset()
+ # self.keyword = keyword = self.request.GET.get('keyword', '')
+ # self.sort = sort = self.request.GET.get('sort', '-date_created')
+ #
+ # if keyword:
+ # self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
+ # Q(comment__icontains=keyword))
+ #
+ # if sort:
+ # self.queryset = self.queryset.order_by(sort)
+ # return self.queryset
class IDCCreateView(AdminUserRequiredMixin, CreateView):
@@ -414,7 +415,15 @@ class IDCUpdateView(AdminUserRequiredMixin, UpdateView):
class IDCDetailView(AdminUserRequiredMixin, DetailView):
- pass
+ model = IDC
+ template_name = 'assets/idc_detail.html'
+ context_object_name = 'idc'
+
+
+class IDCAssetsView(AdminUserRequiredMixin, DetailView):
+ model = IDC
+ template_name = 'assets/idc_assets.html'
+ context_object_name = 'idc'
class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
@@ -423,34 +432,34 @@ class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:idc-list')
-class AdminUserListView(AdminUserRequiredMixin, ListView):
+class AdminUserListView(AdminUserRequiredMixin, TemplateView):
model = AdminUser
- paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
- context_object_name = 'admin_user_list'
+ # paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
+ # context_object_name = 'admin_user_list'
template_name = 'assets/admin_user_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Admin user list'),
- 'keyword': self.request.GET.get('keyword', '')
+ # 'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(AdminUserListView, self).get_context_data(**kwargs)
- def get_queryset(self):
- # Todo: Default order by lose asset connection num
- self.queryset = super(AdminUserListView, self).get_queryset()
- self.keyword = keyword = self.request.GET.get('keyword', '')
- self.sort = sort = self.request.GET.get('sort', '-date_created')
-
- if keyword:
- self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
- Q(comment__icontains=keyword))
-
- if sort:
- self.queryset = self.queryset.order_by(sort)
- return self.queryset
+ # def get_queryset(self):
+ # Todo: Default order by lose asset connection num
+ # self.queryset = super(AdminUserListView, self).get_queryset()
+ # self.keyword = keyword = self.request.GET.get('keyword', '')
+ # self.sort = sort = self.request.GET.get('sort', '-date_created')
+ #
+ # if keyword:
+ # self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
+ # Q(comment__icontains=keyword))
+ #
+ # if sort:
+ # self.queryset = self.queryset.order_by(sort)
+ # return self.queryset
class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
@@ -475,6 +484,9 @@ class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
))
return success_message
+ def form_invalid(self, form):
+ return super(AdminUserCreateView, self).form_invalid(form)
+
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser
diff --git a/apps/audits/api.py b/apps/audits/api.py
index 605de0acd..ea906a022 100644
--- a/apps/audits/api.py
+++ b/apps/audits/api.py
@@ -3,14 +3,14 @@
from __future__ import absolute_import, unicode_literals
-from rest_framework import generics
+from rest_framework import generics, viewsets
from rest_framework.views import APIView, Response
from . import models, serializers
from .hands import IsSuperUserOrTerminalUser, Terminal
-class ProxyLogListCreateApi(generics.ListCreateAPIView):
+class ProxyLogViewSet(viewsets.ModelViewSet):
"""User proxy to backend server need call this api.
params: {
@@ -34,18 +34,8 @@ class ProxyLogListCreateApi(generics.ListCreateAPIView):
serializer_class = serializers.ProxyLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
- def perform_create(self, serializer):
- # Todo: May be save log_file
- super(ProxyLogListCreateApi, self).perform_create(serializer)
-
-class ProxyLogDetailApi(generics.RetrieveUpdateDestroyAPIView):
- queryset = models.ProxyLog.objects.all()
- serializer_class = serializers.ProxyLogSerializer
- permission_classes = (IsSuperUserOrTerminalUser,)
-
-
-class CommandLogListCreateApi(generics.ListCreateAPIView):
+class CommandLogViewSet(viewsets.ModelViewSet):
queryset = models.CommandLog.objects.all()
serializer_class = serializers.CommandLogSerializer
permission_classes = (IsSuperUserOrTerminalUser,)
diff --git a/apps/audits/hands.py b/apps/audits/hands.py
index 36358cbf5..281440780 100644
--- a/apps/audits/hands.py
+++ b/apps/audits/hands.py
@@ -1,6 +1,7 @@
# ~*~ coding: utf-8 ~*~
#
+from users.utils import AdminUserRequiredMixin
from users.models import User
from assets.models import Asset, SystemUser
from users.backends import IsSuperUserOrTerminalUser
diff --git a/apps/audits/models.py b/apps/audits/models.py
index a98178a0d..b2da116e3 100644
--- a/apps/audits/models.py
+++ b/apps/audits/models.py
@@ -22,9 +22,7 @@ class LoginLog(models.Model):
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
login_city = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Login city'))
user_agent = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('User agent'))
- from_terminal = models.ForeignKey
date_login = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
- date_logout = models.DateTimeField(null=True, verbose_name=_('Date logout'))
class Meta:
db_table = 'login_log'
diff --git a/apps/audits/tasks.py b/apps/audits/tasks.py
new file mode 100644
index 000000000..f80aa5f75
--- /dev/null
+++ b/apps/audits/tasks.py
@@ -0,0 +1,12 @@
+#!/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)
+
diff --git a/apps/audits/templates/audits/login_log_list.html b/apps/audits/templates/audits/login_log_list.html
new file mode 100644
index 000000000..53b4ef0a2
--- /dev/null
+++ b/apps/audits/templates/audits/login_log_list.html
@@ -0,0 +1,100 @@
+{% extends '_base_list.html' %}
+{% load i18n %}
+{% load static %}
+{% load common_tags %}
+{% block content_left_head %}
+
+
+{% endblock %}
+
+
+{% block table_search %}
+
+{% endblock %}
+
+{% block table_head %}
+ {% trans 'ID' %} |
+ {% trans 'Username' %} |
+ {% trans 'Name' %} |
+ {% trans 'Type' %} |
+ {% trans 'UA' %} |
+ {% trans 'IP' %} |
+ {% trans 'City' %} |
+ {% trans 'Date' %} |
+{% endblock %}
+
+{% block table_body %}
+ {% for login_log in login_log_list %}
+
+
+ {{ login_log.id }}
+{# {{ login_log.id }}#}
+ |
+ {{ login_log.username }} |
+ {{ login_log.name }} |
+ {{ login_log.get_login_type_display }} |
+ {% if login_log.login_type == 'W' %}
+
+ {{ login_log.user_agent | truncatechars:20 }}
+ |
+ {% else %}
+ {{ login_log.terminal }} |
+ {% endif %}
+ {{ login_log.login_ip }} |
+ {{ login_log.login_city }} |
+ {{ login_log.date_login }} |
+
+ {% endfor %}
+{% endblock %}
+
+{% block custom_foot_js %}
+
+
+{% endblock %}
+
diff --git a/apps/audits/urls/__init__.py b/apps/audits/urls/__init__.py
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/apps/audits/urls/__init__.py
@@ -0,0 +1 @@
+
diff --git a/apps/audits/urls/api_urls.py b/apps/audits/urls/api_urls.py
new file mode 100644
index 000000000..2aa429fd0
--- /dev/null
+++ b/apps/audits/urls/api_urls.py
@@ -0,0 +1,14 @@
+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
+
diff --git a/apps/audits/urls.py b/apps/audits/urls/views_urls.py
similarity index 56%
rename from apps/audits/urls.py
rename to apps/audits/urls/views_urls.py
index 37962d9db..67d5d35c6 100644
--- a/apps/audits/urls.py
+++ b/apps/audits/urls/views_urls.py
@@ -1,8 +1,5 @@
from django.conf.urls import url
-
-
-import api
-import views
+from .. import views
app_name = 'audits'
@@ -11,11 +8,7 @@ urlpatterns = [
url(r'^proxy-log/(?P\d+)$', views.ProxyLogDetailView.as_view(), name='proxy-log-detail'),
url(r'^proxy-log/(?P\d+)/commands$', views.ProxyLogCommandsListView.as_view(), name='proxy-log-commands-list'),
url(r'^command-log$', views.CommandLogListView.as_view(), name='command-log-list'),
+ url(r'^login-log$', views.LoginLogListView.as_view(), name='login-log-list'),
]
-urlpatterns += [
- url(r'^v1/proxy-log/$', api.ProxyLogListCreateApi.as_view(), name='proxy-log-list-create-api'),
- url(r'^v1/proxy-log/(?P\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'),
-]
diff --git a/apps/audits/utils.py b/apps/audits/utils.py
index 2ad77a7e8..303ba18c9 100644
--- a/apps/audits/utils.py
+++ b/apps/audits/utils.py
@@ -1,5 +1,47 @@
# ~*~ 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
+
diff --git a/apps/audits/views.py b/apps/audits/views.py
index 39aa3f578..61fb1ff97 100644
--- a/apps/audits/views.py
+++ b/apps/audits/views.py
@@ -10,13 +10,13 @@ from django.urls import reverse_lazy
from django.conf import settings
from django.db.models import Q
-from .models import ProxyLog, CommandLog
-from .utils import AdminUserRequiredMixin
-from .hands import User, Asset, SystemUser
+from .models import ProxyLog, CommandLog, LoginLog
+from .hands import User, Asset, SystemUser, AdminUserRequiredMixin
-seven_days_ago_s = (datetime.datetime.now()-datetime.timedelta(7)).strftime('%m/%d/%Y')
-now_s = datetime.datetime.now().strftime('%m/%d/%Y')
+date_now = timezone.localtime(timezone.now())
+now_s = date_now.strftime('%m/%d/%Y')
+seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
class ProxyLogListView(AdminUserRequiredMixin, ListView):
@@ -54,6 +54,7 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
return self.queryset
def get_context_data(self, **kwargs):
+ print(self.date_to_s)
context = {
'app': _('Audits'),
'action': _('Proxy log list'),
@@ -152,3 +153,43 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
}
kwargs.update(context)
return super(CommandLogListView, self).get_context_data(**kwargs)
+
+
+class LoginLogListView(AdminUserRequiredMixin, ListView):
+ model = LoginLog
+ template_name = 'audits/login_log_list.html'
+ context_object_name = 'login_log_list'
+
+ def get_queryset(self):
+ self.queryset = super(LoginLogListView, self).get_queryset()
+ self.keyword = keyword = self.request.GET.get('keyword', '')
+ self.username = username = self.request.GET.get('username', '')
+ self.date_from_s = date_from_s = self.request.GET.get('date_from', '%s' % seven_days_ago_s)
+ self.date_to_s = date_to_s = self.request.GET.get('date_to', '%s' % now_s)
+
+ if date_from_s:
+ date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
+ self.queryset = self.queryset.filter(date_login__gt=date_from)
+ if date_to_s:
+ date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
+ self.queryset = self.queryset.filter(date_login__lt=date_to)
+ if username:
+ self.queryset = self.queryset.filter(username=username)
+ if keyword:
+ self.queryset = self.queryset.filter(Q(username__contains=keyword) |
+ Q(name__icontains=keyword) |
+ Q(login_ip=keyword)).distinct()
+ return self.queryset
+
+ def get_context_data(self, **kwargs):
+ context = {
+ 'app': _('Audits'),
+ 'action': _('Proxy log list'),
+ 'user_list': User.objects.all().order_by('username'),
+ 'keyword': self.keyword,
+ 'date_from': self.date_from_s,
+ 'date_to': self.date_to_s,
+ 'username': self.username,
+ }
+ kwargs.update(context)
+ return super(LoginLogListView, self).get_context_data(**kwargs)
diff --git a/apps/common/__init__.py b/apps/common/__init__.py
index 88fef8e67..b64e43e83 100644
--- a/apps/common/__init__.py
+++ b/apps/common/__init__.py
@@ -3,4 +3,3 @@ from __future__ import absolute_import
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app
-
diff --git a/apps/common/celery.py b/apps/common/celery.py
index 9d006fe4a..f4ea048e5 100644
--- a/apps/common/celery.py
+++ b/apps/common/celery.py
@@ -1,8 +1,8 @@
# ~*~ coding: utf-8 ~*~
from __future__ import absolute_import, unicode_literals
-
import os
+from datetime import timedelta
from celery import Celery
@@ -16,6 +16,5 @@ app = Celery('jumpserver')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
-
app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
diff --git a/apps/common/tasks.py b/apps/common/tasks.py
index 8df504baa..11d0d711d 100644
--- a/apps/common/tasks.py
+++ b/apps/common/tasks.py
@@ -1,11 +1,12 @@
from __future__ import absolute_import
-from celery import shared_task
+# from celery import shared_task
from django.core.mail import send_mail
from django.conf import settings
+from common import celery_app as app
-@shared_task(name='send_mail_async')
+@app.task
def send_mail_async(*args, **kwargs):
""" Using celery to send email async
@@ -26,9 +27,3 @@ def send_mail_async(*args, **kwargs):
args = tuple(args)
send_mail(*args, **kwargs)
-
-
-# def send_mail_async(subject, message, from_mail, recipient_list, fail_silently=False, html_message=None):
-# if settings.CONFIG.MAIL_SUBJECT_PREFIX:
-# subject += settings.CONFIG.MAIL_SUBJECT_PREFIX
-# send_mail(subject, message, from_mail, recipient_list, fail_silently=fail_silently, html_message=html_message)
diff --git a/apps/common/utils.py b/apps/common/utils.py
index d3bd61ddf..269455d09 100644
--- a/apps/common/utils.py
+++ b/apps/common/utils.py
@@ -3,11 +3,15 @@
from __future__ import unicode_literals
from six import string_types
+import os
from itertools import chain
import string
import logging
import datetime
+import paramiko
+import paramiko
+import sshpubkeys
from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \
BadSignature, SignatureExpired
from django.shortcuts import reverse as dj_reverse
@@ -15,6 +19,11 @@ from django.conf import settings
from django.core import signing
from django.utils import timezone
+try:
+ import cStringIO as StringIO
+except ImportError:
+ import StringIO
+
SECRET_KEY = settings.SECRET_KEY
@@ -162,4 +171,88 @@ def timesince(dt, since='', default="just now"):
return default
+def ssh_key_string_to_obj(text):
+ key_f = StringIO.StringIO(text)
+ key = None
+ try:
+ key = paramiko.RSAKey.from_private_key(key_f)
+ except paramiko.SSHException:
+ pass
+
+ try:
+ key = paramiko.DSSKey.from_private_key(key_f)
+ except paramiko.SSHException:
+ pass
+ return key
+
+
+def ssh_pubkey_gen(private_key=None, username='jumpserver', hostname='localhost'):
+ if isinstance(private_key, string_types):
+ private_key = ssh_key_string_to_obj(private_key)
+
+ if not isinstance(private_key, (paramiko.RSAKey, paramiko.DSSKey)):
+ raise IOError('Invalid private key')
+
+ public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % {
+ 'key_type': private_key.get_name(),
+ 'key_content': private_key.get_base64(),
+ 'username': username,
+ 'hostname': hostname,
+ }
+ return public_key
+
+
+def ssh_key_gen(length=2048, type='rsa', password=None, username='jumpserver', hostname=None):
+ """Generate user ssh private and public key
+
+ Use paramiko RSAKey generate it.
+ :return private key str and public key str
+ """
+
+ if hostname is None:
+ hostname = os.uname()[1]
+
+ f = StringIO.StringIO()
+
+ try:
+ if type == 'rsa':
+ private_key_obj = paramiko.RSAKey.generate(length)
+ elif type == 'dsa':
+ private_key_obj = paramiko.DSSKey.generate(length)
+ else:
+ raise IOError('SSH private key must be `rsa` or `dsa`')
+ private_key_obj.write_private_key(f, password=password)
+ private_key = f.getvalue()
+ public_key = ssh_pubkey_gen(private_key_obj, username=username, hostname=hostname)
+ return private_key, public_key
+ except IOError:
+ raise IOError('These is error when generate ssh key.')
+
+
+def validate_ssh_private_key(text):
+ key = ssh_key_string_to_obj(text)
+ if key is None:
+ return False
+ else:
+ return True
+
+
+def validate_ssh_public_key(text):
+ ssh = sshpubkeys.SSHKey(text)
+ try:
+ ssh.parse()
+ except sshpubkeys.InvalidKeyException:
+ return False
+ except NotImplementedError as e:
+ return False
+ return True
+
+
+def setattr_bulk(seq, key, value):
+ def set_attr(obj):
+ setattr(obj, key, value)
+ return obj
+ return map(set_attr, seq)
+
+
signer = Signer()
\ No newline at end of file
diff --git a/apps/common/views.py b/apps/common/views.py
index 870cfbf79..5663b8b19 100644
--- a/apps/common/views.py
+++ b/apps/common/views.py
@@ -2,6 +2,3 @@ from __future__ import absolute_import, unicode_literals
from django.shortcuts import render
from django.views.generic import TemplateView
-
-
-
diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py
index 5893ee909..3f31995d6 100644
--- a/apps/jumpserver/settings.py
+++ b/apps/jumpserver/settings.py
@@ -14,6 +14,7 @@ import os
import sys
from django.urls import reverse_lazy
+from datetime import timedelta
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -284,6 +285,31 @@ BROKER_URL = 'redis://%(password)s%(host)s:%(port)s/3' % {
}
CELERY_RESULT_BACKEND = BROKER_URL
+# TERMINAL_HEATBEAT_INTERVAL = CONFIG.TERMINAL_HEATBEAT_INTERVAL or 30
+
+# crontab job
+# CELERYBEAT_SCHEDULE = {
+# Check terminal is alive every 10m
+ # 'check_terminal_alive': {
+ # 'task': 'terminal.tasks.check_terminal_alive',
+ # 'schedule': timedelta(seconds=TERMINAL_HEATBEAT_INTERVAL),
+ # 'args': (),
+ # },
+# }
+
+
+# Cache use redis
+CACHES = {
+ 'default': {
+ 'BACKEND': 'redis_cache.RedisCache',
+ 'LOCATION': 'redis://%(password)s%(host)s:%(port)s/4' % {
+ 'password': CONFIG.REDIS_PASSWORD + '@' if CONFIG.REDIS_PASSWORD else '',
+ 'host': CONFIG.REDIS_HOST or '127.0.0.1',
+ 'port': CONFIG.REDIS_PORT or 6379,
+ }
+ }
+}
+
# Captcha settings, more see https://django-simple-captcha.readthedocs.io/en/latest/advanced.html
CAPTCHA_IMAGE_SIZE = (75, 33)
CAPTCHA_FOREGROUND_COLOR = '#001100'
diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py
index 81a5f9d15..50a4bb77d 100644
--- a/apps/jumpserver/urls.py
+++ b/apps/jumpserver/urls.py
@@ -22,11 +22,16 @@ from django.views.generic.base import TemplateView
urlpatterns = [
url(r'^captcha/', include('captcha.urls')),
url(r'^$', TemplateView.as_view(template_name='base.html'), name='index'),
- url(r'^(api/)?users/', include('users.urls')),
- url(r'^(api/)?assets/', include('assets.urls')),
- url(r'^(api/)?perms/', include('perms.urls')),
- url(r'^(api/)?audits/', include('audits.urls')),
- url(r'^(api/)?terminal/', include('terminal.urls')),
+ url(r'^users/', include('users.urls.views_urls', namespace='users')),
+ url(r'^assets/', include('assets.urls.views_urls', namespace='assets')),
+ url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
+ url(r'^audits/', include('audits.urls.views_urls', namespace='audits')),
+ url(r'^terminal/', include('terminal.urls.views_urls', namespace='terminal')),
+ url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')),
+ url(r'^api/assets/', include('assets.urls.api_urls', namespace='api-assets')),
+ url(r'^api/perms/', include('perms.urls.api_urls', namespace='api-perms')),
+ url(r'^api/audits/', include('audits.urls.api_urls', namespace='api-audits')),
+ url(r'^api/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')),
]
diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po
index b68a30613..0191d8a9c 100644
--- a/apps/locale/zh/LC_MESSAGES/django.po
+++ b/apps/locale/zh/LC_MESSAGES/django.po
@@ -1610,7 +1610,7 @@ msgid ""
"here reset password\n"
" \n"
" This link is valid for 1 hour. After it expires, request new one<\n"
+"(forget_password_url)s?email=%(email)s\">request new one\n"
"\n"
" \n"
" ---\n"
diff --git a/apps/ops/urls/__init__.py b/apps/ops/urls/__init__.py
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/apps/ops/urls/__init__.py
@@ -0,0 +1 @@
+
diff --git a/apps/ops/urls/api_urls.py b/apps/ops/urls/api_urls.py
new file mode 100644
index 000000000..d0c6b5204
--- /dev/null
+++ b/apps/ops/urls/api_urls.py
@@ -0,0 +1,7 @@
+# coding:utf-8
+
+from django.conf.urls import url
+from rest_framework import routers
+
+
+
diff --git a/apps/ops/urls/views_urls.py b/apps/ops/urls/views_urls.py
new file mode 100644
index 000000000..a29072e4f
--- /dev/null
+++ b/apps/ops/urls/views_urls.py
@@ -0,0 +1,8 @@
+# coding:utf-8
+
+from django.conf.urls import url
+from .. import views
+
+app_name = 'ops'
+
+
diff --git a/apps/perms/api.py b/apps/perms/api.py
index c40b9343e..a2522f44b 100644
--- a/apps/perms/api.py
+++ b/apps/perms/api.py
@@ -2,49 +2,120 @@
#
from rest_framework.views import APIView, Response
-from rest_framework.generics import ListCreateAPIView
+from rest_framework.generics import ListAPIView, get_object_or_404
+from rest_framework import viewsets
from users.backends import IsValidUser, IsSuperUser
-from .utils import get_user_granted_assets, get_user_granted_asset_groups
+from common.utils import get_object_or_none
+from .utils import get_user_granted_assets, get_user_granted_asset_groups, get_user_asset_permissions, \
+ get_user_group_asset_permissions, get_user_group_granted_assets, get_user_group_granted_asset_groups
from .models import AssetPermission
+from .hands import AssetGrantedSerializer, User, UserGroup, AssetGroup, Asset, AssetGroup, AssetGroupSerializer
from . import serializers
-class AssetPermissionListCreateApi(ListCreateAPIView):
+class AssetPermissionViewSet(viewsets.ModelViewSet):
queryset = AssetPermission.objects.all()
serializer_class = serializers.AssetPermissionSerializer
permission_classes = (IsSuperUser,)
+ def get_queryset(self):
+ queryset = super(AssetPermissionViewSet, self).get_queryset()
+ user_id = self.request.query_params.get('user', '')
+ user_group_id = self.request.query_params.get('user_group', '')
-class UserAssetsApi(APIView):
+ if user_id and user_id.isdigit():
+ user = get_object_or_404(User, id=int(user_id))
+ queryset = get_user_asset_permissions(user)
+
+ if user_group_id:
+ user_group = get_object_or_404(UserGroup, id=user_group_id)
+ queryset = get_user_group_asset_permissions(user_group)
+ return queryset
+
+ def get_serializer_class(self):
+ if getattr(self, 'user_id', ''):
+ return serializers.UserAssetPermissionSerializer
+ return serializers.AssetPermissionSerializer
+
+
+class RevokeUserAssetPermission(APIView):
+ permission_classes = (IsSuperUser,)
+
+ def put(self, request, *args, **kwargs):
+ permission_id = str(request.data.get('id', ''))
+ user_id = str(request.data.get('user_id', ''))
+
+ if permission_id and user_id and permission_id.isdigit() and user_id.isdigit():
+ asset_permission = get_object_or_404(AssetPermission, id=int(permission_id))
+ user = get_object_or_404(User, id=int(user_id))
+
+ if asset_permission and user:
+ asset_permission.users.remove(user)
+ return Response({'msg': 'success'})
+ return Response({'msg': 'failed'}, status=404)
+
+
+class RevokeUserGroupAssetPermission(APIView):
+ permission_classes = (IsSuperUser,)
+
+ def put(self, request, *args, **kwargs):
+ permission_id = str(request.data.get('id', ''))
+ user_group_id = str(request.data.get('user_group_id', ''))
+
+ if permission_id and user_group_id and permission_id.isdigit() and user_group_id.isdigit():
+ asset_permission = get_object_or_404(AssetPermission, id=int(permission_id))
+ user_group = get_object_or_404(UserGroup, id=int(user_group_id))
+
+ if asset_permission and user_group:
+ asset_permission.user_groups.remove(user_group)
+ return Response({'msg': 'success'})
+ return Response({'msg': 'failed'}, status=404)
+
+
+class UserGrantedAssetsApi(ListAPIView):
+ permission_classes = (IsSuperUser,)
+ serializer_class = AssetGrantedSerializer
+
+ def get_queryset(self):
+ user_id = self.kwargs.get('pk', '')
+
+ if user_id:
+ user = get_object_or_404(User, id=user_id)
+ queryset = get_user_granted_assets(user)
+ else:
+ queryset = []
+ return queryset
+
+
+class UserGrantedAssetGroupsApi(ListAPIView):
+ permission_classes = (IsSuperUser,)
+ serializer_class = AssetGroupSerializer
+
+ def get_queryset(self):
+ user_id = self.kwargs.get('pk', '')
+
+ if user_id:
+ user = get_object_or_404(User, id=user_id)
+ queryset = get_user_granted_asset_groups(user)
+ else:
+ queryset = []
+ return queryset
+
+
+class MyGrantedAssetsApi(ListAPIView):
permission_classes = (IsValidUser,)
+ serializer_class = AssetGrantedSerializer
- def get(self, request, *args, **kwargs):
- assets_json = []
- user = request.user
-
+ def get_queryset(self):
+ user = self.request.user
if user:
- assets = get_user_granted_assets(user)
-
- for asset, system_users in assets.items():
- assets_json.append({
- 'id': asset.id,
- 'hostname': asset.hostname,
- 'ip': asset.ip,
- 'port': asset.port,
- 'system_users': [
- {
- 'id': system_user.id,
- 'name': system_user.name,
- 'username': system_user.username,
- } for system_user in system_users
- ],
- 'comment': asset.comment
- })
-
- return Response(assets_json, status=200)
+ queryset = get_user_granted_assets(user)
+ else:
+ queryset = []
+ return queryset
-class UserAssetsGroupsApi(APIView):
+class MyGrantedAssetsGroupsApi(APIView):
permission_classes = (IsValidUser,)
def get(self, request, *args, **kwargs):
@@ -56,46 +127,61 @@ class UserAssetsGroupsApi(APIView):
for asset in assets:
for asset_group in asset.groups.all():
if asset_group.id in asset_groups:
- asset_groups[asset_group.id]['asset_num'] += 1
+ asset_groups[asset_group.id]['asset_amount'] += 1
else:
asset_groups[asset_group.id] = {
'id': asset_group.id,
'name': asset_group.name,
'comment': asset_group.comment,
- 'asset_num': 1
+ 'assets_amount': 1
}
-
asset_groups_json = asset_groups.values()
return Response(asset_groups_json, status=200)
-class UserAssetsGroupAssetsApi(APIView):
+class MyAssetGroupAssetsApi(ListAPIView):
permission_classes = (IsValidUser,)
+ serializer_class = AssetGrantedSerializer
- def get(self, request, *args, **kwargs):
- # asset_group_id = request.query_params.get('asset_group_id', -1)
- asset_group_id = kwargs.get('pk', -1)
- # asset_group_name = request.query_params.get('asset_group_name', '')
- user = request.user
- assets_json = []
+ def get_queryset(self):
+ queryset = []
+ asset_group_id = self.kwargs.get('pk', -1)
+ user = self.request.user
+ asset_group = get_object_or_none(AssetGroup, id=asset_group_id)
- if user:
+ if user and asset_group:
assets = get_user_granted_assets(user)
- for asset, system_users in assets.items():
- for asset_group in asset.groups.all():
- if str(asset_group.id) == asset_group_id: # and asset_group.name == asset_group_name:
- assets_json.append({
- 'id': asset.id,
- 'hostname': asset.hostname,
- 'ip': asset.ip,
- 'port': asset.port,
- 'system_users': [
- {
- 'id': system_user.id,
- 'name': system_user.name,
- 'username': system_user.username,
- } for system_user in system_users
- ],
- 'comment': asset.comment
- })
- return Response(assets_json, status=200)
+ for asset in assets:
+ if asset_group in asset.groups.all():
+ queryset.append(asset)
+ return queryset
+
+
+class UserGroupGrantedAssetsApi(ListAPIView):
+ permission_classes = (IsSuperUser,)
+ serializer_class = AssetGrantedSerializer
+
+ def get_queryset(self):
+ user_group_id = self.kwargs.get('pk', '')
+
+ if user_group_id:
+ user_group = get_object_or_404(UserGroup, id=user_group_id)
+ queryset = get_user_group_granted_assets(user_group)
+ else:
+ queryset = []
+ return queryset
+
+
+class UserGroupGrantedAssetGroupsApi(ListAPIView):
+ permission_classes = (IsSuperUser,)
+ serializer_class = AssetGroupSerializer
+
+ def get_queryset(self):
+ user_group_id = self.kwargs.get('pk', '')
+
+ if user_group_id:
+ user_group = get_object_or_404(UserGroup, id=user_group_id)
+ queryset = get_user_group_granted_asset_groups(user_group)
+ else:
+ queryset = []
+ return queryset
\ No newline at end of file
diff --git a/apps/perms/hands.py b/apps/perms/hands.py
index 735faecfd..eac38f7d0 100644
--- a/apps/perms/hands.py
+++ b/apps/perms/hands.py
@@ -4,6 +4,7 @@
from users.utils import AdminUserRequiredMixin
from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser
+from assets.serializers import AssetGrantedSerializer, AssetGroupSerializer
def associate_system_users_with_assets(system_users, assets, asset_groups):
diff --git a/apps/perms/serializers.py b/apps/perms/serializers.py
index 78857cf0c..34df76f29 100644
--- a/apps/perms/serializers.py
+++ b/apps/perms/serializers.py
@@ -1,34 +1,26 @@
# -*- coding: utf-8 -*-
#
-from rest_framework import serializers
+from django.utils.translation import ugettext_lazy as _
+from rest_framework import serializers
+from common.utils import get_object_or_none
from .models import AssetPermission
+from .hands import User
class AssetPermissionSerializer(serializers.ModelSerializer):
- # users_amount = serializers.SerializerMethodField()
- # user_groups_amount = serializers.SerializerMethodField()
- # assets_amount = serializers.SerializerMethodField()
- # asset_groups_amount = serializers.SerializerMethodField()
-
class Meta:
model = AssetPermission
- fields = ['id', 'name', 'users', 'user_groups', 'assets', 'asset_groups',
- 'system_users', 'is_active', 'comment', 'date_expired']
- # @staticmethod
- # def get_users_amount(obj):
- # return obj.users.count()
- #
- # @staticmethod
- # def get_user_groups_amount(obj):
- # return obj.user_groups.count()
- #
- # @staticmethod
- # def get_assets_amount(obj):
- # return obj.assets.count()
- #
- # @staticmethod
- # def get_asset_groups_amount(obj):
- # return obj.asset_groups.count()
+
+class UserAssetPermissionSerializer(AssetPermissionSerializer):
+ is_inherited = serializers.SerializerMethodField()
+
+ @staticmethod
+ def get_is_inherited(obj):
+ if getattr(obj, 'inherited', ''):
+ return True
+ else:
+ return False
+
diff --git a/apps/perms/urls/__init__.py b/apps/perms/urls/__init__.py
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/apps/perms/urls/__init__.py
@@ -0,0 +1 @@
+
diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py
new file mode 100644
index 000000000..faac46676
--- /dev/null
+++ b/apps/perms/urls/api_urls.py
@@ -0,0 +1,37 @@
+# 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[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[0-9]+)/assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
+ url(r'^v1/user/(?P[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[0-9]+)/assets/$', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
+ url(r'^v1/user-group/(?P[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
+
diff --git a/apps/perms/urls.py b/apps/perms/urls/views_urls.py
similarity index 66%
rename from apps/perms/urls.py
rename to apps/perms/urls/views_urls.py
index 186199b56..b73d39fab 100644
--- a/apps/perms/urls.py
+++ b/apps/perms/urls/views_urls.py
@@ -1,8 +1,7 @@
# coding:utf-8
from django.conf.urls import url
-import views
-import api
+from .. import views
app_name = 'perms'
@@ -21,14 +20,4 @@ urlpatterns = [
name='asset-permission-asset-list'),
]
-urlpatterns += [
- url(r'^v1/asset-permission/$', api.AssetPermissionListCreateApi.as_view(),
- name='asset-permission-list-create-api'),
- url(r'^v1/user/assets/$', api.UserAssetsApi.as_view(),
- name='user-assets'),
- url(r'^v1/user/asset-groups/$', api.UserAssetsGroupsApi.as_view(),
- name='user-asset-groups'),
- url(r'^v1/user/asset-groups/(?P[0-9]+)/assets/$', api.UserAssetsGroupAssetsApi.as_view(),
- name='user-asset-groups-assets'),
-]
diff --git a/apps/perms/utils.py b/apps/perms/utils.py
index cbc6f823d..585b65808 100644
--- a/apps/perms/utils.py
+++ b/apps/perms/utils.py
@@ -1,5 +1,6 @@
from __future__ import absolute_import, unicode_literals
+from common.utils import setattr_bulk
from .hands import User, UserGroup, Asset, AssetGroup, SystemUser
@@ -7,7 +8,7 @@ def get_user_group_granted_asset_groups(user_group):
"""Return asset groups granted of the user group
:param user_group: Instance of :class: ``UserGroup``
- :return: {asset1: {system_user1, }, asset1: {system_user1, system_user2]}
+ :return: {asset_group1: {system_user1, }, asset_group2: {system_user1, system_user2}}
"""
asset_groups = {}
asset_permissions = user_group.asset_permissions.all()
@@ -20,7 +21,6 @@ def get_user_group_granted_asset_groups(user_group):
asset_groups[asset_group] |= set(asset_permission.system_users.all())
else:
asset_groups[asset_group] = set(asset_permission.system_users.all())
-
return asset_groups
@@ -41,7 +41,6 @@ def get_user_group_granted_assets(user_group):
assets[asset] |= set(asset_permission.system_users.all())
else:
assets[asset] = set(asset_permission.system_users.all())
-
return assets
@@ -61,7 +60,7 @@ def get_user_granted_asset_groups_direct(user):
if asset_group in asset_groups:
asset_groups[asset_group] |= set(asset_permission.system_users.all())
else:
- setattr(asset_group, 'is_inherit_from_user_group', False)
+ setattr(asset_group, 'inherited', False)
asset_groups[asset_group] = set(asset_permission.system_users.all())
return asset_groups
@@ -89,7 +88,7 @@ def get_user_granted_asset_groups_inherit_from_user_groups(user):
if asset_group in asset_groups:
asset_groups[asset_group] |= set(asset_permission.system_users.all())
else:
- setattr(asset_group, 'is_inherit_from_user_group', True)
+ setattr(asset_group, 'inherited', True)
asset_groups[asset_group] = set(asset_permission.system_users.all())
return asset_groups
@@ -112,7 +111,6 @@ def get_user_granted_asset_groups(user):
asset_groups[asset_group] |= asset_groups_direct[asset_group]
else:
asset_groups[asset_group] = asset_groups_direct[asset_group]
-
return asset_groups
@@ -132,10 +130,8 @@ def get_user_granted_assets_direct(user):
if asset in assets:
assets[asset] |= set(asset_permission.system_users.all())
else:
- setattr(asset, 'is_inherit_from_user_groups', False)
- setattr(asset, 'is_inherit_from_user_groups', False)
+ setattr(asset, 'inherited', False)
assets[asset] = set(asset_permission.system_users.all())
-
return assets
@@ -154,7 +150,7 @@ def get_user_granted_assets_inherit_from_user_groups(user):
if asset in assets:
assets[asset] |= assets_inherited[asset]
else:
- setattr(asset, 'is_inherit_from_user_groups', True)
+ setattr(asset, 'inherited', True)
assets[asset] = assets_inherited[asset]
return assets
@@ -175,10 +171,25 @@ def get_user_granted_assets(user):
assets[asset] |= assets_direct[asset]
else:
assets[asset] = assets_direct[asset]
-
return assets
+def get_user_group_asset_permissions(user_group):
+ permissions = user_group.asset_permissions.all()
+ return permissions
+
+
+def get_user_asset_permissions(user):
+ user_group_permissions = set()
+ direct_permissions = set(setattr_bulk(user.asset_permissions.all(), 'inherited', 0))
+
+ for user_group in user.groups.all():
+ permissions = get_user_group_asset_permissions(user_group)
+ user_group_permissions |= set(permissions)
+ user_group_permissions = set(setattr_bulk(user_group_permissions, 'inherited', 1))
+ return direct_permissions | user_group_permissions
+
+
def get_user_groups_granted_in_asset(asset):
pass
diff --git a/apps/static/css/jumpserver.css b/apps/static/css/jumpserver.css
index caa058e97..ecbde4cc4 100644
--- a/apps/static/css/jumpserver.css
+++ b/apps/static/css/jumpserver.css
@@ -255,3 +255,12 @@ table.dataTable tbody td.selected td i.text-navy
font-size: 12px;
vertical-align: middle;
}
+
+div.dataTables_wrapper div.dataTables_filter,
+.dataTables_length {
+ float: right !important;
+}
+
+div.dataTables_wrapper div.dataTables_filter {
+ margin-left: 15px;
+}
\ No newline at end of file
diff --git a/apps/static/css/style.css b/apps/static/css/style.css
index fb30bde06..609d1cffb 100644
--- a/apps/static/css/style.css
+++ b/apps/static/css/style.css
@@ -1585,9 +1585,9 @@ table.dataTable thead .sorting_desc_disabled {
.dataTables_wrapper {
padding-bottom: 30px;
}
-.dataTables_length {
- float: left;
-}
+/*.dataTables_length {*/
+ /*float: left;*/
+/*}*/
.dataTables_filter label {
margin-right: 5px;
}
diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js
index e2bc67ba6..e107832f4 100644
--- a/apps/static/js/jumpserver.js
+++ b/apps/static/js/jumpserver.js
@@ -189,9 +189,8 @@ function activeNav() {
function APIUpdateAttr(props) {
// props = {url: .., body: , success: , error: , method: ,}
props = props || {};
- success_message = props.success_message || 'Update Successfully!';
- fail_message = props.fail_message || 'Error occurred while updating.';
-
+ var success_message = props.success_message || 'Update Successfully!';
+ var fail_message = props.fail_message || 'Error occurred while updating.';
$.ajax({
url: props.url,
type: props.method || "PATCH",
@@ -199,19 +198,18 @@ function APIUpdateAttr(props) {
contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json"
}).done(function(data, textStatue, jqXHR) {
+ toastr.success(success_message);
if (typeof props.success === 'function') {
return props.success(data);
- } else {
- toastr.success(success_message);
- }
+ }
+
}).fail(function(jqXHR, textStatue, errorThrown) {
+ toastr.error(fail_message);
if (typeof props.error === 'function') {
return props.error(errorThrown);
- } else {
- toastr.error(fail_message);
- }
+ }
});
- return true;
+ // return true;
}
// Sweet Alert for Delete
@@ -267,6 +265,7 @@ $.fn.serializeObject = function()
};
var jumpserver = {};
jumpserver.checked = false;
+jumpserver.selected = {};
jumpserver.initDataTable = function (options) {
// options = {
// ele *: $('#dataTable_id'),
@@ -285,59 +284,37 @@ jumpserver.initDataTable = function (options) {
{
targets: 0,
orderable: false,
- createdCell: function(td) {
- $(td).html('');
- }
- },
+ createdCell: function(td, cellData) {
+ $(td).html(''.replace('99991937', cellData));
+ }},
{className: 'text-center', targets: '_all'}
];
columnDefs = options.columnDefs ? options.columnDefs.concat(columnDefs) : columnDefs;
var table = ele.DataTable({
- pageLength: options.pageLength || 25,
+ pageLength: options.pageLength || 15,
dom: options.dom || '<"#uc.pull-left"><"html5buttons"B>flti<"row m-t"<"#op.col-md-6"><"col-md-6"p>>',
language: {
url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json"
},
order: options.order || [[ 1, 'asc' ]],
- buttons: options.buttons || [
- {extend: 'excel',
- exportOptions: {
- modifier: {
- selected: true
- }
- }
- },
- {extend: 'pdf',
- exportOptions: {
- modifier: {
- selected: true
- }
- }
- },
- {extend: 'print',
- customize: function (win){
- $(win.document.body).addClass('white-bg');
- $(win.document.body).css('font-size', '10px');
- $(win.document.body).find('table')
- .addClass('compact')
- .css('font-size', 'inherit');
- }
- }
- ],
+ select: options.select || 'multi',
+ buttons: [],
columnDefs: columnDefs,
- select: options.select || {style: 'multi'},
ajax: {
url: options.ajax_url ,
dataSrc: ""
},
- columns: options.columns || []
+ columns: options.columns || [],
+ lengthMenu: [[15, 25, 50, -1], [15, 25, 50, "All"]]
});
table.on('select', function(e, dt, type, indexes) {
var $node = table[ type ]( indexes ).nodes().to$();
$node.find('input.ipt_check').prop('checked', true);
+ jumpserver.selected[$node.find('input.ipt_check').prop('id')] = true
}).on('deselect', function(e, dt, type, indexes) {
var $node = table[ type ]( indexes ).nodes().to$();
$node.find('input.ipt_check').prop('checked', false);
+ jumpserver.selected[$node.find('input.ipt_check').prop('id')] = false
}).on('draw', function(){
$('#op').html(options.op_html || '');
$('#uc').html(options.uc_html || '');
@@ -353,5 +330,6 @@ jumpserver.initDataTable = function (options) {
table.rows().deselect();
}
});
+
return table;
};
diff --git a/apps/templates/_base_create_update.html b/apps/templates/_base_create_update.html
index 2e1a981f8..c011feb71 100644
--- a/apps/templates/_base_create_update.html
+++ b/apps/templates/_base_create_update.html
@@ -28,10 +28,17 @@
- {% block form %} {% endblock %}
+ {% if form.errors.all %}
+
+ {{ form.errors.all }}
+
+ {% endif %}
+ {% block form %}
+ {% endblock %}
{% endblock %}
+
diff --git a/apps/templates/_footer.html b/apps/templates/_footer.html
index d3b860048..c43bda248 100644
--- a/apps/templates/_footer.html
+++ b/apps/templates/_footer.html
@@ -1,6 +1,6 @@