From 968b1b4cb6cc7306087ebc58ebbd8848a96ab0c8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 6 Nov 2016 21:29:04 +0800 Subject: [PATCH 01/23] Stash --- apps/assets/api.py | 59 ++++--- apps/assets/models.py | 1 + apps/assets/serializers.py | 42 ++++- .../templates/assets/admin_user_list.html | 151 +++++++++++++----- apps/assets/templates/assets/asset_list.html | 37 ++--- .../templates/assets/delete_confirm.html | 2 +- apps/assets/templates/assets/idc_assets.html | 138 ++++++++++++++++ apps/assets/templates/assets/idc_detail.html | 138 ++++++++++++++++ apps/assets/templates/assets/idc_list.html | 102 ++++++++---- apps/assets/urls.py | 54 +++++-- apps/assets/views.py | 48 +++--- .../flash_message_standalone.html | 0 apps/users/models.py | 2 +- apps/users/templates/users/user_list.html | 2 +- 14 files changed, 625 insertions(+), 151 deletions(-) create mode 100644 apps/assets/templates/assets/idc_assets.html create mode 100644 apps/assets/templates/assets/idc_detail.html rename apps/{common/templates/common => templates}/flash_message_standalone.html (100%) diff --git a/apps/assets/api.py b/apps/assets/api.py index 2aa8c564c..05ca46331 100644 --- a/apps/assets/api.py +++ b/apps/assets/api.py @@ -1,33 +1,16 @@ # ~*~ 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 - - -class AssetGroupSerializer(serializers.ModelSerializer): - class Meta: - model = AssetGroup - - -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') +from .models import AssetGroup, Asset, IDC, SystemUser, AdminUser +from . import serializers class AssetGroupViewSet(viewsets.ModelViewSet): @@ -35,26 +18,50 @@ 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 + serializer_class = serializers.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.AssetBulkUpdateSerializer permission_classes = (IsSuperUser,) diff --git a/apps/assets/models.py b/apps/assets/models.py index 1103d0b4c..fdb298e3c 100644 --- a/apps/assets/models.py +++ b/apps/assets/models.py @@ -329,6 +329,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..cc00144ec 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -1,7 +1,7 @@ # -*- 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 @@ -14,11 +14,47 @@ class AssetBulkUpdateSerializer(BulkSerializerMixin, serializers.ModelSerializer class Meta(object): model = Asset list_serializer_class = BulkListSerializer - fields = ['id', 'port', 'idc'] + 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 + # return not (obj.is_expired and obj.is_active) + + +class AssetGroupSerializer(serializers.ModelSerializer): + class Meta: + model = AssetGroup + + +class AssetSerializer(serializers.ModelSerializer): + class Meta: + model = Asset + + +class AdminUserSerializer(serializers.ModelSerializer): + class Meta: + model = AdminUser + + +class SystemUserSerializer(serializers.ModelSerializer): + class Meta: + model = SystemUser + + +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_list.html b/apps/assets/templates/assets/admin_user_list.html index ed41a5cfe..e204b2e15 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -1,41 +1,118 @@ {% extends '_base_list.html' %} -{% load i18n %} -{% load common_tags %} -{% block content_left_head %} - {% trans "Create admin user" %} +{#{% load i18n %}#} +{#{% load common_tags %}#} +{#{% block content_left_head %}#} +{# {% trans "Create admin user" %} #} +{#{% 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 %}#} +{% extends '_base_list.html' %} +{% load i18n static %} +{% block custom_head_css_js %} +{{ block.super }} + +{% endblock %} +{% block table_search %}{% endblock %} +{% block table_container %} +
+ {% trans "Create IDC" %} +
+ + + + + + + + + + + + + + +
+ + {% trans 'Name' %}{% trans 'Username' %}{% trans 'Asset num' %}{% trans 'Lost connection' %}{% trans 'Comment' %}
+{% 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_list.html b/apps/assets/templates/assets/asset_list.html index cf2fa8e90..d8229855a 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -7,19 +7,19 @@ {% endblock %} {% block content_left_head %}{% endblock %} @@ -80,7 +80,8 @@ div.dataTables_wrapper div.dataTables_filter, {% trans 'Update' %} - {% trans 'Delete' %} + + {% trans 'Delete' %} {% endfor %} @@ -190,7 +191,7 @@ div.dataTables_wrapper div.dataTables_filter, }else{ $(this).addClass('selected'); this.children[0].children[0].checked=1; - }; + } }); $('#btn_bulk_update').on('click',function(){ @@ -201,7 +202,7 @@ div.dataTables_wrapper div.dataTables_filter, for(var i=0;i
{% csrf_token %} -

Are you sure you want to delete "{{ object.name }}"?

+

{% trans 'Are you sure delete' %} {{ object.name }} ?

diff --git a/apps/assets/templates/assets/idc_assets.html b/apps/assets/templates/assets/idc_assets.html new file mode 100644 index 000000000..0afd1fa0c --- /dev/null +++ b/apps/assets/templates/assets/idc_assets.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 'IDC assets' %} {{ idc.name }} +
+ + + + + + + + + + +
+
+
+ + + + + + + + + + + + + +
+ + {% trans 'Hostname' %}{% trans 'IP' %}{% trans 'Port' %}{% trans 'Type' %}{% trans 'Valid' %}
+
+
+
+
+
+
+ {% 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 %} +
+
+
+
+ +
+
+
+
+ {{ idc.name }} +
+ + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{% 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..e7bb236ca 100644 --- a/apps/assets/templates/assets/idc_list.html +++ b/apps/assets/templates/assets/idc_list.html @@ -1,43 +1,77 @@ {% extends '_base_list.html' %} -{% load i18n %} -{% load common_tags %} -{% block content_left_head %} - {% trans "Create IDC" %} -{% endblock %} +{% load i18n static %} +{% block custom_head_css_js %} +{{ block.super }} + {% 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 %} +{% block table_search %}{% endblock %} +{% block table_container %} + + + + + + + + + + + + + + + +
+ + {% trans 'Name' %}{% trans 'Asset num' %}{% trans 'Contact' %}{% trans 'Phone' %}{% trans 'Operator' %}{% trans 'Action' %}
{% endblock %} +{% block content_bottom_left %}{% endblock %} {% block custom_foot_js %} {% endblock %} + + diff --git a/apps/assets/urls.py b/apps/assets/urls.py index 825b3e4ab..01c431ec3 100644 --- a/apps/assets/urls.py +++ b/apps/assets/urls.py @@ -2,14 +2,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) +from rest_framework import routers + app_name = 'assets' urlpatterns = [ @@ -43,6 +37,7 @@ urlpatterns = [ 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'), @@ -63,10 +58,49 @@ urlpatterns = [ ] +# router = routers.DefaultRouter() +# router.register(r'v1/asset-groups/', api.AssetGroupViewSet) +# router.register(r'v1/assets/', api.AssetViewSet) +# router.register(r'v1/idc/', api.IDCViewSet) + +asset_list_view = api.AssetViewSet.as_view({ + 'get': 'list', + 'post': 'create' +}) + +asset_detail_view = api.AssetViewSet.as_view({ + 'get': 'retrieve', + 'put': 'update', + 'patch': 'partial_update', + 'delete': 'destroy', +}) + +idc_list_view = api.IDCViewSet.as_view({ + 'get': 'list', + 'post': 'create', +}) + +idc_detail_view = api.IDCViewSet.as_view({ + 'get': 'retrieve', + 'put': 'update', + 'patch': 'partial_update', + 'delete': 'destroy', +}) + +admin_user_list_view = api.AdminUserViewSet.as_view({ + 'get': 'list', + 'post': 'create', +}) + urlpatterns += [ - url(r'^v1/assets/$', api.AssetViewSet.as_view({'get':'list'}), name='assets-list-api'), + url(r'^v1/assets/$', asset_list_view, name='asset-list-create-api'), + url(r'^v1/assets/(?P[0-9]+)/$', asset_detail_view, name='asset-detail-update-delete-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/idc/$', idc_list_view, name='idc-list-create-api'), + url(r'^v1/idc/(?P[0-9]+)/$', idc_detail_view, name='idc-detail-update-delete-api'), + url(r'^v1/idc/(?P[0-9]+)/assets/$', api.IDCAssetsApi.as_view(), name='idc-assets-api'), + url(r'^v1/admin-user/$', idc_list_view, name='idc-list-create-api'), + url(r'^v1/idc/(?P[0-9]+)/$', idc_detail_view, name='idc-detail-update-delete-api'), url(r'^v1/system-user/auth/', api.SystemUserAuthApi.as_view(), name='system-user-auth'), ] diff --git a/apps/assets/views.py b/apps/assets/views.py index f15612de8..837dc3fed 100644 --- a/apps/assets/views.py +++ b/apps/assets/views.py @@ -30,14 +30,14 @@ 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) @@ -341,33 +341,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 +414,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): diff --git a/apps/common/templates/common/flash_message_standalone.html b/apps/templates/flash_message_standalone.html similarity index 100% rename from apps/common/templates/common/flash_message_standalone.html rename to apps/templates/flash_message_standalone.html diff --git a/apps/users/models.py b/apps/users/models.py index 8187eb916..531c8f402 100644 --- a/apps/users/models.py +++ b/apps/users/models.py @@ -234,7 +234,7 @@ class User(AbstractUser): user.groups.add(UserGroup.initial()) def delete(self): - if self.pk == 1: + if self.pk == 1 or self.username == 'admin': return return super(User, self).delete() diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index b50d5b24b..da124adfb 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -79,7 +79,7 @@ $(document).ready(function(){ {targets: 7, createdCell: function (td, cellData, rowData) { var update_btn = '{% trans "Update" %}'.replace('99991937', cellData); var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); - if (rowData.id === 1) { + if (rowData.id === 1 || rowData.username == "admin") { $(td).html(update_btn) } else { $(td).html(update_btn + del_btn) From 072da114dbfb8cd381ce150da874624dee707ea7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 6 Nov 2016 22:45:26 +0800 Subject: [PATCH 02/23] Finish system user list --- apps/assets/api.py | 29 ++++--- apps/assets/models.py | 10 ++- apps/assets/serializers.py | 10 +++ .../templates/assets/admin_user_list.html | 75 +++++-------------- apps/assets/templates/assets/asset_list.html | 12 +-- apps/assets/templates/assets/idc_assets.html | 12 +-- apps/assets/templates/assets/idc_list.html | 15 +--- apps/assets/urls.py | 53 +++---------- apps/assets/views.py | 34 ++++----- apps/static/css/jumpserver.css | 9 +++ apps/static/css/style.css | 6 +- apps/static/js/jumpserver.js | 5 +- .../templates/users/user_group_list.html | 19 +---- apps/users/templates/users/user_list.html | 13 ---- 14 files changed, 103 insertions(+), 199 deletions(-) diff --git a/apps/assets/api.py b/apps/assets/api.py index 05ca46331..820f418ef 100644 --- a/apps/assets/api.py +++ b/apps/assets/api.py @@ -26,6 +26,13 @@ class AssetViewSet(viewsets.ModelViewSet): queryset = Asset.objects.all() serializer_class = serializers.AssetSerializer + 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 IDCViewSet(viewsets.ModelViewSet): """API endpoint that allows IDC to be viewed or edited.""" @@ -46,17 +53,17 @@ class SystemUserViewSet(viewsets.ModelViewSet): 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 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): diff --git a/apps/assets/models.py b/apps/assets/models.py index fdb298e3c..c07833982 100644 --- a/apps/assets/models.py +++ b/apps/assets/models.py @@ -107,7 +107,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 +129,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 +220,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' diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index cc00144ec..9044c3cc6 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -38,11 +38,21 @@ 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 + 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 IDCSerializer(serializers.ModelSerializer): assets_amount = serializers.SerializerMethodField() diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html index e204b2e15..bc5d01692 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -1,57 +1,16 @@ {% extends '_base_list.html' %} -{#{% load i18n %}#} -{#{% load common_tags %}#} -{#{% block content_left_head %}#} -{# {% trans "Create admin user" %} #} -{#{% 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 %}#} -{% extends '_base_list.html' %} {% load i18n static %} {% block custom_head_css_js %} {{ block.super }} {% endblock %} {% block table_search %}{% endblock %} @@ -85,13 +44,13 @@ $(document).ready(function(){ ele: $('#admin_user_list_table'), columnDefs: [ {targets: 1, createdCell: function (td, cellData, rowData) { - var detail_btn = '' + cellData + ''; + var detail_btn = '' + cellData + ''; $(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('' + innerHtml + '');#} -{# }},#} + {targets: 5, createdCell: function (td, cellData) { + var innerHtml = cellData.length > 8 ? cellData.substring(0, 24) + '...': cellData; + $(td).html('' + innerHtml + ''); + }}, {# {targets: 6, createdCell: function (td, cellData) {#} {# if (!cellData) {#} {# $(td).html('')#} @@ -100,13 +59,13 @@ $(document).ready(function(){ {# }#} {# }},#} {targets: 6, createdCell: function (td, cellData, rowData) { - var update_btn = '{% trans "Update" %}'.replace('99991937', cellData); - var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); + var update_btn = '{% trans "Update" %}'.replace('99991937', cellData); + var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); $(td).html(update_btn + del_btn) }}], - ajax_url: '{% url "assets:idc-list-create-api" %}', - columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }, {data: "contact" }, {data: "phone" }, - {data: "operator" }, {data: "id" }], + ajax_url: '{% url "assets:api-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); diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index d8229855a..549a6bab3 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -7,14 +7,6 @@ {% endblock %} {% block content %}
@@ -128,7 +118,7 @@ $(td).html('') } }}], - ajax_url: '{% url "assets:idc-assets-api" pk=idc.id %}', + ajax_url: '{% url "assets:api-asset-list" %}?idc={{ idc.id }}', columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "type" }, {data: "is_active" }] }; diff --git a/apps/assets/templates/assets/idc_list.html b/apps/assets/templates/assets/idc_list.html index e7bb236ca..924b05fc8 100644 --- a/apps/assets/templates/assets/idc_list.html +++ b/apps/assets/templates/assets/idc_list.html @@ -1,18 +1,5 @@ {% extends '_base_list.html' %} {% load i18n static %} -{% block custom_head_css_js %} -{{ block.super }} - -{% endblock %} {% block table_search %}{% endblock %} {% block table_container %}
@@ -63,7 +50,7 @@ $(document).ready(function(){ var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); $(td).html(update_btn + del_btn) }}], - ajax_url: '{% url "assets:idc-list-create-api" %}', + ajax_url: '{% url "assets:api-idc-list" %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }, {data: "contact" }, {data: "phone" }, {data: "operator" }, {data: "id" }], op_html: $('#actions').html() diff --git a/apps/assets/urls.py b/apps/assets/urls.py index 01c431ec3..be53759ca 100644 --- a/apps/assets/urls.py +++ b/apps/assets/urls.py @@ -58,50 +58,17 @@ urlpatterns = [ ] -# router = routers.DefaultRouter() -# router.register(r'v1/asset-groups/', api.AssetGroupViewSet) -# router.register(r'v1/assets/', api.AssetViewSet) -# router.register(r'v1/idc/', api.IDCViewSet) - -asset_list_view = api.AssetViewSet.as_view({ - 'get': 'list', - 'post': 'create' -}) - -asset_detail_view = api.AssetViewSet.as_view({ - 'get': 'retrieve', - 'put': 'update', - 'patch': 'partial_update', - 'delete': 'destroy', -}) - -idc_list_view = api.IDCViewSet.as_view({ - 'get': 'list', - 'post': 'create', -}) - -idc_detail_view = api.IDCViewSet.as_view({ - 'get': 'retrieve', - 'put': 'update', - 'patch': 'partial_update', - 'delete': 'destroy', -}) - -admin_user_list_view = api.AdminUserViewSet.as_view({ - 'get': 'list', - 'post': 'create', -}) +router = routers.DefaultRouter() +router.register(r'v1/asset-groups', api.AssetGroupViewSet, 'api-asset-group') +router.register(r'v1/assets', api.AssetViewSet, 'api-asset') +router.register(r'v1/idc', api.IDCViewSet, 'api-idc') +router.register(r'v1/admin-user', api.AdminUserViewSet, 'api-admin-user') +router.register(r'v1/system-user', api.SystemUserViewSet, 'api-system-user') urlpatterns += [ - url(r'^v1/assets/$', asset_list_view, name='asset-list-create-api'), - url(r'^v1/assets/(?P[0-9]+)/$', asset_detail_view, name='asset-detail-update-delete-api'), - url(r'^v1/assets_bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update-api'), - url(r'^v1/idc/$', idc_list_view, name='idc-list-create-api'), - url(r'^v1/idc/(?P[0-9]+)/$', idc_detail_view, name='idc-detail-update-delete-api'), - url(r'^v1/idc/(?P[0-9]+)/assets/$', api.IDCAssetsApi.as_view(), name='idc-assets-api'), - url(r'^v1/admin-user/$', idc_list_view, name='idc-list-create-api'), - url(r'^v1/idc/(?P[0-9]+)/$', idc_detail_view, name='idc-detail-update-delete-api'), - url(r'^v1/system-user/auth/', api.SystemUserAuthApi.as_view(), name='system-user-auth'), + url(r'^v1/assets_bulk/$', api.AssetListUpdateApi.as_view(), name='api-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='api-system-user-auth'), ] - +urlpatterns += router.urls diff --git a/apps/assets/views.py b/apps/assets/views.py index 837dc3fed..8f5803552 100644 --- a/apps/assets/views.py +++ b/apps/assets/views.py @@ -431,34 +431,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): 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..c49dc96d3 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -293,7 +293,7 @@ jumpserver.initDataTable = function (options) { ]; 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" @@ -330,7 +330,8 @@ jumpserver.initDataTable = function (options) { 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$(); diff --git a/apps/users/templates/users/user_group_list.html b/apps/users/templates/users/user_group_list.html index 0463c3305..43101fd87 100644 --- a/apps/users/templates/users/user_group_list.html +++ b/apps/users/templates/users/user_group_list.html @@ -1,18 +1,5 @@ {% extends '_base_list.html' %} {% load i18n static %} -{% block custom_head_css_js %} -{{ block.super }} - -{% endblock %} {% block table_search %}{% endblock %} {% block table_container %} @@ -20,10 +7,10 @@ div.dataTables_wrapper div.dataTables_filter { -
+ - {% trans 'Name' %} - {% trans 'User Amount' %} + {% trans 'Name' %} + {% trans 'User Amount' %} {% trans 'Asset Amount' %} {% trans 'Comment' %} {% trans 'Action' %} diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index da124adfb..16744c39c 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -1,18 +1,5 @@ {% extends '_base_list.html' %} {% load i18n static %} -{% block custom_head_css_js %} -{{ block.super }} - -{% endblock %} {% block table_search %}{% endblock %} {% block table_container %} From a75e1db970f9c13211bdefcbd093a327ba7a07b4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 7 Nov 2016 00:39:26 +0800 Subject: [PATCH 03/23] Add form validate --- apps/assets/forms.py | 35 ++++++--- apps/assets/models.py | 17 ++++- .../assets/admin_user_create_update.html | 12 --- .../templates/assets/admin_user_list.html | 23 ++---- apps/assets/views.py | 3 + apps/common/utils.py | 74 +++++++++++++++++++ apps/users/utils.py | 29 -------- requirements.txt | 1 + 8 files changed, 122 insertions(+), 72 deletions(-) diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 5ec97d042..62460146e 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): @@ -141,7 +142,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 +166,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 c07833982..2ac4aabe8 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')) 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.auto_generate_key}} -
-
{{ form.password|bootstrap_horizontal }} {{ form.private_key_file|bootstrap_horizontal }} -
- -
- {{ form.as_default}} -
-
{{ 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 bc5d01692..0451d989f 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -1,22 +1,10 @@ {% extends '_base_list.html' %} {% load i18n static %} -{% block custom_head_css_js %} -{{ block.super }} - +{% block table_search %} {% endblock %} -{% block table_search %}{% endblock %} {% block table_container %} @@ -29,7 +17,7 @@ - + @@ -59,9 +47,10 @@ $(document).ready(function(){ {# }#} {# }},#} {targets: 6, createdCell: function (td, cellData, rowData) { - var update_btn = '{% trans "Update" %}'.replace('99991937', cellData); + var script_btn = '{% trans "Script" %}'.replace('99991937', cellData); + var update_btn = '{% trans "Update" %}'.replace('99991937', cellData); var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); - $(td).html(update_btn + del_btn) + $(td).html(script_btn + update_btn + del_btn) }}], ajax_url: '{% url "assets:api-admin-user-list" %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" }, {data: function () {return 'lost'} }, diff --git a/apps/assets/views.py b/apps/assets/views.py index 8f5803552..612379602 100644 --- a/apps/assets/views.py +++ b/apps/assets/views.py @@ -483,6 +483,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/common/utils.py b/apps/common/utils.py index d3bd61ddf..9a998adf9 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -3,11 +3,14 @@ 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 from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \ BadSignature, SignatureExpired from django.shortcuts import reverse as dj_reverse @@ -15,6 +18,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 +170,70 @@ 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 + + signer = Signer() \ No newline at end of file diff --git a/apps/users/utils.py b/apps/users/utils.py index 5291584ae..129bc6d78 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -34,36 +34,7 @@ class AdminUserRequiredMixin(UserPassesTestMixin): return self.request.user.is_staff -def ssh_key_gen(length=2048, password=None, username='root', hostname=None): - """Generate user ssh private and public key - Use paramiko RSAKey generate it. - - """ - - if hostname is None: - hostname = os.uname()[1] - - f = StringIO.StringIO() - - try: - logger.debug(_('Begin to generate ssh private key ...')) - private_key_obj = RSAKey.generate(length) - private_key_obj.write_private_key(f, password=password) - private_key = f.getvalue() - - public_key = "%(key_type)s %(key_content)s %(username)s@%(hostname)s" % { - 'key_type': private_key_obj.get_name(), - 'key_content': private_key_obj.get_base64(), - 'username': username, - 'hostname': hostname, - } - - logger.debug(_('Finish to generate ssh private key ...')) - return private_key, public_key - - except IOError: - raise IOError(_('These is error when generate ssh key.')) def user_add_success_next(user): diff --git a/requirements.txt b/requirements.txt index 4b3926c50..f4fcdac3a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ django-simple-captcha==0.5.2 django-formtools==1.0 sshpubkeys==2.2.0 djangorestframework-bulk==0.2.1 +paramiko==2.0.2 From ea3f8af1615cc5171f81d6fea4693a3042701eb2 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 7 Nov 2016 16:59:52 +0800 Subject: [PATCH 04/23] Fix pubkey auth bug --- apps/users/api.py | 2 +- apps/users/utils.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/users/api.py b/apps/users/api.py index 13cb666be..611bcbc0a 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -140,4 +140,4 @@ class UserTokenApi(APIView): cache.set('%s_%s' % (user.id, remote_addr), token, self.expiration) return Response({'token': token, 'id': user.id, 'username': user.username, 'name': user.name}) else: - return Response({'msg': 'Invalid password or public key or user is not active or expired'}) + return Response({'msg': 'Invalid password or public key or user is not active or expired'}, status=401) diff --git a/apps/users/utils.py b/apps/users/utils.py index 129bc6d78..29eabceb0 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -187,8 +187,14 @@ def check_user_valid(**kwargs): return None if password and user.check_password(password): return user - if public_key and user.public_key == public_key: - return user + if public_key: + public_key_saved = user.public_key.split() + if len(public_key_saved) == 1: + if public_key == public_key_saved[0]: + return user + elif len(public_key_saved) > 1: + if public_key == public_key_saved[1]: + return user return None From 0d4d64c27432166d9b70241e73682171f8ecbd25 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 9 Nov 2016 00:36:23 +0800 Subject: [PATCH 05/23] Update api --- apps/audits/api.py | 16 +++------------- apps/audits/urls.py | 10 +++++----- 2 files changed, 8 insertions(+), 18 deletions(-) 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/urls.py b/apps/audits/urls.py index 37962d9db..a752378ff 100644 --- a/apps/audits/urls.py +++ b/apps/audits/urls.py @@ -1,5 +1,6 @@ from django.conf.urls import url +from rest_framework import routers import api import views @@ -13,9 +14,8 @@ urlpatterns = [ url(r'^command-log$', views.CommandLogListView.as_view(), name='command-log-list'), ] +router = routers.DefaultRouter() +router.register(r'v1/proxy-log', api.ProxyLogViewSet, 'api-proxy-log') +router.register(r'v1/command-log', api.CommandLogViewSet, 'api-command-log') -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'), -] +urlpatterns += router.urls From 8d7759d22f5cbfe3409c8412896eb34ba3f65673 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 9 Nov 2016 19:29:15 +0800 Subject: [PATCH 06/23] change user api --- apps/assets/models.py | 3 +- .../assets/templates/assets/asset_create.html | 8 +- apps/assets/views.py | 5 +- apps/locale/zh/LC_MESSAGES/django.po | 2 +- apps/static/js/jumpserver.js | 6 +- apps/templates/_base_create_update.html | 9 +- apps/users/api.py | 145 +++++++++--------- apps/users/models.py | 1 + apps/users/serializers.py | 105 ++++++++----- apps/users/templates/users/_user.html | 118 ++++++-------- apps/users/templates/users/user_detail.html | 67 ++++---- apps/users/templates/users/user_list.html | 8 +- apps/users/urls.py | 33 ++-- apps/users/utils.py | 2 +- apps/users/views.py | 8 +- 15 files changed, 266 insertions(+), 254 deletions(-) diff --git a/apps/assets/models.py b/apps/assets/models.py index 2ac4aabe8..1b13b8621 100644 --- a/apps/assets/models.py +++ b/apps/assets/models.py @@ -315,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')) 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/views.py b/apps/assets/views.py index 612379602..f1c1d85bb 100644 --- a/apps/assets/views.py +++ b/apps/assets/views.py @@ -44,7 +44,7 @@ class AssetListView(AdminUserRequiredMixin, ListView): 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): 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/static/js/jumpserver.js b/apps/static/js/jumpserver.js index c49dc96d3..6f1fff39c 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -189,9 +189,9 @@ 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.'; + console.log(props.body); $.ajax({ url: props.url, type: props.method || "PATCH", 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/users/api.py b/apps/users/api.py index 611bcbc0a..8c945ca53 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -6,120 +6,119 @@ import base64 from django.shortcuts import get_object_or_404 from django.core.cache import cache from django.conf import settings -from rest_framework import generics, status +from rest_framework import generics, status, viewsets from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView +from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView, BulkModelViewSet from rest_framework import authentication from common.mixins import BulkDeleteApiMixin from common.utils import get_logger from .utils import check_user_valid, token_gen from .models import User, UserGroup -from .serializers import UserDetailSerializer, UserAndGroupSerializer, \ - GroupDetailSerializer, UserPKUpdateSerializer, UserBulkUpdateSerializer, GroupBulkUpdateSerializer +from . import serializers from .backends import IsSuperUser, IsTerminalUser, IsValidUser, IsSuperUserOrTerminalUser logger = get_logger(__name__) -class UserDetailApi(generics.RetrieveUpdateDestroyAPIView): +class UserViewSet(BulkModelViewSet): queryset = User.objects.all() - serializer_class = UserDetailSerializer + serializer_class = serializers.UserSerializer permission_classes = (IsSuperUser,) -class UserAndGroupEditApi(generics.RetrieveUpdateAPIView): - queryset = User.objects.all() - serializer_class = UserAndGroupSerializer - permission_classes = (IsSuperUser,) +# class UserAndGroupEditApi(generics.RetrieveUpdateAPIView): +# queryset = User.objects.all() +# serializer_class = serializers.UserAndGroupSerializer +# permission_classes = (IsSuperUser,) class UserResetPasswordApi(generics.UpdateAPIView): queryset = User.objects.all() - serializer_class = UserDetailSerializer + serializer_class = serializers.UserSerializer def perform_update(self, serializer): # Note: we are not updating the user object here. # We just do the reset-password staff. - user = self.get_object() import uuid + from .utils import send_reset_password_mail + user = self.get_object() user.password_raw = str(uuid.uuid4()) user.save() - from .utils import send_reset_password_mail send_reset_password_mail(user) -class UserResetPKApi(generics.UpdateAPIView): +class UserResetPubKeyApi(generics.UpdateAPIView): queryset = User.objects.all() - serializer_class = UserDetailSerializer + serializer_class = serializers.UserSerializer def perform_update(self, serializer): + from .utils import send_reset_ssh_key_mail user = self.get_object() user.is_public_key_valid = False user.save() - from .utils import send_reset_ssh_key_mail send_reset_ssh_key_mail(user) - -class UserUpdatePKApi(generics.UpdateAPIView): - queryset = User.objects.all() - serializer_class = UserPKUpdateSerializer - - def perform_update(self, serializer): - user = self.get_object() - user.private_key = serializer.validated_data['_public_key'] - user.save() +# +# class UserUpdatePKApi(generics.UpdateAPIView): +# queryset = User.objects.all() +# serializer_class = serializers.UserPKUpdateSerializer +# +# def perform_update(self, serializer): +# user = self.get_object() +# user.private_key = serializer.validated_data['_public_key'] +# user.save() +# +# +# class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView): +# queryset = UserGroup.objects.all() +# serializer_class = serializers.GroupDetailSerializer +# +# def perform_update(self, serializer): +# users = serializer.validated_data.get('users') +# if users: +# group = self.get_object() +# Note: use `list` method to force hitting the db. + # group_users = list(group.users.all()) + # serializer.save() + # group.users.set(users + group_users) + # group.save() + # return + # serializer.save() +# +# +# class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): +# queryset = User.objects.all() +# serializer_class = serializers.UserBulkUpdateSerializer +# permission_classes = (IsSuperUserOrTerminalUser,) +# +# def get(self, request, *args, **kwargs): +# return super(UserListUpdateApi, self).get(request, *args, **kwargs) +# +# +# class GroupListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): +# queryset = UserGroup.objects.all() +# serializer_class = serializers.GroupBulkUpdateSerializer -class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView): - queryset = UserGroup.objects.all() - serializer_class = GroupDetailSerializer - - def perform_update(self, serializer): - users = serializer.validated_data.get('users') - if users: - group = self.get_object() - # Note: use `list` method to force hitting the db. - group_users = list(group.users.all()) - serializer.save() - group.users.set(users + group_users) - group.save() - return - serializer.save() - - -class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): - queryset = User.objects.all() - serializer_class = UserBulkUpdateSerializer - permission_classes = (IsSuperUserOrTerminalUser,) - - # def get(self, request, *args, **kwargs): - # return super(UserListUpdateApi, self).get(request, *args, **kwargs) - - -class GroupListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): - queryset = UserGroup.objects.all() - serializer_class = GroupBulkUpdateSerializer - - -class DeleteUserFromGroupApi(generics.DestroyAPIView): - queryset = UserGroup.objects.all() - serializer_class = GroupDetailSerializer - - def destroy(self, request, *args, **kwargs): - group = self.get_object() - self.perform_destroy(group, **kwargs) - return Response(status=status.HTTP_204_NO_CONTENT) - - 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 UserTokenApi(APIView): +# class DeleteUserFromGroupApi(generics.DestroyAPIView): +# queryset = UserGroup.objects.all() +# serializer_class = serializers.GroupDetailSerializer +# +# def destroy(self, request, *args, **kwargs): +# group = self.get_object() +# self.perform_destroy(group, **kwargs) +# return Response(status=status.HTTP_204_NO_CONTENT) +# +# def perform_destroy(self, instance, **kwargs): +# user_id = kwargs.get('uid') +# user = get_object_or_404(User, id=user_id) +# instance.users.remove(user) +# +# +class UserAuthApi(APIView): permission_classes = () expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600 @@ -128,9 +127,9 @@ class UserTokenApi(APIView): password = request.data.get('password', '') public_key = request.data.get('public_key', '') remote_addr = request.META.get('REMOTE_ADDR', '') - remote_addr = base64.b64encode(remote_addr).replace('=', '') user = check_user_valid(username=username, password=password, public_key=public_key) + if user: token = cache.get('%s_%s' % (user.id, remote_addr)) if not token: diff --git a/apps/users/models.py b/apps/users/models.py index 531c8f402..934aaea61 100644 --- a/apps/users/models.py +++ b/apps/users/models.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals from django.conf import settings +from django.contrib.auth import logout from django.contrib.auth.hashers import make_password from django.contrib.auth.models import AbstractUser from django.core import signing diff --git a/apps/users/serializers.py b/apps/users/serializers.py index 64aab820a..0d02f85d5 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -9,10 +9,35 @@ from common.utils import signer 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): + group_display = serializers.SerializerMethodField() + active_display = serializers.SerializerMethodField() + groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all()) + class Meta: model = User - fields = ['avatar', 'wechat', 'phone', 'enable_otp', 'comment', 'is_active', 'name'] + list_serializer_class = BulkListSerializer + exclude = ['first_name', 'last_name', 'password', '_private_key', '_public_key'] + + def get_field_names(self, declared_fields, info): + fields = super(UserSerializer, self).get_field_names(declared_fields, info) + fields.extend(['group_display', 'get_role_display']) + return fields + + @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 UserPKUpdateSerializer(serializers.ModelSerializer): @@ -44,43 +69,43 @@ class UserAndGroupSerializer(serializers.ModelSerializer): fields = ['id', 'groups'] -class GroupDetailSerializer(serializers.ModelSerializer): - class Meta: - model = UserGroup - fields = ['id', 'name', 'comment', 'date_created', 'created_by', 'users'] +# 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() - +# 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() +# diff --git a/apps/users/templates/users/_user.html b/apps/users/templates/users/_user.html index 54c69a9d3..dd9f8232b 100644 --- a/apps/users/templates/users/_user.html +++ b/apps/users/templates/users/_user.html @@ -1,82 +1,52 @@ -{% extends 'base.html' %} +{% extends '_base_create_update.html' %} {% load i18n %} {% load static %} {% load bootstrap %} -{% block custom_head_css_js %} - - - -{% endblock %} +{% block form %} +
+ {% csrf_token %} +

{% trans 'Account' %}

+ {% block username %} {% endblock %} + {{ form.name|bootstrap_horizontal }} + {{ form.email|bootstrap_horizontal }} + {{ form.groups|bootstrap_horizontal }} -{% block content %} -
-
-
-
-
-
{% block user_template_title %}{% trans 'Create user' %}{% endblock %}
- -
-
- - {% csrf_token %} -

{% trans 'Account' %}

- {% block username %} {% endblock %} - {{ form.name|bootstrap_horizontal }} - {{ form.email|bootstrap_horizontal }} - {{ form.groups|bootstrap_horizontal }} +
+ {% block password %} {% endblock %} -
- {% block password %} {% endblock %} - -
-

{% trans 'Security and Role' %}

- {{ form.role|bootstrap_horizontal }} -
- -
-
- - -
- {{ form.date_expired.errors }} -
-
-{# {{ form.date_expired|bootstrap_horizontal }}#} -
- -
- {{ form.enable_otp }} -
-
-
-

{% trans 'Profile' %}

- {{ form.phone|bootstrap_horizontal }} - {{ form.wechat|bootstrap_horizontal }} - {{ form.comment|bootstrap_horizontal }} -
-
-
- - -
-
- -
-
-
-
-
+
+

{% trans 'Security and Role' %}

+ {{ form.role|bootstrap_horizontal }} +
+ +
+
+ + +
+ {{ form.date_expired.errors }} +
+
+ {{ form.date_expired|bootstrap_horizontal }}#} +
+ +
+ {{ form.enable_otp }} +
+
+
+

{% trans 'Profile' %}

+ {{ form.phone|bootstrap_horizontal }} + {{ form.wechat|bootstrap_horizontal }} + {{ form.comment|bootstrap_horizontal }} +
+
+
+ + +
+
+ {% endblock %} {% block custom_foot_js %} diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index b87e17a39..768746b08 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -18,20 +18,23 @@
- {{ user_object.name }} + {{ user.name }}
- + - + - + - {% if user_object.phone %} + {% if user.phone %} - + {% endif %} - {% if user_object.wechat %} + {% if user.wechat %} - + {% endif %} - + - + - + - + - + - +
{% trans 'Asset num' %} {% trans 'Lost connection' %} {% trans 'Comment' %}{% trans 'Action' %}
- +
{% trans 'Name' %}:{{ user_object.name }}{{ user.name }}
{% trans 'Username' %}:{{ user_object.username }}{{ user.username }}
{% trans 'Email' %}:{{ user_object.email }}{{ user.email }}
{% trans 'Phone' %}:{{ user_object.phone }}{{ user.phone }}
{% trans 'Wechat' %}:{{ user_object.wechat }}{{ user.wechat }}
{% trans 'Role' %}:{{ user_object.get_role_display }}{{ user.get_role_display }}
{% trans 'Date expired' %}:{{ user_object.date_expired|date:"Y-m-j H:i:s" }}{{ user.date_expired|date:"Y-m-j H:i:s" }}
{% trans 'Created by' %}:{{ user_object.created_by }}{{ user.created_by }}
{% trans 'Date joined' %}:{{ user_object.date_joined|date:"Y-m-j H:i:s" }}{{ user.date_joined|date:"Y-m-j H:i:s" }}
{% trans 'Last login' %}:{{ user_object.last_login|date:"Y-m-j H:i:s" }}{{ user.last_login|date:"Y-m-j H:i:s" }}
{% trans 'Comment' %}:{{ user_object.comment }}{{ user.comment }}
@@ -120,7 +123,7 @@
- +
- Click here reset password
- This link is valid for 1 hour. After it expires, request new one< + This link is valid for 1 hour. After it expires, request new one
--- diff --git a/apps/users/views.py b/apps/users/views.py index 6144ceace..7b472584e 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -64,7 +64,7 @@ class UserLoginView(FormView): @method_decorator(never_cache, name='dispatch') class UserLogoutView(TemplateView): - template_name = 'common/flash_message_standalone.html' + template_name = 'flash_message_standalone.html' def get(self, request, *args, **kwargs): auth_logout(request) @@ -142,7 +142,7 @@ class UserUpdateView(AdminUserRequiredMixin, UpdateView): class UserDetailView(AdminUserRequiredMixin, DetailView): model = User template_name = 'users/user_detail.html' - context_object_name = "user_object" + context_object_name = "user" def get_context_data(self, **kwargs): groups = UserGroup.objects.exclude(id__in=self.object.groups.all()) @@ -239,7 +239,7 @@ class UserForgotPasswordView(TemplateView): class UserForgotPasswordSendmailSuccessView(TemplateView): - template_name = 'common/flash_message_standalone.html' + template_name = 'flash_message_standalone.html' def get_context_data(self, **kwargs): context = { @@ -252,7 +252,7 @@ class UserForgotPasswordSendmailSuccessView(TemplateView): class UserResetPasswordSuccessView(TemplateView): - template_name = 'common/flash_message_standalone.html' + template_name = 'flash_message_standalone.html' def get_context_data(self, **kwargs): context = { From 47090eb0f788b2c8892563fb64f8168deda0652d Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 9 Nov 2016 23:49:10 +0800 Subject: [PATCH 07/23] update some user api --- apps/common/utils.py | 12 +++++ apps/static/js/jumpserver.js | 3 +- apps/users/api.py | 38 +++++++------- apps/users/forms.py | 55 ++++++++++----------- apps/users/serializers.py | 15 ++---- apps/users/templates/users/_user.html | 4 +- apps/users/templates/users/user_create.html | 6 +-- apps/users/templates/users/user_detail.html | 16 +++--- apps/users/templates/users/user_list.html | 2 +- apps/users/templates/users/user_update.html | 16 +++--- apps/users/urls.py | 12 ++--- apps/users/utils.py | 3 -- apps/users/views.py | 27 +++++----- run_server.py | 1 + 14 files changed, 102 insertions(+), 108 deletions(-) diff --git a/apps/common/utils.py b/apps/common/utils.py index 9a998adf9..2aa70507a 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -11,6 +11,7 @@ import datetime import paramiko import paramiko +import sshpubkeys from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \ BadSignature, SignatureExpired from django.shortcuts import reverse as dj_reverse @@ -236,4 +237,15 @@ def validate_ssh_private_key(text): 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 + + signer = Signer() \ No newline at end of file diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 6f1fff39c..600cfa606 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -191,7 +191,6 @@ function APIUpdateAttr(props) { props = props || {}; var success_message = props.success_message || 'Update Successfully!'; var fail_message = props.fail_message || 'Error occurred while updating.'; - console.log(props.body); $.ajax({ url: props.url, type: props.method || "PATCH", @@ -208,7 +207,7 @@ function APIUpdateAttr(props) { if (typeof props.error === 'function') { return props.error(errorThrown); } else { - toastr.error(fail_message); + toastr.error(textStatue); } }); return true; diff --git a/apps/users/api.py b/apps/users/api.py index 8c945ca53..b074ab2e1 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -29,10 +29,10 @@ class UserViewSet(BulkModelViewSet): permission_classes = (IsSuperUser,) -# class UserAndGroupEditApi(generics.RetrieveUpdateAPIView): -# queryset = User.objects.all() -# serializer_class = serializers.UserAndGroupSerializer -# permission_classes = (IsSuperUser,) +class UserAndGroupEditApi(generics.RetrieveUpdateAPIView): + queryset = User.objects.all() + serializer_class = serializers.UserAndGroupSerializer + permission_classes = (IsSuperUser,) class UserResetPasswordApi(generics.UpdateAPIView): @@ -50,7 +50,7 @@ class UserResetPasswordApi(generics.UpdateAPIView): send_reset_password_mail(user) -class UserResetPubKeyApi(generics.UpdateAPIView): +class UserResetPKApi(generics.UpdateAPIView): queryset = User.objects.all() serializer_class = serializers.UserSerializer @@ -61,16 +61,16 @@ class UserResetPubKeyApi(generics.UpdateAPIView): user.save() send_reset_ssh_key_mail(user) -# -# class UserUpdatePKApi(generics.UpdateAPIView): -# queryset = User.objects.all() -# serializer_class = serializers.UserPKUpdateSerializer -# -# def perform_update(self, serializer): -# user = self.get_object() -# user.private_key = serializer.validated_data['_public_key'] -# user.save() -# + +class UserUpdatePKApi(generics.UpdateAPIView): + queryset = User.objects.all() + serializer_class = serializers.UserPKUpdateSerializer + + def perform_update(self, serializer): + user = self.get_object() + user.public_key = serializer.validated_data['_public_key'] + user.save() + # # class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView): # queryset = UserGroup.objects.all() @@ -87,8 +87,8 @@ class UserResetPubKeyApi(generics.UpdateAPIView): # group.save() # return # serializer.save() -# -# + + # class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): # queryset = User.objects.all() # serializer_class = serializers.UserBulkUpdateSerializer @@ -96,12 +96,12 @@ class UserResetPubKeyApi(generics.UpdateAPIView): # # 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 DeleteUserFromGroupApi(generics.DestroyAPIView): # queryset = UserGroup.objects.all() diff --git a/apps/users/forms.py b/apps/users/forms.py index 08c14f860..51947372d 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -5,6 +5,7 @@ from django.contrib.auth.forms import AuthenticationForm from django.utils.translation import gettext_lazy as _ from captcha.fields import CaptchaField +from common.utils import validate_ssh_public_key from .models import User, UserGroup from .hands import AssetPermission @@ -17,7 +18,7 @@ class UserLoginForm(AuthenticationForm): captcha = CaptchaField() -class UserCreateForm(forms.ModelForm): +class UserCreateUpdateForm(forms.ModelForm): class Meta: model = User @@ -42,22 +43,22 @@ class UserBulkImportForm(forms.ModelForm): fields = ['username', 'email', 'enable_otp', 'role'] -class UserUpdateForm(forms.ModelForm): - - class Meta: - model = User - fields = [ - 'name', 'email', 'groups', 'wechat', - 'phone', 'enable_otp', 'role', 'date_expired', 'comment', - ] - help_texts = { - 'username': '* required', - 'email': '* required', - 'groups': '* required' - } - widgets = { - 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}), - } +# class UserUpdateForm(forms.ModelForm): +# +# class Meta: +# model = User +# fields = [ +# 'name', 'email', 'groups', 'wechat', +# 'phone', 'enable_otp', 'role', 'date_expired', 'comment', +# ] +# help_texts = { +# 'username': '* required', +# 'email': '* required', +# 'groups': '* required' +# } +# widgets = { +# 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}), +# } class UserGroupForm(forms.ModelForm): @@ -84,22 +85,14 @@ class UserKeyForm(forms.Form): public_key = forms.CharField( label=_('ssh public key'), max_length=5000, widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), - help_text=_('Paste your id_ras.pub here.')) + help_text=_('Paste your id_rsa.pub here.')) def clean_public_key(self): public_key = self.cleaned_data['public_key'] - if self.user._public_key and public_key == self.user.public_key: + if self.user.public_key and public_key == self.user.public_key: raise forms.ValidationError(_('Public key should not be the same as your old one.')) - from sshpubkeys import SSHKey - from sshpubkeys.exceptions import InvalidKeyException - ssh = SSHKey(public_key) - try: - ssh.parse() - except InvalidKeyException as e: - print e - raise forms.ValidationError(_('Not a valid ssh public key')) - except NotImplementedError as e: - print e + + if not validate_ssh_public_key(public_key): raise forms.ValidationError(_('Not a valid ssh public key')) return public_key @@ -126,3 +119,7 @@ class UserPrivateAssetPermissionForm(forms.ModelForm): 'system_users': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select system users')}), } + + +class FileForm(forms.Form): + excel = forms.FileField() diff --git a/apps/users/serializers.py b/apps/users/serializers.py index 0d02f85d5..9d148aa20 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -5,7 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin -from common.utils import signer +from common.utils import signer, validate_ssh_public_key from .models import User, UserGroup @@ -47,16 +47,9 @@ class UserPKUpdateSerializer(serializers.ModelSerializer): @staticmethod def validate__public_key(value): - from sshpubkeys import SSHKey - from sshpubkeys.exceptions import InvalidKeyException - ssh = SSHKey(value) - try: - ssh.parse() - except InvalidKeyException as e: - print e - raise serializers.ValidationError(_('Not a valid ssh public key')) - except NotImplementedError as e: - print e + if not validate_ssh_public_key(value): + print('Not a valid key') + print(value) raise serializers.ValidationError(_('Not a valid ssh public key')) return value diff --git a/apps/users/templates/users/_user.html b/apps/users/templates/users/_user.html index dd9f8232b..a9df818d4 100644 --- a/apps/users/templates/users/_user.html +++ b/apps/users/templates/users/_user.html @@ -6,7 +6,7 @@
{% csrf_token %}

{% trans 'Account' %}

- {% block username %} {% endblock %} + {{ form.username|bootstrap_horizontal }} {{ form.name|bootstrap_horizontal }} {{ form.email|bootstrap_horizontal }} {{ form.groups|bootstrap_horizontal }} @@ -27,7 +27,7 @@ {{ form.date_expired.errors }}
- {{ form.date_expired|bootstrap_horizontal }}#} + {{ form.date_expired|bootstrap_horizontal }}
diff --git a/apps/users/templates/users/user_create.html b/apps/users/templates/users/user_create.html index 07235d018..9f3cba36e 100644 --- a/apps/users/templates/users/user_create.html +++ b/apps/users/templates/users/user_create.html @@ -2,9 +2,9 @@ {% load i18n %} {% load bootstrap %} {% block user_template_title %}{% trans "Create user" %}{% endblock %} -{% block username %} - {{ form.username|bootstrap_horizontal }} -{% endblock %} +{#{% block username %}#} +{# {{ form.username|bootstrap_horizontal }}#} +{#{% endblock %}#} {% block password %}

{% trans 'Password' %}

diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 768746b08..c1cdbc5f5 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -225,7 +225,7 @@ jumpserver.selected_groups = {}; function updateUserGroups(user_groups) { -{# var the_url = "{% url 'users:group-user-edit-api' pk=user.id %}";#} + var the_url = "{% url 'users:group-user-edit-api' pk=user.id %}"; var body = { id: {{ user.id }}, groups: Object.assign([], user_groups) @@ -313,10 +313,11 @@ $(document).ready(function() { var user_groups = $('.bdg_user_group').map(function() { return $(this).data('gid'); }).get(); + console.log(user_groups); updateUserGroups(user_groups) }).on('click', '#btn_reset_password', function() { function doReset() { - var the_url = '{% url "users:user-reset-password-api" pk=user.id %}'; + var the_url = '{% url "users:api-user-reset-password" pk=user.id %}'; var body = {}; var success = function() { var msg = "{% trans "An e-mail has been sent to the user\'s mailbox." %}"; @@ -341,7 +342,7 @@ $(document).ready(function() { }); }).on('click', '#btn_reset_pk', function() { function doReset() { -{# var the_url = '{% url "users:user-reset-pk-api" pk=user.id %}';#} + var the_url = '{% url "users:api-user-reset-pk" pk=user.id %}'; var body = {}; var success = function() { var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}"; @@ -349,7 +350,7 @@ $(document).ready(function() { }; APIUpdateAttr({ url: the_url, - body: JSON.stringify(body), + body: body, success: success }); } @@ -367,7 +368,7 @@ $(document).ready(function() { }).on('click', '#btn_user_update_pk', function(){ var $this = $(this); var pk = $('#txt_pk').val(); -{# var the_url = '{% url "users:user-update-pk-api" pk=user.id %}';#} + var the_url = '{% url "users:api-user-update-pk" pk=user.id %}'; var body = {'_public_key': pk}; var success = function() { $('#txt_pk').val(''); @@ -375,8 +376,7 @@ $(document).ready(function() { var msg = "{% trans 'Successfully updated the SSH public key.' %}"; swal("{% trans 'User SSH Public Key Update' %}", msg, "success"); }; - var fail = function() { - var msg = "{% trans 'Failed to update the user\'s SSH public key.' %}"; + var fail = function(msg) { swal({ title: "{% trans 'User SSH Public Key Update' %}", text: msg, @@ -389,7 +389,7 @@ $(document).ready(function() { $('#txt_pk').focus(); } ); - } + }; APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail}); }); diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index 24b4f8e0c..087d27db0 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -2,8 +2,8 @@ {% load i18n static %} {% block table_search %}{% endblock %} {% block table_container %} - + diff --git a/apps/users/templates/users/user_update.html b/apps/users/templates/users/user_update.html index 36294a01f..b431fb013 100644 --- a/apps/users/templates/users/user_update.html +++ b/apps/users/templates/users/user_update.html @@ -1,14 +1,14 @@ {% extends 'users/_user.html' %} {% load i18n %} {% block user_template_title %}{% trans "Update user" %}{% endblock %} -{% block username %} -
- -
- -
-
-{% endblock %} +{#{% block username %}#} +{#
#} +{# #} +{#
#} +{# #} +{#
#} +{#
#} +{#{% endblock %}#} {% block password %}

{% trans 'Password' %}

diff --git a/apps/users/urls.py b/apps/users/urls.py index 2efd430f4..6fd732d8c 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -44,16 +44,16 @@ router.register(r'v1/users', api.UserViewSet, 'api-user') 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-api'), - url(r'^v1/users/(?P\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password-api'), - # url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='user-reset-pk-api'), - # url(r'^v1/users/(?P\d+)/update-pk/$', api.UserUpdatePKApi.as_view(), name='user-update-pk-api'), + url(r'^v1/users/token/$', api.UserAuthApi.as_view(), name='api-user-token'), + url(r'^v1/users/(?P\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='api-user-reset-password'), + url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='api-user-reset-pk'), + url(r'^v1/users/(?P\d+)/update-pk/$', api.UserUpdatePKApi.as_view(), name='api-user-update-pk'), # url(r'^v1/user-groups/$', api.GroupListUpdateApi.as_view(), name='user-group-bulk-update-api'), # url(r'^v1/user-groups/(?P\d+)/$', api.GroupDetailApi.as_view(), name='user-group-detail-api'), # url(r'^v1/user-groups/(?P\d+)/user/(?P\d+)/$', # api.DeleteUserFromGroupApi.as_view(), name='delete-user-from-group-api'), - # url(r'^v1/user-groups/(?P\d+)/users/$', - # api.UserAndGroupEditApi.as_view(), name='group-user-edit-api'), + url(r'^v1/users/(?P\d+)/groups/$', + api.UserAndGroupEditApi.as_view(), name='group-user-edit-api'), ] urlpatterns += router.urls diff --git a/apps/users/utils.py b/apps/users/utils.py index 1e9e2d8e4..7ce37a463 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -34,9 +34,6 @@ class AdminUserRequiredMixin(UserPassesTestMixin): return self.request.user.is_staff - - - def user_add_success_next(user): subject = _('Create account successfully') recipient_list = [user.email] diff --git a/apps/users/views.py b/apps/users/views.py index 7b472584e..006df3328 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -27,10 +27,9 @@ from formtools.wizard.views import SessionWizardView from common.mixins import JSONResponseMixin from common.utils import get_object_or_none, get_logger from .models import User, UserGroup -from .forms import UserCreateForm, UserUpdateForm, UserGroupForm, UserLoginForm, UserInfoForm, UserKeyForm, \ - UserPrivateAssetPermissionForm, UserBulkImportForm from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_password_mail from .hands import AssetPermission, get_user_granted_asset_groups, get_user_granted_assets +from . import forms logger = get_logger(__name__) @@ -41,7 +40,7 @@ logger = get_logger(__name__) @method_decorator(never_cache, name='dispatch') class UserLoginView(FormView): template_name = 'users/login.html' - form_class = UserLoginForm + form_class = forms.UserLoginForm redirect_field_name = 'next' def get(self, request, *args, **kwargs): @@ -92,7 +91,7 @@ class UserListView(AdminUserRequiredMixin, TemplateView): class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): model = User - form_class = UserCreateForm + form_class = forms.UserCreateUpdateForm template_name = 'users/user_create.html' success_url = reverse_lazy('users:user-list') success_message = _('Create user %s successfully.') @@ -118,7 +117,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): class UserUpdateView(AdminUserRequiredMixin, UpdateView): model = User - form_class = UserUpdateForm + form_class = forms.UserCreateUpdateForm template_name = 'users/user_update.html' context_object_name = 'user_object' success_url = reverse_lazy('users:user-list') @@ -162,7 +161,7 @@ class UserGroupListView(AdminUserRequiredMixin, TemplateView): class UserGroupCreateView(AdminUserRequiredMixin, CreateView): model = UserGroup - form_class = UserGroupForm + form_class = forms.UserGroupForm template_name = 'users/user_group_create.html' success_url = reverse_lazy('users:user-group-list') @@ -184,7 +183,7 @@ class UserGroupCreateView(AdminUserRequiredMixin, CreateView): class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView): model = UserGroup - form_class = UserGroupForm + form_class = forms.UserGroupForm template_name = 'users/user_group_create.html' success_url = reverse_lazy('users:user-group-list') @@ -294,7 +293,7 @@ class UserResetPasswordView(TemplateView): class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): template_name = 'users/first_login.html' - form_list = [UserInfoForm, UserKeyForm] + form_list = [forms.UserInfoForm, forms.UserKeyForm] file_storage = default_storage def dispatch(self, request, *args, **kwargs): @@ -346,7 +345,7 @@ class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMix paginate_by = settings.CONFIG.DISPLAY_PER_PAGE template_name = 'users/user_asset_permission.html' context_object_name = 'user_object' - form_class = UserPrivateAssetPermissionForm + form_class = forms.UserPrivateAssetPermissionForm def get(self, request, *args, **kwargs): self.object = self.get_object(queryset=User.objects.all()) @@ -379,7 +378,7 @@ class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMix class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView): - form_class = UserPrivateAssetPermissionForm + form_class = forms.UserPrivateAssetPermissionForm model = AssetPermission def get(self, request, *args, **kwargs): @@ -432,12 +431,8 @@ class UserGrantedAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView): return super(UserGrantedAssetView, self).get_context_data(**kwargs) -class FileForm(forms.Form): - excel = forms.FileField() - - class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): - form_class = FileForm + form_class = forms.FileForm def form_invalid(self, form): try: @@ -478,7 +473,7 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): 'enable_otp': True if enable_otp in ['T', '1', 1, True] else False, 'role': role } - form = UserBulkImportForm(data, auto_id=False) + form = forms.UserBulkImportForm(data, auto_id=False) if form.is_valid(): form.save() else: diff --git a/run_server.py b/run_server.py index e1d3240c3..689740f0f 100644 --- a/run_server.py +++ b/run_server.py @@ -28,6 +28,7 @@ def start_django(): def start_celery(): os.chdir(apps_dir) + os.environ.setdefault('C_FORCE_ROOT', '1') print('start celery') subprocess.call('celery -A common worker -l info', shell=True) From dde9ffb2ae7d6f36c39e8c26766feb4964abb11c Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 10 Nov 2016 00:18:57 +0800 Subject: [PATCH 08/23] Update perm api --- apps/assets/api.py | 18 +++++++++--------- apps/assets/serializers.py | 18 +----------------- apps/perms/api.py | 11 ++++++++++- apps/perms/urls.py | 7 +++++-- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/apps/assets/api.py b/apps/assets/api.py index 820f418ef..f6d6926bb 100644 --- a/apps/assets/api.py +++ b/apps/assets/api.py @@ -13,14 +13,6 @@ from .models import AssetGroup, Asset, IDC, SystemUser, AdminUser from . import serializers -class AssetGroupViewSet(viewsets.ModelViewSet): - """ API endpoint that allows AssetGroup to be viewed or edited. - some other comment - """ - queryset = AssetGroup.objects.all() - serializer_class = serializers.AssetGroupSerializer - - class AssetViewSet(viewsets.ModelViewSet): """API endpoint that allows Asset to be viewed or edited.""" queryset = Asset.objects.all() @@ -34,6 +26,14 @@ class AssetViewSet(viewsets.ModelViewSet): return queryset +class AssetGroupViewSet(viewsets.ModelViewSet): + """ API endpoint that allows AssetGroup to be viewed or edited. + some other comment + """ + queryset = AssetGroup.objects.all() + serializer_class = serializers.AssetGroupSerializer + + class IDCViewSet(viewsets.ModelViewSet): """API endpoint that allows IDC to be viewed or edited.""" queryset = IDC.objects.all() @@ -68,7 +68,7 @@ class SystemUserViewSet(viewsets.ModelViewSet): class AssetListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): queryset = Asset.objects.all() - serializer_class = serializers.AssetBulkUpdateSerializer + serializer_class = serializers.AssetSerializer permission_classes = (IsSuperUser,) diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index 9044c3cc6..256b3e87e 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -6,22 +6,11 @@ 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 AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer): 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) class AssetGroupSerializer(serializers.ModelSerializer): @@ -29,11 +18,6 @@ class AssetGroupSerializer(serializers.ModelSerializer): model = AssetGroup -class AssetSerializer(serializers.ModelSerializer): - class Meta: - model = Asset - - class AdminUserSerializer(serializers.ModelSerializer): class Meta: model = AdminUser diff --git a/apps/perms/api.py b/apps/perms/api.py index c40b9343e..fd9294500 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -3,17 +3,26 @@ from rest_framework.views import APIView, Response from rest_framework.generics import ListCreateAPIView +from rest_framework import viewsets from users.backends import IsValidUser, IsSuperUser from .utils import get_user_granted_assets, get_user_granted_asset_groups from .models import AssetPermission 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', '') + if user_id: + queryset = queryset.filter(users__id=user_id) + + return queryset + class UserAssetsApi(APIView): permission_classes = (IsValidUser,) diff --git a/apps/perms/urls.py b/apps/perms/urls.py index 186199b56..ae1c9d60a 100644 --- a/apps/perms/urls.py +++ b/apps/perms/urls.py @@ -1,6 +1,7 @@ # coding:utf-8 from django.conf.urls import url +from rest_framework import routers import views import api @@ -21,9 +22,10 @@ urlpatterns = [ name='asset-permission-asset-list'), ] +router = routers.DefaultRouter() +router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'api-asset-permission') + 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(), @@ -32,3 +34,4 @@ urlpatterns += [ name='user-asset-groups-assets'), ] +urlpatterns += router.urls From 69f2bf664bf22aefcfa6ffdef68a16e44759e6c0 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 10 Nov 2016 16:59:50 +0800 Subject: [PATCH 09/23] Finish user permission revoke --- .../templates/assets/admin_user_list.html | 2 +- apps/common/utils.py | 7 + apps/perms/api.py | 40 +++- apps/perms/serializers.py | 38 ++-- apps/perms/urls.py | 8 +- apps/perms/utils.py | 22 +- apps/static/js/jumpserver.js | 13 +- apps/users/forms.py | 4 +- apps/users/hands.py | 4 +- .../users/user_asset_permission.html | 203 ++++++++++-------- apps/users/templates/users/user_detail.html | 1 - apps/users/templates/users/user_list.html | 2 +- apps/users/urls.py | 6 +- apps/users/utils.py | 106 ++++----- apps/users/views.py | 62 +++--- 15 files changed, 289 insertions(+), 229 deletions(-) diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html index 0451d989f..fec3e6368 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -32,7 +32,7 @@ $(document).ready(function(){ ele: $('#admin_user_list_table'), columnDefs: [ {targets: 1, createdCell: function (td, cellData, rowData) { - var detail_btn = '' + cellData + ''; + var detail_btn = '' + cellData + ''; $(td).html(detail_btn.replace('99991937', rowData.id)); }}, {targets: 5, createdCell: function (td, cellData) { diff --git a/apps/common/utils.py b/apps/common/utils.py index 2aa70507a..269455d09 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -248,4 +248,11 @@ def validate_ssh_public_key(text): 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/perms/api.py b/apps/perms/api.py index fd9294500..9b95a1a1f 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -5,8 +5,10 @@ from rest_framework.views import APIView, Response from rest_framework.generics import ListCreateAPIView 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 from .models import AssetPermission +from .hands import User from . import serializers @@ -18,11 +20,41 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): def get_queryset(self): queryset = super(AssetPermissionViewSet, self).get_queryset() user_id = self.request.query_params.get('user', '') - if user_id: - queryset = queryset.filter(users__id=user_id) - + if user_id and user_id.isdigit(): + from users.models import User + self.user_id = user_id + user = get_object_or_none(User, id=int(user_id)) + if user: + queryset = get_user_asset_permissions(user) + print(queryset) 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(): + permission_id = int(permission_id) + user_id = int(user_id) + asset_permission = get_object_or_none(AssetPermission, id=permission_id) + user = get_object_or_none(User, id=user_id) + print(asset_permission) + print(user) + + if asset_permission and user: + asset_permission.users.remove(user) + return Response({'msg': 'success'}) + return Response({'msg': 'failed'}, status=404) + class UserAssetsApi(APIView): permission_classes = (IsValidUser,) 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.py b/apps/perms/urls.py index ae1c9d60a..faf51c737 100644 --- a/apps/perms/urls.py +++ b/apps/perms/urls.py @@ -26,10 +26,10 @@ router = routers.DefaultRouter() router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'api-asset-permission') urlpatterns += [ - 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/assets/$', api.UserAssetsApi.as_view(), name='user-assets'), + url(r'^v1/asset-permissions/user/revoke/', api.RevokeUserAssetPermission.as_view(), + name='revoke-user-asset-permission'), + 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..3bc9b246f 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() @@ -41,7 +42,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 @@ -112,7 +112,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 @@ -175,10 +174,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/js/jumpserver.js b/apps/static/js/jumpserver.js index 600cfa606..01d62b607 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -198,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(textStatue); - } + } }); - return true; + // return true; } // Sweet Alert for Delete diff --git a/apps/users/forms.py b/apps/users/forms.py index 51947372d..bc8079f3b 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -6,8 +6,8 @@ from django.utils.translation import gettext_lazy as _ from captcha.fields import CaptchaField from common.utils import validate_ssh_public_key +from perms.models import AssetPermission from .models import User, UserGroup -from .hands import AssetPermission class UserLoginForm(AuthenticationForm): @@ -101,7 +101,7 @@ class UserPrivateAssetPermissionForm(forms.ModelForm): def save(self, commit=True): self.instance = super(UserPrivateAssetPermissionForm, self).save(commit=commit) - # self.instance.private_for = 'U' + self.instance.private_for = 'U' self.instance.users = [self.user] self.instance.save() return self.instance diff --git a/apps/users/hands.py b/apps/users/hands.py index ad44762a0..41ddbeb3a 100644 --- a/apps/users/hands.py +++ b/apps/users/hands.py @@ -11,5 +11,5 @@ """ from terminal.models import Terminal -from perms.models import AssetPermission -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 diff --git a/apps/users/templates/users/user_asset_permission.html b/apps/users/templates/users/user_asset_permission.html index 4240ecb0b..47cc45803 100644 --- a/apps/users/templates/users/user_asset_permission.html +++ b/apps/users/templates/users/user_asset_permission.html @@ -17,16 +17,16 @@
+
- - - - - - - - + + + + + + + - {% for asset_permission in object_list %} - - - - - - - - - - - - {% endfor %}
{% trans 'Name' %}{% trans 'User ' %}{% trans 'User group ' %}{% trans 'Asset ' %}{% trans 'Asset group ' %}{% trans 'System user ' %} - {% trans 'Is valid' %} - + + {% trans 'Name' %}{% trans 'Asset' %}{% trans 'Asset group' %}{% trans 'System user' %}{% trans 'Valid' %}
- - {{ asset_permission.name }} - - {{ asset_permission.users.count}}{{ asset_permission.user_groups.count}}{{ asset_permission.assets.count }}{{ asset_permission.asset_groups.count }}{{ asset_permission.system_users.count }} - {% if asset_permission.is_valid %} - - {% else %} - - {% endif %} - - -
-
- {% include '_pagination.html' %} -
@@ -115,38 +86,38 @@ {% trans 'Quick create permission for user' %}
- - - - {% csrf_token %} - - - - - - - - - - - - - - - - -
- {{ form.name|bootstrap }} -
- {{ form.assets|bootstrap }} -
- {{ form.asset_groups|bootstrap }} -
- {{ form.system_users|bootstrap }} -
- -
- +{#
#} +{# #} +{# #} +{# {% csrf_token %}#} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{#
#} +{# {{ form.name|bootstrap }}#} +{#
#} +{# {{ form.assets|bootstrap }}#} +{#
#} +{# {{ form.asset_groups|bootstrap }}#} +{#
#} +{# {{ form.system_users|bootstrap }}#} +{#
#} +{# #} +{#
#} +{#
#}
@@ -159,25 +130,69 @@ {% endblock %} {% block custom_foot_js %} {% endblock %} \ No newline at end of file diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index c1cdbc5f5..d4e328465 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -313,7 +313,6 @@ $(document).ready(function() { var user_groups = $('.bdg_user_group').map(function() { return $(this).data('gid'); }).get(); - console.log(user_groups); updateUserGroups(user_groups) }).on('click', '#btn_reset_password', function() { function doReset() { diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index 087d27db0..cf2c0d2b7 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -53,7 +53,7 @@ $(document).ready(function(){ $(td).html(detail_btn.replace('99991937', rowData.id)); }}, {targets: 4, createdCell: function (td, cellData) { - var innerHtml = cellData.length > 8 ? cellData.substring(0, 8) + '...': cellData; + var innerHtml = cellData.length > 20 ? cellData.substring(0, 20) + '...': cellData; $(td).html('' + innerHtml + ''); }}, {targets: 6, createdCell: function (td, cellData) { diff --git a/apps/users/urls.py b/apps/users/urls.py index 6fd732d8c..dd39c87e9 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -19,13 +19,13 @@ urlpatterns = [ url(r'^user/(?P[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'), url(r'^user/(?P[0-9]+)/asset-permission$', views.UserAssetPermissionView.as_view(), name='user-asset-permission'), - url(r'^user/(?P[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), - name='user-asset-permission-create'), + # url(r'^user/(?P[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), + # name='user-asset-permission-create'), url(r'^user/(?P[0-9]+)/granted-asset', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), url(r'^user/(?P[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'), url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'), url(r'^import/$', views.BulkImportUserView.as_view(), name='user-import'), - url(r'^user/(?P[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'), + # url(r'^user/(?P[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'), url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'), url(r'^user/(?P[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'), diff --git a/apps/users/utils.py b/apps/users/utils.py index 7ce37a463..9797b5fe4 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -120,60 +120,60 @@ def send_reset_ssh_key_mail(user): send_mail_async.delay(subject, message, recipient_list, html_message=message) -def validate_ssh_pk(text): - """ - Expects a SSH private key as string. - Returns a boolean and a error message. - If the text is parsed as private key successfully, - (True,'') is returned. Otherwise, - (False, ) is returned. - - from https://github.com/githubnemo/SSH-private-key-validator/blob/master/validate.py - - """ - - if not text: - return False, 'No text given' - - startPattern = re.compile("^-----BEGIN [A-Z]+ PRIVATE KEY-----") - optionPattern = re.compile("^.+: .+") - contentPattern = re.compile("^([a-zA-Z0-9+/]{64}|[a-zA-Z0-9+/]{1,64}[=]{0,2})$") - endPattern = re.compile("^-----END [A-Z]+ PRIVATE KEY-----") - - def contentState(text): - for i in range(0, len(text)): - line = text[i] - - if endPattern.match(line): - if i == len(text) - 1 or len(text[i + 1]) == 0: - return True, '' - else: - return False, 'At end but content coming' - - elif not contentPattern.match(line): - return False, 'Wrong string in content section' - - return False, 'No content or missing end line' - - def optionState(text): - for i in range(0, len(text)): - line = text[i] - - if line[-1:] == '\\': - return optionState(text[i + 2:]) - - if not optionPattern.match(line): - return contentState(text[i + 1:]) - - return False, 'Expected option, found nothing' - - def startState(text): - if len(text) == 0 or not startPattern.match(text[0]): - return False, 'Header is wrong' - return optionState(text[1:]) - - return startState([n.strip() for n in text.splitlines()]) +# def validate_ssh_pk(text): +# """ +# Expects a SSH private key as string. +# Returns a boolean and a error message. +# If the text is parsed as private key successfully, +# (True,'') is returned. Otherwise, +# (False, ) is returned. +# +# from https://github.com/githubnemo/SSH-private-key-validator/blob/master/validate.py +# +# """ +# +# if not text: +# return False, 'No text given' +# +# startPattern = re.compile("^-----BEGIN [A-Z]+ PRIVATE KEY-----") +# optionPattern = re.compile("^.+: .+") +# contentPattern = re.compile("^([a-zA-Z0-9+/]{64}|[a-zA-Z0-9+/]{1,64}[=]{0,2})$") +# endPattern = re.compile("^-----END [A-Z]+ PRIVATE KEY-----") +# +# def contentState(text): +# for i in range(0, len(text)): +# line = text[i] +# +# if endPattern.match(line): +# if i == len(text) - 1 or len(text[i + 1]) == 0: +# return True, '' +# else: +# return False, 'At end but content coming' +# +# elif not contentPattern.match(line): +# return False, 'Wrong string in content section' +# +# return False, 'No content or missing end line' +# +# def optionState(text): +# for i in range(0, len(text)): +# line = text[i] +# +# if line[-1:] == '\\': +# return optionState(text[i + 2:]) +# +# if not optionPattern.match(line): +# return contentState(text[i + 1:]) +# +# return False, 'Expected option, found nothing' + # def startState(text): + # if len(text) == 0 or not startPattern.match(text[0]): + # return False, 'Header is wrong' + # return optionState(text[1:]) + # + # return startState([n.strip() for n in text.splitlines()]) +# def check_user_valid(**kwargs): password = kwargs.pop('password', None) diff --git a/apps/users/views.py b/apps/users/views.py index 006df3328..5a07efbbc 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -21,14 +21,14 @@ from django.views.generic.list import ListView from django.views.generic.edit import CreateView, DeleteView, UpdateView, FormView, SingleObjectMixin, \ FormMixin from django.views.generic.detail import DetailView - from formtools.wizard.views import SessionWizardView from common.mixins import JSONResponseMixin from common.utils import get_object_or_none, get_logger +from perms.models import AssetPermission from .models import User, UserGroup from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_password_mail -from .hands import AssetPermission, get_user_granted_asset_groups, get_user_granted_assets +# from .hands import AssetPermission, get_user_granted_asset_groups, get_user_granted_assets from . import forms @@ -341,32 +341,33 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): return form -class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView): - paginate_by = settings.CONFIG.DISPLAY_PER_PAGE +class UserAssetPermissionView(AdminUserRequiredMixin, DetailView): + model = User template_name = 'users/user_asset_permission.html' - context_object_name = 'user_object' - form_class = forms.UserPrivateAssetPermissionForm + context_object_name = 'user' - def get(self, request, *args, **kwargs): - self.object = self.get_object(queryset=User.objects.all()) - return super(UserAssetPermissionView, self).get(request, *args, **kwargs) + # form_class = forms.UserPrivateAssetPermissionForm - def get_asset_permission_inherit_from_user_group(self): - asset_permissions = set() - user_groups = self.object.groups.all() + # def get(self, request, *args, **kwargs): + # self.object = self.get_object(queryset=User.objects.all()) + # return super(UserAssetPermissionView, self).get(request, *args, **kwargs) - for user_group in user_groups: - for asset_permission in user_group.asset_permissions.all(): - setattr(asset_permission, 'is_inherit_from_user_groups', True) - setattr(asset_permission, 'inherit_from_user_groups', - getattr(asset_permission, b'inherit_from_user_groups', set()).add(user_group)) - asset_permissions.add(asset_permission) - return asset_permissions - - def get_queryset(self): - asset_permissions = set(self.object.asset_permissions.all()) \ - | self.get_asset_permission_inherit_from_user_group() - return list(asset_permissions) + # def get_asset_permission_inherit_from_user_group(self): + # asset_permissions = set() + # user_groups = self.object.groups.all() + # + # for user_group in user_groups: + # for asset_permission in user_group.asset_permissions.all(): + # setattr(asset_permission, 'is_inherit_from_user_groups', True) + # setattr(asset_permission, 'inherit_from_user_groups', + # getattr(asset_permission, b'inherit_from_user_groups', set()).add(user_group)) + # asset_permissions.add(asset_permission) + # return asset_permissions + # + # def get_queryset(self): + # asset_permissions = set(self.object.asset_permissions.all()) \ + # | self.get_asset_permission_inherit_from_user_group() + # return list(asset_permissions) def get_context_data(self, **kwargs): context = { @@ -414,18 +415,19 @@ class UserGrantedAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView): def get_queryset(self): # Convert format from {'asset': ['system_users'], ..} to # [('asset', ['system_users']), ('asset', ['system_users'])) - assets_granted = [(asset, system_users) for asset, system_users in - get_user_granted_assets(self.object).items()] + # assets_granted = [(asset, system_users) for asset, system_users in + # get_user_granted_assets(self.object).items()] - return assets_granted + # return assets_granted + return [] def get_context_data(self, **kwargs): - asset_groups = [(asset_group, system_users) for asset_group, system_users in - get_user_granted_asset_groups(self.object).items()] + # asset_groups = [(asset_group, system_users) for asset_group, system_users in + # get_user_granted_asset_groups(self.object).items()] context = { 'app': 'User', 'action': 'User granted asset', - 'asset_groups': asset_groups, + # 'asset_groups': asset_groups, } kwargs.update(context) return super(UserGrantedAssetView, self).get_context_data(**kwargs) From f70abec5ef05e192cb3e7e0eda703456736d200d Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 10 Nov 2016 22:06:23 +0800 Subject: [PATCH 10/23] Finish user detail --- apps/assets/serializers.py | 46 +++++- apps/jumpserver/settings.py | 13 ++ apps/perms/api.py | 87 +++-------- apps/perms/hands.py | 1 + apps/perms/urls.py | 6 +- apps/perms/utils.py | 13 +- apps/users/backends.py | 2 +- .../users/user_asset_permission.html | 74 ++++----- .../templates/users/user_granted_asset.html | 146 ++++++++---------- apps/users/urls.py | 6 +- apps/users/views.py | 46 +----- requirements.txt | 1 + 12 files changed, 192 insertions(+), 249 deletions(-) diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index 256b3e87e..be4183619 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -6,17 +6,17 @@ from common.mixins import BulkDeleteApiMixin from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin -class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer): - - class Meta(object): - model = Asset - list_serializer_class = BulkListSerializer - - 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: @@ -31,6 +31,7 @@ class AdminUserSerializer(serializers.ModelSerializer): 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) @@ -38,6 +39,37 @@ class SystemUserSerializer(serializers.ModelSerializer): 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 + + +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() diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 5893ee909..6f9880acc 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -284,6 +284,19 @@ BROKER_URL = 'redis://%(password)s%(host)s:%(port)s/3' % { } CELERY_RESULT_BACKEND = BROKER_URL + +# 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/perms/api.py b/apps/perms/api.py index 9b95a1a1f..eefb8a448 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -2,13 +2,13 @@ # from rest_framework.views import APIView, Response -from rest_framework.generics import ListCreateAPIView +from rest_framework.generics import ListAPIView from rest_framework import viewsets from users.backends import IsValidUser, IsSuperUser from common.utils import get_object_or_none from .utils import get_user_granted_assets, get_user_granted_asset_groups, get_user_asset_permissions from .models import AssetPermission -from .hands import User +from .hands import AssetGrantedSerializer, User, AssetGroup, Asset, AssetGroup from . import serializers @@ -21,7 +21,6 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): queryset = super(AssetPermissionViewSet, self).get_queryset() user_id = self.request.query_params.get('user', '') if user_id and user_id.isdigit(): - from users.models import User self.user_id = user_id user = get_object_or_none(User, id=int(user_id)) if user: @@ -43,12 +42,8 @@ class RevokeUserAssetPermission(APIView): user_id = str(request.data.get('user_id', '')) if permission_id and user_id and permission_id.isdigit() and user_id.isdigit(): - permission_id = int(permission_id) - user_id = int(user_id) - asset_permission = get_object_or_none(AssetPermission, id=permission_id) - user = get_object_or_none(User, id=user_id) - print(asset_permission) - print(user) + asset_permission = get_object_or_none(AssetPermission, id=int(permission_id)) + user = get_object_or_none(User, id=int(user_id)) if asset_permission and user: asset_permission.users.remove(user) @@ -56,33 +51,16 @@ class RevokeUserAssetPermission(APIView): return Response({'msg': 'failed'}, status=404) -class UserAssetsApi(APIView): +class UserAssetsApi(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) + return queryset + return [] class UserAssetsGroupsApi(APIView): @@ -97,46 +75,31 @@ 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 + 'asset_amount': 1 } - asset_groups_json = asset_groups.values() return Response(asset_groups_json, status=200) -class UserAssetsGroupAssetsApi(APIView): +class UserAssetsGroupAssetsApi(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 diff --git a/apps/perms/hands.py b/apps/perms/hands.py index 735faecfd..714aa39d5 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 def associate_system_users_with_assets(system_users, assets, asset_groups): diff --git a/apps/perms/urls.py b/apps/perms/urls.py index faf51c737..10bb972cd 100644 --- a/apps/perms/urls.py +++ b/apps/perms/urls.py @@ -26,11 +26,11 @@ router = routers.DefaultRouter() router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'api-asset-permission') urlpatterns += [ - url(r'^v1/user/assets/$', api.UserAssetsApi.as_view(), name='user-assets'), + url(r'^v1/user/assets/$', api.UserAssetsApi.as_view(), name='api-user-assets'), url(r'^v1/asset-permissions/user/revoke/', api.RevokeUserAssetPermission.as_view(), name='revoke-user-asset-permission'), - 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(), + url(r'^v1/user/asset-groups/$', api.UserAssetsGroupsApi.as_view(), name='api-user-asset-groups'), + url(r'^v1/user/asset-group/(?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 3bc9b246f..585b65808 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -8,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: {asset_group1: {system_user1, }, asset_group2: {system_user1, system_user2]} + :return: {asset_group1: {system_user1, }, asset_group2: {system_user1, system_user2}} """ asset_groups = {} asset_permissions = user_group.asset_permissions.all() @@ -21,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 @@ -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 @@ -131,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 @@ -153,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 diff --git a/apps/users/backends.py b/apps/users/backends.py index 00061bdec..688adcace 100644 --- a/apps/users/backends.py +++ b/apps/users/backends.py @@ -78,6 +78,7 @@ class AccessTokenAuthentication(authentication.BaseAuthentication): def authenticate_credentials(self, token, request): user_id = cache.get(token) + print('Auth id: %s' % user_id) user = get_object_or_none(User, id=user_id) if not user: @@ -87,7 +88,6 @@ class AccessTokenAuthentication(authentication.BaseAuthentication): remote_addr = base64.b16encode(remote_addr).replace('=', '') cache.set(token, user_id, self.expiration) cache.set('%s_%s' % (user.id, remote_addr), token, self.expiration) - return user, None diff --git a/apps/users/templates/users/user_asset_permission.html b/apps/users/templates/users/user_asset_permission.html index 47cc45803..8c6eba155 100644 --- a/apps/users/templates/users/user_asset_permission.html +++ b/apps/users/templates/users/user_asset_permission.html @@ -28,16 +28,6 @@
  • {% trans 'Login history' %}
  • -
    @@ -86,38 +76,38 @@ {% trans 'Quick create permission for user' %}
    -{#
    #} -{# #} -{# #} -{# {% csrf_token %}#} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{# #} -{#
    #} -{# {{ form.name|bootstrap }}#} -{#
    #} -{# {{ form.assets|bootstrap }}#} -{#
    #} -{# {{ form.asset_groups|bootstrap }}#} -{#
    #} -{# {{ form.system_users|bootstrap }}#} -{#
    #} -{# #} -{#
    #} -{#
    #} +
    + + + {% csrf_token %} + + + + + + + + + + + + + + + + +
    + {{ form.name|bootstrap }} +
    + {{ form.assets|bootstrap }} +
    + {{ form.asset_groups|bootstrap }} +
    + {{ form.system_users|bootstrap }} +
    + +
    +
    diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index 08feee6c1..7279231ff 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -17,34 +17,24 @@
    - +
    - - - + + - {% for asset_group, system_users in asset_groups %} - - - - - - - {% endfor %}
    {% trans 'Name' %}{% trans 'Asset count' %}{% trans 'System user' %} {% trans 'Name' %}{% trans 'Asset' %}
    - - {{ asset_group.name }} - - {{ asset_group.assets.count }}{{ system_users|join_attr:"name" }} - -
    @@ -161,25 +109,53 @@ {% endblock %} {% block custom_foot_js %} {% endblock %} \ No newline at end of file diff --git a/apps/users/urls.py b/apps/users/urls.py index dd39c87e9..07561e3ff 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -19,9 +19,9 @@ urlpatterns = [ url(r'^user/(?P[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'), url(r'^user/(?P[0-9]+)/asset-permission$', views.UserAssetPermissionView.as_view(), name='user-asset-permission'), - # url(r'^user/(?P[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), - # name='user-asset-permission-create'), - url(r'^user/(?P[0-9]+)/granted-asset', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), + url(r'^user/(?P[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), + name='user-asset-permission-create'), + url(r'^user/(?P[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'), url(r'^user/(?P[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'), url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'), url(r'^import/$', views.BulkImportUserView.as_view(), name='user-import'), diff --git a/apps/users/views.py b/apps/users/views.py index 5a07efbbc..212c2a262 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -341,33 +341,15 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): return form -class UserAssetPermissionView(AdminUserRequiredMixin, DetailView): +class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView): model = User template_name = 'users/user_asset_permission.html' context_object_name = 'user' + form_class = forms.UserPrivateAssetPermissionForm - # form_class = forms.UserPrivateAssetPermissionForm - - # def get(self, request, *args, **kwargs): - # self.object = self.get_object(queryset=User.objects.all()) - # return super(UserAssetPermissionView, self).get(request, *args, **kwargs) - - # def get_asset_permission_inherit_from_user_group(self): - # asset_permissions = set() - # user_groups = self.object.groups.all() - # - # for user_group in user_groups: - # for asset_permission in user_group.asset_permissions.all(): - # setattr(asset_permission, 'is_inherit_from_user_groups', True) - # setattr(asset_permission, 'inherit_from_user_groups', - # getattr(asset_permission, b'inherit_from_user_groups', set()).add(user_group)) - # asset_permissions.add(asset_permission) - # return asset_permissions - # - # def get_queryset(self): - # asset_permissions = set(self.object.asset_permissions.all()) \ - # | self.get_asset_permission_inherit_from_user_group() - # return list(asset_permissions) + def get(self, request, *args, **kwargs): + self.object = self.get_object(queryset=User.objects.all()) + return super(UserAssetPermissionView, self).get(request, *args, **kwargs) def get_context_data(self, **kwargs): context = { @@ -403,31 +385,19 @@ class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView): return reverse('users:user-asset-permission', kwargs={'pk': self.user_object.id}) -class UserGrantedAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView): - paginate_by = settings.CONFIG.DISPLAY_PER_PAGE +class UserGrantedAssetView(AdminUserRequiredMixin, DetailView): + model = User template_name = 'users/user_granted_asset.html' - context_object_name = 'user_object' + context_object_name = 'user' def get(self, request, *args, **kwargs): self.object = self.get_object(queryset=User.objects.all()) return super(UserGrantedAssetView, self).get(request, *args, **kwargs) - def get_queryset(self): - # Convert format from {'asset': ['system_users'], ..} to - # [('asset', ['system_users']), ('asset', ['system_users'])) - # assets_granted = [(asset, system_users) for asset, system_users in - # get_user_granted_assets(self.object).items()] - - # return assets_granted - return [] - def get_context_data(self, **kwargs): - # asset_groups = [(asset_group, system_users) for asset_group, system_users in - # get_user_granted_asset_groups(self.object).items()] context = { 'app': 'User', 'action': 'User granted asset', - # 'asset_groups': asset_groups, } kwargs.update(context) return super(UserGrantedAssetView, self).get_context_data(**kwargs) diff --git a/requirements.txt b/requirements.txt index f4fcdac3a..a89fd6191 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ django-formtools==1.0 sshpubkeys==2.2.0 djangorestframework-bulk==0.2.1 paramiko==2.0.2 +django-redis-cache==1.7.1 From 10aa8c40a76cf63b2742fa09b0aa29a99ac30da7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 10 Nov 2016 23:54:21 +0800 Subject: [PATCH 11/23] Add login log --- apps/audits/hands.py | 1 + apps/audits/models.py | 2 - apps/audits/tasks.py | 11 +++++ apps/audits/utils.py | 45 ++++++++++++++++++- apps/audits/views.py | 3 +- apps/templates/_footer.html | 2 +- apps/users/api.py | 10 +++-- apps/users/hands.py | 1 + .../templates/users/user_granted_asset.html | 2 +- apps/users/views.py | 6 ++- requirements.txt | 1 + 11 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 apps/audits/tasks.py 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..a43d261de --- /dev/null +++ b/apps/audits/tasks.py @@ -0,0 +1,11 @@ +#!/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) \ No newline at end of file diff --git a/apps/audits/utils.py b/apps/audits/utils.py index 2ad77a7e8..c495dd6a5 100644 --- a/apps/audits/utils.py +++ b/apps/audits/utils.py @@ -1,5 +1,48 @@ # ~*~ 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) + return True + except ValueError: + pass + return False + + +def write_login_log(username, name='', login_type='W', + terminal='', login_ip='', user_agent=''): + print(login_ip) + 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=3): + # 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..35371894b 100644 --- a/apps/audits/views.py +++ b/apps/audits/views.py @@ -11,8 +11,7 @@ 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 .hands import User, Asset, SystemUser, AdminUserRequiredMixin seven_days_ago_s = (datetime.datetime.now()-datetime.timedelta(7)).strftime('%m/%d/%Y') 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 @@
    - +
    diff --git a/apps/users/views.py b/apps/users/views.py index 212c2a262..12a71df21 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -28,7 +28,7 @@ from common.utils import get_object_or_none, get_logger from perms.models import AssetPermission from .models import User, UserGroup from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_password_mail -# from .hands import AssetPermission, get_user_granted_asset_groups, get_user_granted_assets +from .hands import write_login_log_async from . import forms @@ -50,6 +50,10 @@ class UserLoginView(FormView): def form_valid(self, form): auth_login(self.request, form.get_user()) + login_ip = self.request.META.get('REMOTE_ADDR', '') + user_agent = self.request.META.get('HTTP_USER_AGENT', '') + write_login_log_async.delay(self.request.user.username, self.request.user.name, + login_type='W', login_ip=login_ip, user_agent=user_agent) return redirect(self.get_success_url()) def get_success_url(self): diff --git a/requirements.txt b/requirements.txt index a89fd6191..698006f9a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ sshpubkeys==2.2.0 djangorestframework-bulk==0.2.1 paramiko==2.0.2 django-redis-cache==1.7.1 +requests==2.11.1 \ No newline at end of file From c6fc3dfe914c2871fb4ddcfb7bbdc99e6f1ba353 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 11 Nov 2016 02:13:13 +0800 Subject: [PATCH 12/23] Update terminal interval --- apps/jumpserver/settings.py | 1 - apps/terminal/api.py | 49 ++++++++++++------- apps/terminal/models.py | 5 +- apps/terminal/serializers.py | 12 ++--- .../templates/terminal/terminal_list.html | 19 +++++-- apps/terminal/urls.py | 11 +++-- 6 files changed, 60 insertions(+), 37 deletions(-) diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 6f9880acc..e06158852 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -284,7 +284,6 @@ BROKER_URL = 'redis://%(password)s%(host)s:%(port)s/3' % { } CELERY_RESULT_BACKEND = BROKER_URL - # Cache use redis CACHES = { 'default': { diff --git a/apps/terminal/api.py b/apps/terminal/api.py index e76cf2783..a895723d4 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -1,52 +1,63 @@ # -*- coding: utf-8 -*- # +from django.core.cache import cache +from django.conf import settings from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView from rest_framework.views import APIView, Response +from rest_framework.viewsets import ModelViewSet from rest_framework.permissions import AllowAny from common.utils import signer, get_object_or_none -from .models import Terminal, TerminalHeatbeat +from .models import Terminal, HeatbeatFailedLog from .serializers import TerminalSerializer, TerminalHeatbeatSerializer from .hands import IsSuperUserOrTerminalUser -class TerminalCreateListApi(ListCreateAPIView): +class TerminalViewSet(ModelViewSet): queryset = Terminal.objects.all() serializer_class = TerminalSerializer permission_classes = (AllowAny,) - def post(self, request, *args, **kwargs): + def create(self, request, *args, **kwargs): name = signer.unsign(request.data.get('name', '')) if name: terminal = get_object_or_none(Terminal, name=name) if terminal: + data = { + 'data': {'name': name, 'id': terminal.id}, + } if terminal.is_active: - return Response(data={'data': {'name': name, 'id': terminal.id}, - 'msg': 'Success'}, - status=200) + data['msg'] = 'Success' + return Response(data=data, status=200) else: - return Response(data={'data': {'name': name, 'ip': terminal.ip}, - 'msg': 'Need admin active it'}, - status=203) + data['msg'] = 'Need admin active this terminal' + return Response(data=data, status=203) else: ip = request.META.get('X-Real-IP') or request.META.get('REMOTE_ADDR') terminal = Terminal.objects.create(name=name, ip=ip) - return Response(data={'data': {'name': name, 'ip': terminal.ip}, - 'msg': 'Need admin active it'}, - status=201) + data = { + 'data': {'name': name, 'id': terminal.id}, + 'msg': 'Need admin active this terminal', + } + return Response(data=data, status=201) else: return Response(data={'msg': 'Secrete key invalid'}, status=401) -class TerminalHeatbeatApi(ListCreateAPIView): - model = TerminalHeatbeat - serializer_class = TerminalHeatbeatSerializer +class TerminalHeatbeatApi(APIView): + # model = HeatbeatFailedLog + # serializer_class = TerminalHeatbeatSerializer permission_classes = (IsSuperUserOrTerminalUser,) + def put(self, request, *args, **kwargs): + terminal_id = request.user.id + cache.set('terminal_heatbeat_%s' % terminal_id, settings.CONFIG.TERMINAL_HEATBEAT_INTERVAL * 3) + return Response({'msg': 'Success'}) -class TerminalApiDetailUpdateDetailApi(RetrieveUpdateDestroyAPIView): - queryset = Terminal.objects.all() - serializer_class = TerminalSerializer - permission_classes = (IsSuperUserOrTerminalUser,) + +# class TerminalApiDetailUpdateDetailApi(RetrieveUpdateDestroyAPIView): +# queryset = Terminal.objects.all() +# serializer_class = TerminalSerializer +# permission_classes = (IsSuperUserOrTerminalUser,) diff --git a/apps/terminal/models.py b/apps/terminal/models.py index 8b65ed6da..e64a43d89 100644 --- a/apps/terminal/models.py +++ b/apps/terminal/models.py @@ -36,9 +36,10 @@ class Terminal(models.Model): ordering = ['is_active'] -class TerminalHeatbeat(models.Model): +class HeatbeatFailedLog(models.Model): + """Terminal heatbeat failed log""" terminal = models.ForeignKey(Terminal, on_delete=models.CASCADE) date_created = models.DateTimeField(auto_now_add=True) class Meta: - db_table = 'terminal_heatbeat' + db_table = 'heatbeat_failed_log' diff --git a/apps/terminal/serializers.py b/apps/terminal/serializers.py index 5bac8e11b..d025f7254 100644 --- a/apps/terminal/serializers.py +++ b/apps/terminal/serializers.py @@ -3,26 +3,26 @@ from rest_framework import serializers -from .models import Terminal, TerminalHeatbeat +from .models import Terminal, HeatbeatFailedLog from .hands import ProxyLog class TerminalSerializer(serializers.ModelSerializer): - proxy_amount = serializers.SerializerMethodField() + proxy_online = serializers.SerializerMethodField() class Meta: model = Terminal - fields = ['id', 'name', 'ip', 'type', 'url', 'comment', 'is_active', - 'get_type_display', 'proxy_amount'] + fields = ['id', 'name', 'ip', 'type', 'url', 'comment', + 'is_active', 'get_type_display', 'proxy_online'] @staticmethod - def get_proxy_amount(obj): + def get_proxy_online(obj): return ProxyLog.objects.filter(terminal=obj.name, is_finished=False).count() class TerminalHeatbeatSerializer(serializers.ModelSerializer): class Meta: - model = TerminalHeatbeat + model = HeatbeatFailedLog fields = ['terminal'] diff --git a/apps/terminal/templates/terminal/terminal_list.html b/apps/terminal/templates/terminal/terminal_list.html index 60db0c104..0440a2d0c 100644 --- a/apps/terminal/templates/terminal/terminal_list.html +++ b/apps/terminal/templates/terminal/terminal_list.html @@ -27,8 +27,9 @@ - + + @@ -42,6 +43,7 @@ $(document).ready(function(){ var options = { ele: $('#terminal_list_table'), + buttons: [], columnDefs: [ {targets: 1, createdCell: function (td, cellData, rowData) { var detail_btn = '' + cellData + ''; @@ -54,7 +56,14 @@ $(document).ready(function(){ $(td).html('') } }}, - {targets: 6, createdCell: function (td, cellData, rowData) { + {targets: 6, createdCell: function (td, cellData) { + if (!cellData) { + $(td).html('') + } else { + $(td).html('') + } + }}, + {targets: 7, createdCell: function (td, cellData, rowData) { console.log(rowData.name); var update_btn = '{% trans "Update" %}' .replace('99991937', cellData); @@ -64,9 +73,9 @@ $(document).ready(function(){ $(td).html(update_btn + delete_btn) }} ], - ajax_url: '{% url "terminal:terminal-list-create-api" %}', + ajax_url: '{% url "terminal:api-terminal-list" %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "ip" }, {data: "get_type_display" }, - {data: "proxy_amount"}, {data: "is_active" }, {data: "id"}], + {data: "proxy_online"}, {data: "is_active" }, {data: 'is_active'}, {data: "id"}], op_html: $('#actions').html() }; jumpserver.initDataTable(options); @@ -74,7 +83,7 @@ $(document).ready(function(){ var $this = $(this); var uid = $this.data('uid'); var name = $(this).data('name'); - var the_url = '{% url "terminal:terminal-detail-update-delete-api" pk=99991937 %}'.replace('99991937', uid); + var the_url = '{% url "terminal:api-terminal-detail" pk=99991937 %}'.replace('99991937', uid); objectDelete($this, name, the_url) }) diff --git a/apps/terminal/urls.py b/apps/terminal/urls.py index f9005db9f..215e9f3cb 100644 --- a/apps/terminal/urls.py +++ b/apps/terminal/urls.py @@ -3,6 +3,7 @@ # from django.conf.urls import url +from rest_framework import routers import views import api @@ -14,9 +15,11 @@ urlpatterns = [ url(r'^terminal/(?P\d+)/update$', views.TerminalUpdateView.as_view(), name='terminal-update'), ] +router = routers.DefaultRouter() +router.register(r'v1/terminal', api.TerminalViewSet, 'api-terminal') + urlpatterns += [ - url(r'^v1/terminal/$', api.TerminalCreateListApi.as_view(), name='terminal-list-create-api'), - url(r'^v1/terminal/(?P\d+)/$', api.TerminalApiDetailUpdateDetailApi.as_view(), - name='terminal-detail-update-delete-api'), - url(r'^v1/terminal-heatbeat/$', api.TerminalHeatbeatApi.as_view(), name='terminal-heatbeat-api'), + url(r'^v1/terminal/heatbeat/$', api.TerminalHeatbeatApi.as_view(), name='terminal-heatbeat-api'), ] + +urlpatterns += router.urls From 26352174214df4a755a19330bc925c43c8aa0332 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 11 Nov 2016 09:48:47 +0800 Subject: [PATCH 13/23] Update celery --- apps/audits/tasks.py | 3 ++- apps/celerybeat-schedule.db | 0 apps/celerybeat.pid | 1 + apps/common/__init__.py | 2 +- apps/common/celery.py | 41 ++++++++++++++++++------------------- apps/common/tasks.py | 36 +++++++++++++++++++++++++++----- 6 files changed, 55 insertions(+), 28 deletions(-) create mode 100644 apps/celerybeat-schedule.db create mode 100644 apps/celerybeat.pid diff --git a/apps/audits/tasks.py b/apps/audits/tasks.py index a43d261de..f80aa5f75 100644 --- a/apps/audits/tasks.py +++ b/apps/audits/tasks.py @@ -8,4 +8,5 @@ from .utils import write_login_log @shared_task def write_login_log_async(*args, **kwargs): - write_login_log(*args, **kwargs) \ No newline at end of file + write_login_log(*args, **kwargs) + diff --git a/apps/celerybeat-schedule.db b/apps/celerybeat-schedule.db new file mode 100644 index 000000000..e69de29bb diff --git a/apps/celerybeat.pid b/apps/celerybeat.pid new file mode 100644 index 000000000..a5a55e28c --- /dev/null +++ b/apps/celerybeat.pid @@ -0,0 +1 @@ +64256 diff --git a/apps/common/__init__.py b/apps/common/__init__.py index 88fef8e67..02bbfb61f 100644 --- a/apps/common/__init__.py +++ b/apps/common/__init__.py @@ -2,5 +2,5 @@ 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 +# from .celery import app as celery_app diff --git a/apps/common/celery.py b/apps/common/celery.py index 9d006fe4a..681ce6a6a 100644 --- a/apps/common/celery.py +++ b/apps/common/celery.py @@ -1,21 +1,20 @@ -# ~*~ coding: utf-8 ~*~ - -from __future__ import absolute_import, unicode_literals - -import os - -from celery import Celery - -# set the default Django settings module for the 'celery' program. -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') - -from django.conf import settings - -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]) - +# # ~*~ coding: utf-8 ~*~ +# +# from __future__ import absolute_import, unicode_literals +# import os +# +# from celery import Celery +# +# # set the default Django settings module for the 'celery' program. +# os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') +# +# from django.conf import settings +# +# 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..43d35f313 100644 --- a/apps/common/tasks.py +++ b/apps/common/tasks.py @@ -1,11 +1,29 @@ from __future__ import absolute_import +import os from celery import shared_task +from celery.schedules import crontab from django.core.mail import send_mail +# from django.conf import settings +# from common import celery_app + + +from celery import Celery + +# set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') + from django.conf import settings +app = Celery('jumpserver') -@shared_task(name='send_mail_async') +# 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]) + + +@app.task def send_mail_async(*args, **kwargs): """ Using celery to send email async @@ -28,7 +46,15 @@ def send_mail_async(*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) +# @celery_app.task +# def test(arg): +# print(arg) + + +# celery_app.conf.beat_schedule = { +# 'add-every-30-seconds': { +# 'task': 'common.test', +# 'schedule': crontab(minute='*/1'), +# 'args': ('nihao',) +# } +# } From 419876b575d6a3510c31ea7409b182f729b7bf87 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 13 Nov 2016 22:34:38 +0800 Subject: [PATCH 14/23] Update heatbeat --- apps/celerybeat-schedule.db | 0 apps/celerybeat.pid | 1 - apps/common/__init__.py | 3 +-- apps/common/celery.py | 40 ++++++++++++++++++------------------ apps/common/tasks.py | 35 ++----------------------------- apps/jumpserver/settings.py | 14 +++++++++++++ apps/terminal/api.py | 29 +++++++++++++------------- apps/terminal/models.py | 5 ++--- apps/terminal/serializers.py | 17 +++++++++++---- apps/terminal/tasks.py | 5 +++++ apps/terminal/urls.py | 8 ++++---- run_server.py | 2 +- 12 files changed, 77 insertions(+), 82 deletions(-) delete mode 100644 apps/celerybeat-schedule.db delete mode 100644 apps/celerybeat.pid create mode 100644 apps/terminal/tasks.py diff --git a/apps/celerybeat-schedule.db b/apps/celerybeat-schedule.db deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/celerybeat.pid b/apps/celerybeat.pid deleted file mode 100644 index a5a55e28c..000000000 --- a/apps/celerybeat.pid +++ /dev/null @@ -1 +0,0 @@ -64256 diff --git a/apps/common/__init__.py b/apps/common/__init__.py index 02bbfb61f..b64e43e83 100644 --- a/apps/common/__init__.py +++ b/apps/common/__init__.py @@ -2,5 +2,4 @@ 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 - +from .celery import app as celery_app diff --git a/apps/common/celery.py b/apps/common/celery.py index 681ce6a6a..f4ea048e5 100644 --- a/apps/common/celery.py +++ b/apps/common/celery.py @@ -1,20 +1,20 @@ -# # ~*~ coding: utf-8 ~*~ -# -# from __future__ import absolute_import, unicode_literals -# import os -# -# from celery import Celery -# -# # set the default Django settings module for the 'celery' program. -# os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') -# -# from django.conf import settings -# -# 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]) -# +# ~*~ coding: utf-8 ~*~ + +from __future__ import absolute_import, unicode_literals +import os +from datetime import timedelta + +from celery import Celery + +# set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') + +from django.conf import settings + +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 43d35f313..11d0d711d 100644 --- a/apps/common/tasks.py +++ b/apps/common/tasks.py @@ -1,26 +1,9 @@ from __future__ import absolute_import -import os -from celery import shared_task -from celery.schedules import crontab +# from celery import shared_task from django.core.mail import send_mail -# from django.conf import settings -# from common import celery_app - - -from celery import Celery - -# set the default Django settings module for the 'celery' program. -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'jumpserver.settings') - from django.conf import settings - -app = Celery('jumpserver') - -# Using a string here means the worker will not have to -# pickle the object when using Windows. -app.config_from_object('django.conf:settings') -app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS]) +from common import celery_app as app @app.task @@ -44,17 +27,3 @@ def send_mail_async(*args, **kwargs): args = tuple(args) send_mail(*args, **kwargs) - - -# @celery_app.task -# def test(arg): -# print(arg) - - -# celery_app.conf.beat_schedule = { -# 'add-every-30-seconds': { -# 'task': 'common.test', -# 'schedule': crontab(minute='*/1'), -# 'args': ('nihao',) -# } -# } diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index e06158852..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,19 @@ 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': { diff --git a/apps/terminal/api.py b/apps/terminal/api.py index a895723d4..224dac98b 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -4,17 +4,17 @@ from django.core.cache import cache from django.conf import settings from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView +from rest_framework import viewsets from rest_framework.views import APIView, Response -from rest_framework.viewsets import ModelViewSet from rest_framework.permissions import AllowAny from common.utils import signer, get_object_or_none -from .models import Terminal, HeatbeatFailedLog +from .models import Terminal, TerminalHeatbeat from .serializers import TerminalSerializer, TerminalHeatbeatSerializer from .hands import IsSuperUserOrTerminalUser -class TerminalViewSet(ModelViewSet): +class TerminalViewSet(viewsets.ModelViewSet): queryset = Terminal.objects.all() serializer_class = TerminalSerializer permission_classes = (AllowAny,) @@ -46,18 +46,19 @@ class TerminalViewSet(ModelViewSet): return Response(data={'msg': 'Secrete key invalid'}, status=401) -class TerminalHeatbeatApi(APIView): - # model = HeatbeatFailedLog - # serializer_class = TerminalHeatbeatSerializer +class TerminalHeatbeatApi(ListCreateAPIView): + queryset = TerminalHeatbeat.objects.all() + serializer_class = TerminalHeatbeatSerializer permission_classes = (IsSuperUserOrTerminalUser,) - def put(self, request, *args, **kwargs): - terminal_id = request.user.id - cache.set('terminal_heatbeat_%s' % terminal_id, settings.CONFIG.TERMINAL_HEATBEAT_INTERVAL * 3) + +class TerminalHeatbeatViewSet(viewsets.ModelViewSet): + queryset = TerminalHeatbeat.objects.all() + serializer_class = TerminalHeatbeatSerializer + permission_classes = (IsSuperUserOrTerminalUser,) + + def create(self, request, *args, **kwargs): + terminal = request.user + TerminalHeatbeat.objects.create(terminal=terminal) return Response({'msg': 'Success'}) - -# class TerminalApiDetailUpdateDetailApi(RetrieveUpdateDestroyAPIView): -# queryset = Terminal.objects.all() -# serializer_class = TerminalSerializer -# permission_classes = (IsSuperUserOrTerminalUser,) diff --git a/apps/terminal/models.py b/apps/terminal/models.py index e64a43d89..8b65ed6da 100644 --- a/apps/terminal/models.py +++ b/apps/terminal/models.py @@ -36,10 +36,9 @@ class Terminal(models.Model): ordering = ['is_active'] -class HeatbeatFailedLog(models.Model): - """Terminal heatbeat failed log""" +class TerminalHeatbeat(models.Model): terminal = models.ForeignKey(Terminal, on_delete=models.CASCADE) date_created = models.DateTimeField(auto_now_add=True) class Meta: - db_table = 'heatbeat_failed_log' + db_table = 'terminal_heatbeat' diff --git a/apps/terminal/serializers.py b/apps/terminal/serializers.py index d025f7254..2903fa896 100644 --- a/apps/terminal/serializers.py +++ b/apps/terminal/serializers.py @@ -1,29 +1,38 @@ # -*- coding: utf-8 -*- # +from django.utils import timezone from rest_framework import serializers -from .models import Terminal, HeatbeatFailedLog +from .models import Terminal, TerminalHeatbeat from .hands import ProxyLog class TerminalSerializer(serializers.ModelSerializer): proxy_online = serializers.SerializerMethodField() + is_alive = serializers.SerializerMethodField() class Meta: model = Terminal fields = ['id', 'name', 'ip', 'type', 'url', 'comment', - 'is_active', 'get_type_display', 'proxy_online'] + 'is_active', 'get_type_display', 'proxy_online', 'is_alive'] @staticmethod def get_proxy_online(obj): return ProxyLog.objects.filter(terminal=obj.name, is_finished=False).count() + @staticmethod + def get_is_alive(obj): + log = obj.terminalheatbeat_set.last() + if timezone.now() - log.date_created > timezone.timedelta(seconds=600): + return False + else: + return True + class TerminalHeatbeatSerializer(serializers.ModelSerializer): class Meta: - model = HeatbeatFailedLog - fields = ['terminal'] + model = TerminalHeatbeat if __name__ == '__main__': diff --git a/apps/terminal/tasks.py b/apps/terminal/tasks.py new file mode 100644 index 000000000..69a80f243 --- /dev/null +++ b/apps/terminal/tasks.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + + diff --git a/apps/terminal/urls.py b/apps/terminal/urls.py index 215e9f3cb..03bc6ce9e 100644 --- a/apps/terminal/urls.py +++ b/apps/terminal/urls.py @@ -16,10 +16,10 @@ urlpatterns = [ ] router = routers.DefaultRouter() +router.register(r'v1/terminal/heatbeat', api.TerminalHeatbeatViewSet, 'api-terminal-heatbeat') router.register(r'v1/terminal', api.TerminalViewSet, 'api-terminal') - -urlpatterns += [ - url(r'^v1/terminal/heatbeat/$', api.TerminalHeatbeatApi.as_view(), name='terminal-heatbeat-api'), -] +# urlpatterns += [ +# url(r'v1/terminal/heatbeat/', api.TerminalHeatbeatApi.as_view(), name='api-terminal-heatbeat') +# ] urlpatterns += router.urls diff --git a/run_server.py b/run_server.py index 689740f0f..3d69eb5f0 100644 --- a/run_server.py +++ b/run_server.py @@ -30,7 +30,7 @@ def start_celery(): os.chdir(apps_dir) os.environ.setdefault('C_FORCE_ROOT', '1') print('start celery') - subprocess.call('celery -A common worker -l info', shell=True) + subprocess.call('celery -A common worker -B -s /tmp/celerybeat-schedule -l info ', shell=True) def main(): From 8d358a7a689834df14f027328f5041013bc6e192 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 14 Nov 2016 19:28:21 +0800 Subject: [PATCH 15/23] UPdate some --- .../templates/audits/login_log_list.html | 100 ++++++++++++++++++ apps/audits/urls.py | 1 + apps/audits/utils.py | 9 +- apps/audits/views.py | 42 +++++++- apps/static/js/jumpserver.js | 24 +++-- apps/templates/_nav.html | 6 +- .../users/user_asset_permission.html | 3 - apps/users/templates/users/user_detail.html | 1 - .../templates/users/user_granted_asset.html | 3 - apps/users/templates/users/user_list.html | 2 +- requirements.txt | 3 +- 11 files changed, 165 insertions(+), 29 deletions(-) create mode 100644 apps/audits/templates/audits/login_log_list.html 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 %} +
    +
    +
    + + + to + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    + +{% endblock %} + +{% block table_head %} +
    + + + + + + + +{% endblock %} + +{% block table_body %} + {% for login_log in login_log_list %} + + + + + + {% if login_log.login_type == 'W' %} + + {% else %} + + {% endif %} + + + + + {% endfor %} +{% endblock %} + +{% block custom_foot_js %} + + +{% endblock %} + diff --git a/apps/audits/urls.py b/apps/audits/urls.py index a752378ff..b3556733f 100644 --- a/apps/audits/urls.py +++ b/apps/audits/urls.py @@ -12,6 +12,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'), ] router = routers.DefaultRouter() diff --git a/apps/audits/utils.py b/apps/audits/utils.py index c495dd6a5..303ba18c9 100644 --- a/apps/audits/utils.py +++ b/apps/audits/utils.py @@ -10,16 +10,15 @@ from .models import LoginLog def validate_ip(ip): try: - ipaddress.ip_address(ip) + ipaddress.ip_address(ip.decode('utf-8')) return True except ValueError: - pass + print('valid error') return False def write_login_log(username, name='', login_type='W', terminal='', login_ip='', user_agent=''): - print(login_ip) if not (login_ip and validate_ip(login_ip)): login_ip = '0.0.0.0' if not name: @@ -29,11 +28,11 @@ def write_login_log(username, name='', login_type='W', terminal=terminal, login_city=login_city, user_agent=user_agent) -def get_ip_city(ip, timeout=3): +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 + url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' + ip r = requests.get(url, timeout=timeout) city = 'Unknown' if r.status_code == 200: diff --git a/apps/audits/views.py b/apps/audits/views.py index 35371894b..cc9755107 100644 --- a/apps/audits/views.py +++ b/apps/audits/views.py @@ -10,7 +10,7 @@ from django.urls import reverse_lazy from django.conf import settings from django.db.models import Q -from .models import ProxyLog, CommandLog +from .models import ProxyLog, CommandLog, LoginLog from .hands import User, Asset, SystemUser, AdminUserRequiredMixin @@ -151,3 +151,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/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 01d62b607..367e51d05 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -323,7 +323,9 @@ jumpserver.initDataTable = function (options) { } ], columnDefs: columnDefs, - select: options.select || {style: 'multi'}, + // select: 'single', + // select: options.select || {style: 'single'}, + // select: false, ajax: { url: options.ajax_url , dataSrc: "" @@ -331,16 +333,16 @@ jumpserver.initDataTable = function (options) { 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); - }).on('deselect', function(e, dt, type, indexes) { - var $node = table[ type ]( indexes ).nodes().to$(); - $node.find('input.ipt_check').prop('checked', false); - }).on('draw', function(){ - $('#op').html(options.op_html || ''); - $('#uc').html(options.uc_html || ''); - }); + // table.on('select', function(e, dt, type, indexes) { + // var $node = table[ type ]( indexes ).nodes().to$(); + // $node.find('input.ipt_check').prop('checked', true); + // }).on('deselect', function(e, dt, type, indexes) { + // var $node = table[ type ]( indexes ).nodes().to$(); + // $node.find('input.ipt_check').prop('checked', false); + // }).on('draw', function(){ + // $('#op').html(options.op_html || ''); + // $('#uc').html(options.uc_html || ''); + // }); $('.ipt_check_all').on('click', function() { if (!jumpserver.checked) { $(this).closest('table').find('.ipt_check').prop('checked', true); diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 5a49c0a91..4d62e185d 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -48,10 +48,10 @@
  • {% trans 'Command log' %}
  • -
  • - {% trans 'Login log' %} +
  • + {% trans 'Login log' %}
  • -
  • +
  • {% trans 'Admin log' %}
  • diff --git a/apps/users/templates/users/user_asset_permission.html b/apps/users/templates/users/user_asset_permission.html index 8c6eba155..9937b5af8 100644 --- a/apps/users/templates/users/user_asset_permission.html +++ b/apps/users/templates/users/user_asset_permission.html @@ -25,9 +25,6 @@
  • {% trans 'Asset granted' %}
  • -
  • - {% trans 'Login history' %} -
  • diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index d4e328465..49e9fb62e 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -24,7 +24,6 @@ {% trans 'Asset permission' %}
  • {% trans 'Asset granted' %}
  • -
  • {% trans 'Login history' %}
  • Update
  • diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index 0ffba37c3..1009d6540 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -25,9 +25,6 @@
  • {% trans 'Asset granted' %}
  • -
  • - {% trans 'Login history' %} -
  • diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index cf2c0d2b7..d38e1183b 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -54,7 +54,7 @@ $(document).ready(function(){ }}, {targets: 4, createdCell: function (td, cellData) { var innerHtml = cellData.length > 20 ? cellData.substring(0, 20) + '...': cellData; - $(td).html('' + innerHtml + ''); + $(td).html('' + innerHtml + ''); }}, {targets: 6, createdCell: function (td, cellData) { if (!cellData) { diff --git a/requirements.txt b/requirements.txt index 698006f9a..15ad75500 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,5 @@ sshpubkeys==2.2.0 djangorestframework-bulk==0.2.1 paramiko==2.0.2 django-redis-cache==1.7.1 -requests==2.11.1 \ No newline at end of file +requests==2.11.1 +itsdangerous==0.24 \ No newline at end of file From bd882c8befb29549c95e289299201d16f20c7c53 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 15 Nov 2016 00:48:48 +0800 Subject: [PATCH 16/23] User group detail --- apps/static/js/jumpserver.js | 25 +- apps/users/api.py | 16 +- apps/users/serializers.py | 36 ++- apps/users/templates/users/user_detail.html | 7 +- .../templates/users/user_group_detail.html | 249 ++++++++++-------- .../templates/users/user_group_list.html | 21 +- apps/users/templates/users/user_list.html | 11 +- apps/users/urls.py | 5 +- apps/users/views.py | 8 +- 9 files changed, 224 insertions(+), 154 deletions(-) diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 367e51d05..a4c8930b6 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -297,6 +297,7 @@ jumpserver.initDataTable = function (options) { url: options.i18n_url || "/static/js/plugins/dataTables/i18n/zh-hans.json" }, order: options.order || [[ 1, 'asc' ]], + select: options.select || 'multi', buttons: options.buttons || [ {extend: 'excel', exportOptions: { @@ -323,9 +324,6 @@ jumpserver.initDataTable = function (options) { } ], columnDefs: columnDefs, - // select: 'single', - // select: options.select || {style: 'single'}, - // select: false, ajax: { url: options.ajax_url , dataSrc: "" @@ -333,16 +331,16 @@ jumpserver.initDataTable = function (options) { 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); - // }).on('deselect', function(e, dt, type, indexes) { - // var $node = table[ type ]( indexes ).nodes().to$(); - // $node.find('input.ipt_check').prop('checked', false); - // }).on('draw', function(){ - // $('#op').html(options.op_html || ''); - // $('#uc').html(options.uc_html || ''); - // }); + table.on('select', function(e, dt, type, indexes) { + var $node = table[ type ]( indexes ).nodes().to$(); + $node.find('input.ipt_check').prop('checked', true); + }).on('deselect', function(e, dt, type, indexes) { + var $node = table[ type ]( indexes ).nodes().to$(); + $node.find('input.ipt_check').prop('checked', false); + }).on('draw', function(){ + $('#op').html(options.op_html || ''); + $('#uc').html(options.uc_html || ''); + }); $('.ipt_check_all').on('click', function() { if (!jumpserver.checked) { $(this).closest('table').find('.ipt_check').prop('checked', true); @@ -354,5 +352,6 @@ jumpserver.initDataTable = function (options) { table.rows().deselect(); } }); + return table; }; diff --git a/apps/users/api.py b/apps/users/api.py index aedf0635b..bc881d9b4 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -30,9 +30,9 @@ class UserViewSet(BulkModelViewSet): permission_classes = (IsSuperUser,) -class UserAndGroupEditApi(generics.RetrieveUpdateAPIView): +class UserUpdateGroupApi(generics.RetrieveUpdateAPIView): queryset = User.objects.all() - serializer_class = serializers.UserAndGroupSerializer + serializer_class = serializers.UserUpdateGroupSerializer permission_classes = (IsSuperUser,) @@ -72,7 +72,17 @@ class UserUpdatePKApi(generics.UpdateAPIView): user.public_key = serializer.validated_data['_public_key'] user.save() -# + +class UserGroupViewSet(viewsets.ModelViewSet): + queryset = UserGroup.objects.all() + serializer_class = serializers.UserGroupSerializer + + +class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView): + queryset = UserGroup.objects.all() + serializer_class = serializers.UserGroupUpdateMemeberSerializer + permission_classes = (IsSuperUser,) + # class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView): # queryset = UserGroup.objects.all() # serializer_class = serializers.GroupDetailSerializer diff --git a/apps/users/serializers.py b/apps/users/serializers.py index 9d148aa20..48fc820c6 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -16,8 +16,7 @@ from .models import User, UserGroup class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): - group_display = serializers.SerializerMethodField() - active_display = serializers.SerializerMethodField() + groups_display = serializers.SerializerMethodField() groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all()) class Meta: @@ -27,17 +26,16 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): def get_field_names(self, declared_fields, info): fields = super(UserSerializer, self).get_field_names(declared_fields, info) - fields.extend(['group_display', 'get_role_display']) + fields.extend(['groups_display', 'get_role_display', 'is_valid']) return fields @staticmethod - def get_group_display(obj): + def get_groups_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) + # @staticmethod + # def get_active_display(obj): + # return not (obj.is_expired and obj.is_active) class UserPKUpdateSerializer(serializers.ModelSerializer): @@ -54,7 +52,7 @@ class UserPKUpdateSerializer(serializers.ModelSerializer): return value -class UserAndGroupSerializer(serializers.ModelSerializer): +class UserUpdateGroupSerializer(serializers.ModelSerializer): groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all()) class Meta: @@ -62,6 +60,26 @@ class UserAndGroupSerializer(serializers.ModelSerializer): fields = ['id', 'groups'] +class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): + user_amount = serializers.SerializerMethodField() + + class Meta: + model = UserGroup + list_serializer_class = BulkListSerializer + + @staticmethod + def get_user_amount(obj): + return obj.users.count() + + +class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer): + users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all()) + + class Meta: + model = UserGroup + fields = ['id', 'users'] + + # class GroupDetailSerializer(serializers.ModelSerializer): # class Meta: # model = UserGroup diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 49e9fb62e..9b7a48761 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -224,9 +224,8 @@ jumpserver.selected_groups = {}; function updateUserGroups(user_groups) { - var the_url = "{% url 'users:group-user-edit-api' pk=user.id %}"; + var the_url = "{% url 'users:api-user-update-group' pk=user.id %}"; var body = { - id: {{ user.id }}, groups: Object.assign([], user_groups) }; var success = function(data) { @@ -245,13 +244,11 @@ function updateUserGroups(user_groups) { }); // clear jumpserver.selected_groups jumpserver.selected_groups = {}; - toastr.success('{% trans "UserGroup Update Success!" %}') }; APIUpdateAttr({ url: the_url, body: JSON.stringify(body), - success: success, - method: 'PUT' + success: success }); } $(document).ready(function() { diff --git a/apps/users/templates/users/user_group_detail.html b/apps/users/templates/users/user_group_detail.html index 3e00d0055..349233efc 100644 --- a/apps/users/templates/users/user_group_detail.html +++ b/apps/users/templates/users/user_group_detail.html @@ -11,52 +11,52 @@ {% endblock %} {% block content %} @@ -67,7 +67,7 @@ dd { @@ -75,7 +75,7 @@ dd {
    {% trans 'Name' %} {% trans 'IP' %} {% trans 'Type' %}{% trans 'proxy_amount' %}{% trans 'proxy online' %} {% trans 'Active' %}{% trans 'Alive' %} {% trans 'Action' %}
    {% trans 'ID' %}{% trans 'Username' %}{% trans 'Name' %}{% trans 'Type' %}{% trans 'UA' %}{% trans 'IP' %}{% trans 'City' %}{% trans 'Date' %}
    + {{ login_log.id }} +{# {{ login_log.id }}#} + {{ login_log.username }}{{ login_log.name }}{{ login_log.get_login_type_display }} + {{ login_log.user_agent | truncatechars:20 }} + {{ login_log.terminal }}{{ login_log.login_ip }}{{ login_log.login_city }}{{ login_log.date_login }}
    + + + + + + + + + + + + + + + + + + +
    {% trans 'Name' %}:{{ user_group.name }}
    {% trans 'Create by' %}:{{ user_group.created_by }}
    {% trans 'Date created' %}:{{ user_group.date_created }}
    {% trans 'Comment' %}:{{ user_group.comment }}
    -
    +
    - {% trans 'Quick modify' %} + {% trans 'User' %}
    - +
    + - - - - + + + {% for user in user_group.users.all %} + + + + + {% endfor %}
    {% trans 'Add User' %}: - - - + +
    {% trans 'Delete' %}: - - - + +
    {{ user.name }} + +
    @@ -147,35 +156,63 @@ dd { {% endblock %} {% block custom_foot_js %} diff --git a/apps/users/templates/users/user_group_list.html b/apps/users/templates/users/user_group_list.html index 43101fd87..2ff66b848 100644 --- a/apps/users/templates/users/user_group_list.html +++ b/apps/users/templates/users/user_group_list.html @@ -11,7 +11,6 @@ {% trans 'Name' %} {% trans 'User Amount' %} - {% trans 'Asset Amount' %} {% trans 'Comment' %} {% trans 'Action' %} @@ -43,11 +42,11 @@ $(document).ready(function() { var detail_btn = '' + cellData + ''; $(td).html(detail_btn.replace('99991937', rowData.id)); }}, - {targets: 4, createdCell: function (td, cellData) { - var innerHtml = cellData.length > 18 ? cellData.substring(0, 18) + '...': cellData; - $(td).html('' + innerHtml + ''); + {targets: 3, createdCell: function (td, cellData) { + var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData; + $(td).html('' + innerHtml + ''); }}, - {targets: 5, createdCell: function (td, cellData, rowData) { + {targets: 4, createdCell: function (td, cellData, rowData) { var update_btn = '{% trans "Update" %}'.replace('99991937', cellData); var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); if (rowData.id === 1) { @@ -56,9 +55,9 @@ $(document).ready(function() { $(td).html(update_btn + del_btn) } }}], - ajax_url: '{% url "users:user-group-bulk-update-api" %}', + ajax_url: '{% url "users:api-user-group-list" %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "user_amount"}, - {data: function(){return 999}}, {data: "comment"}, {data: "id" }], + {data: "comment"}, {data: "id" }], op_html: $('#actions').html() }; jumpserver.initDataTable(options); @@ -66,7 +65,7 @@ $(document).ready(function() { var $this = $(this); function doDelete() { var group_id = $this.data('gid'); - var the_url = "{% url 'users:user-group-detail-api' 99991937 %}".replace('99991937', group_id); + var the_url = "{% url 'users:api-user-group-detail' pk=99991937 %}".replace('99991937', group_id); var body = {}; var success = function() { var msg = "{% trans 'Group Deleted.' %}"; @@ -76,7 +75,7 @@ $(document).ready(function() { var fail = function() { var msg = "{% trans 'Group Deleting failed.' %}"; swal("{% trans 'Group Delete' %}", msg, "error"); - } + }; APIUpdateAttr({ url: the_url, body: JSON.stringify(body), @@ -105,8 +104,8 @@ $(document).ready(function() { }); if (plain_id_list === []) { return false; - }; - var the_url = "{% url 'users:user-group-bulk-update-api' %}"; + } + var the_url = "{% url 'users:api-user-group-list' %}"; function doDelete() { swal({ title: "{% trans 'Are you sure?' %}", diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index d38e1183b..c260bd25b 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -15,7 +15,6 @@ {% trans 'Username' %} {% trans 'Role' %} {% trans 'User group' %} - {% trans 'Asset num' %} {% trans 'Active' %} {% trans 'Action' %} @@ -56,14 +55,14 @@ $(document).ready(function(){ var innerHtml = cellData.length > 20 ? cellData.substring(0, 20) + '...': cellData; $(td).html('' + innerHtml + ''); }}, - {targets: 6, createdCell: function (td, cellData) { + {targets: 5, createdCell: function (td, cellData) { if (!cellData) { $(td).html('') } else { $(td).html('') } }}, - {targets: 7, createdCell: function (td, cellData, rowData) { + {targets: 6, createdCell: function (td, cellData, rowData) { var update_btn = '{% trans "Update" %}'.replace('99991937', cellData); var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); if (rowData.id === 1 || rowData.username == "admin") { @@ -73,8 +72,8 @@ $(document).ready(function(){ } }}], ajax_url: '{% url "users:api-user-list" %}', - columns: [{data: function(){return ""}}, {data: "username" }, {data: "name" }, {data: "get_role_display" }, {data: "group_display" }, - {data: function(){return 999}}, {data: "active_display" }, {data: "id" }], + columns: [{data: function(){return ""}}, {data: "username" }, {data: "name" }, {data: "get_role_display" }, + {data: "groups_display" }, {data: "is_valid" }, {data: "id" }], op_html: $('#actions').html() }; jumpserver.initDataTable(options); @@ -224,6 +223,8 @@ $(document).ready(function(){ $form.ajaxSubmit({success: success}); }).on('change', '#id_excel', function() { $(this).siblings('.help-block').remove(); +}).on('click', '.ipt_check', function () { + console.log('Hello') }) {% endblock %} diff --git a/apps/users/urls.py b/apps/users/urls.py index 07561e3ff..a4af64bd1 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -39,6 +39,7 @@ urlpatterns = [ router = BulkRouter() router.register(r'v1/users', api.UserViewSet, 'api-user') +router.register(r'v1/user-groups', api.UserGroupViewSet, 'api-user-group') # router.register(r'v1/user-groups', api.AssetViewSet, 'api-groups') @@ -53,7 +54,9 @@ urlpatterns += [ # url(r'^v1/user-groups/(?P\d+)/user/(?P\d+)/$', # api.DeleteUserFromGroupApi.as_view(), name='delete-user-from-group-api'), url(r'^v1/users/(?P\d+)/groups/$', - api.UserAndGroupEditApi.as_view(), name='group-user-edit-api'), + api.UserUpdateGroupApi.as_view(), name='api-user-update-group'), + url(r'^v1/user-groups/(?P\d+)/users/$', + api.UserGroupUpdateUserApi.as_view(), name='api-user-group-update-user'), ] urlpatterns += router.urls diff --git a/apps/users/views.py b/apps/users/views.py index 12a71df21..90468e5bb 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -216,10 +216,16 @@ class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView): class UserGroupDetailView(AdminUserRequiredMixin, DetailView): model = UserGroup + context_object_name = 'user_group' template_name = 'users/user_group_detail.html' def get_context_data(self, **kwargs): - context = {'app': _('Users'), 'action': _('User Group Detail')} + users = User.objects.exclude(id__in=self.object.users.all()) + context = { + 'app': _('Users'), + 'action': _('User Group Detail'), + 'users': users, + } kwargs.update(context) return super(UserGroupDetailView, self).get_context_data(**kwargs) From db8b0022fc32ce21a520354220a33479c05cb04c Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 15 Nov 2016 19:33:04 +0800 Subject: [PATCH 17/23] Update usergroup detail --- apps/assets/forms.py | 2 - apps/assets/serializers.py | 2 +- .../templates/assets/asset_group_create.html | 11 +- .../templates/assets/asset_modal_list.html | 2 - apps/assets/views.py | 8 +- apps/common/views.py | 3 - apps/perms/api.py | 82 ++++++-- apps/perms/hands.py | 2 +- apps/perms/urls.py | 17 +- apps/users/forms.py | 27 ++- apps/users/templates/users/user_detail.html | 60 +++--- .../users/user_group_asset_permission.html | 182 +++++++++++++++++ ...ate.html => user_group_create_update.html} | 14 +- .../templates/users/user_group_detail.html | 187 +++++------------- .../users/user_group_granted_asset.html | 158 +++++++++++++++ apps/users/templates/users/user_update.html | 8 - apps/users/urls.py | 6 + apps/users/views.py | 82 ++++++-- 18 files changed, 616 insertions(+), 237 deletions(-) create mode 100644 apps/users/templates/users/user_group_asset_permission.html rename apps/users/templates/users/{user_group_create.html => user_group_create_update.html} (78%) create mode 100644 apps/users/templates/users/user_group_granted_asset.html diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 62460146e..09fcc9a24 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -24,12 +24,10 @@ from common.utils import validate_ssh_private_key, ssh_pubkey_gen # 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) diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index be4183619..86acbff37 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -8,7 +8,7 @@ from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin class AssetGroupSerializer(serializers.ModelSerializer): assets_amount = serializers.SerializerMethodField() - assets = serializers.PrimaryKeyRelatedField(many=True, read_only=True) + # assets = serializers.PrimaryKeyRelatedField(many=True, read_only=True) class Meta: model = AssetGroup 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_modal_list.html b/apps/assets/templates/assets/asset_modal_list.html index f408149f5..c24452c0e 100644 --- a/apps/assets/templates/assets/asset_modal_list.html +++ b/apps/assets/templates/assets/asset_modal_list.html @@ -46,9 +46,7 @@
    +{% endblock %} +{% block content %} +
    +
    +
    +
    + +
    +
    +
    +
    + {% trans 'Asset permission of ' %} {{ user_group.name }} +
    + + + + + + + + + + +
    +
    +
    + + + + + + + + + + + + + + +
    + + {% trans 'Name' %}{% trans 'Asset' %}{% trans 'Asset group' %}{% trans 'System user' %}{% trans 'Valid' %}
    +
    +
    +
    +
    +
    +
    + {% trans 'Quick create permission for user group' %} +
    +
    +
    + + + {% csrf_token %} + + + + + + + + + + + + + + + + +
    + {{ form.name|bootstrap }} +
    + {{ form.assets|bootstrap }} +
    + {{ form.asset_groups|bootstrap }} +
    + {{ form.system_users|bootstrap }} +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +{% endblock %} +{% block custom_foot_js %} + +{% endblock %} \ No newline at end of file diff --git a/apps/users/templates/users/user_group_create.html b/apps/users/templates/users/user_group_create_update.html similarity index 78% rename from apps/users/templates/users/user_group_create.html rename to apps/users/templates/users/user_group_create_update.html index 2cc835ee4..abb18a239 100644 --- a/apps/users/templates/users/user_group_create.html +++ b/apps/users/templates/users/user_group_create_update.html @@ -19,20 +19,21 @@
    {% csrf_token %} {{ form.name|bootstrap_horizontal }} -
    - {% for user in users %} - + {% if user.id in group_users %} + + {% else %} + + {% endif %} {% endfor %}
    - {{ form.comment|bootstrap_horizontal }} -
    @@ -45,11 +46,12 @@
    + {% include "users/_select_user_modal.html" %} {% endblock %} {% block custom_foot_js %} {% endblock %} diff --git a/apps/users/templates/users/user_group_detail.html b/apps/users/templates/users/user_group_detail.html index 349233efc..cffa9930a 100644 --- a/apps/users/templates/users/user_group_detail.html +++ b/apps/users/templates/users/user_group_detail.html @@ -10,54 +10,6 @@ - {% endblock %} {% block content %}
    @@ -110,7 +71,7 @@
    -
    +
    {% trans 'User' %}
    @@ -120,7 +81,7 @@ - {% for user in users %} {% endfor %} @@ -129,7 +90,7 @@ - + @@ -138,7 +99,7 @@ {{ user.name }} - + {% endfor %} @@ -152,33 +113,32 @@
    - {% include "users/_select_user_modal.html" %} {% endblock %} {% block custom_foot_js %} {% endblock %} diff --git a/apps/users/templates/users/user_group_granted_asset.html b/apps/users/templates/users/user_group_granted_asset.html new file mode 100644 index 000000000..dbe031904 --- /dev/null +++ b/apps/users/templates/users/user_group_granted_asset.html @@ -0,0 +1,158 @@ +{% extends 'base.html' %} +{% load common_tags %} +{% load users_tags %} +{% load bootstrap %} +{% load static %} +{% load i18n %} + +{% block custom_head_css_js %} + + +{% endblock %} +{% block content %} +
    +
    +
    +
    + +
    +
    +
    +
    + {% trans 'Assets granted of ' %} {{ user_group.name }} +
    + + + + + + + + + + +
    +
    +
    + + + + + + + + + + + + + +
    {% trans 'Hostname' %}{% trans 'IP' %}{% trans 'Port' %}{% trans 'System user' %}{% trans 'Valid' %}
    +
    +
    +
    +
    +
    +
    + {% trans 'Asset groups granted of ' %} {{ user_group.name }} +
    + + + + + + + + + + +
    +
    +
    + + + + + + + + + + +
    {% trans 'Name' %}{% trans 'Asset' %}
    +
    +
    +
    +
    +
    +
    +
    +
    + +{% endblock %} +{% block custom_foot_js %} + +{% endblock %} \ No newline at end of file diff --git a/apps/users/templates/users/user_update.html b/apps/users/templates/users/user_update.html index b431fb013..95dbb680f 100644 --- a/apps/users/templates/users/user_update.html +++ b/apps/users/templates/users/user_update.html @@ -1,14 +1,6 @@ {% extends 'users/_user.html' %} {% load i18n %} {% block user_template_title %}{% trans "Update user" %}{% endblock %} -{#{% block username %}#} -{#
    #} -{# #} -{#
    #} -{# #} -{#
    #} -{#
    #} -{#{% endblock %}#} {% block password %}

    {% trans 'Password' %}

    diff --git a/apps/users/urls.py b/apps/users/urls.py index a4af64bd1..7ede4e38c 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -34,6 +34,12 @@ urlpatterns = [ url(r'^user-group/(?P[0-9]+)$', views.UserGroupDetailView.as_view(), name='user-group-detail'), url(r'^user-group/create$', views.UserGroupCreateView.as_view(), name='user-group-create'), url(r'^user-group/(?P[0-9]+)/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'), + url(r'^user-group/(?P[0-9]+)/asset-permission$', views.UserGroupAssetPermissionView.as_view(), + name='user-group-asset-permission'), + url(r'^user-group/(?P[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), + name='user-group-asset-permission-create'), + url(r'^user-group/(?P[0-9]+)/assets', views.UserGroupGrantedAssetView.as_view(), + name='user-group-granted-asset'), ] diff --git a/apps/users/views.py b/apps/users/views.py index 90468e5bb..0a0f4ff75 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -166,7 +166,7 @@ class UserGroupListView(AdminUserRequiredMixin, TemplateView): class UserGroupCreateView(AdminUserRequiredMixin, CreateView): model = UserGroup form_class = forms.UserGroupForm - template_name = 'users/user_group_create.html' + template_name = 'users/user_group_create_update.html' success_url = reverse_lazy('users:user-group-list') def get_context_data(self, **kwargs): @@ -188,14 +188,14 @@ class UserGroupCreateView(AdminUserRequiredMixin, CreateView): class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView): model = UserGroup form_class = forms.UserGroupForm - template_name = 'users/user_group_create.html' + template_name = 'users/user_group_create_update.html' success_url = reverse_lazy('users:user-group-list') def get_context_data(self, **kwargs): - self.object = self.get_object() + # self.object = self.get_object() context = super(UserGroupUpdateView, self).get_context_data(**kwargs) users = User.objects.all() - group_users = ",".join([str(u.id) for u in self.object.users.all()]) + group_users = [user.id for user in self.object.users.all()] context.update({ 'app': _('Users'), 'action': _('Update User Group'), @@ -370,29 +370,71 @@ class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMix return super(UserAssetPermissionView, self).get_context_data(**kwargs) +class UserGroupAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView): + model = UserGroup + template_name = 'users/user_group_asset_permission.html' + context_object_name = 'user_group' + form_class = forms.UserPrivateAssetPermissionForm + + def get(self, request, *args, **kwargs): + self.object = self.get_object(queryset=UserGroup.objects.all()) + return super(UserGroupAssetPermissionView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + 'app': 'Users', + 'action': 'User group asset permissions', + } + kwargs.update(context) + return super(UserGroupAssetPermissionView, self).get_context_data(**kwargs) + + class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView): form_class = forms.UserPrivateAssetPermissionForm model = AssetPermission def get(self, request, *args, **kwargs): - user_object = self.get_object(queryset=User.objects.all()) - return redirect(reverse('users:user-asset-permission', kwargs={'pk': user_object.id})) + user = self.get_object(queryset=User.objects.all()) + return redirect(reverse('users:user-asset-permission', kwargs={'pk': user.id})) def post(self, request, *args, **kwargs): - self.user_object = self.get_object(queryset=User.objects.all()) + self.user = self.get_object(queryset=User.objects.all()) return super(UserAssetPermissionCreateView, self).post(request, *args, **kwargs) def get_form(self, form_class=None): form = super(UserAssetPermissionCreateView, self).get_form(form_class=form_class) - form.user = self.user_object + form.user = self.user return form def form_invalid(self, form): - print(form.errors) - return redirect(reverse('users:user-asset-permission', kwargs={'pk': self.user_object.id})) + return redirect(reverse('users:user-asset-permission', kwargs={'pk': self.user.id})) def get_success_url(self): - return reverse('users:user-asset-permission', kwargs={'pk': self.user_object.id}) + return reverse('users:user-asset-permission', kwargs={'pk': self.user.id}) + + +class UserGroupAssetPermissionCreateView(AdminUserRequiredMixin, CreateView): + form_class = forms.UserPrivateAssetPermissionForm + model = AssetPermission + + def get(self, request, *args, **kwargs): + user_group = self.get_object(queryset=UserGroup.objects.all()) + return redirect(reverse('users:user-group-asset-permission', kwargs={'pk': user_group.id})) + + def post(self, request, *args, **kwargs): + self.user_group = self.get_object(queryset=UserGroup.objects.all()) + return super(UserGroupAssetPermissionCreateView, self).post(request, *args, **kwargs) + + def get_form(self, form_class=None): + form = super(UserGroupAssetPermissionCreateView, self).get_form(form_class=form_class) + form.user_group = self.user_group + return form + + def form_invalid(self, form): + return redirect(reverse('users:user-group-asset-permission', kwargs={'pk': self.user_group.id})) + + def get_success_url(self): + return reverse('users:user-group-asset-permission', kwargs={'pk': self.user_group.id}) class UserGrantedAssetView(AdminUserRequiredMixin, DetailView): @@ -413,6 +455,24 @@ class UserGrantedAssetView(AdminUserRequiredMixin, DetailView): return super(UserGrantedAssetView, self).get_context_data(**kwargs) +class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView): + model = User + template_name = 'users/user_group_granted_asset.html' + context_object_name = 'user_group' + + def get(self, request, *args, **kwargs): + self.object = self.get_object(queryset=UserGroup.objects.all()) + return super(UserGroupGrantedAssetView, self).get(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + context = { + 'app': 'User', + 'action': 'User group granted asset', + } + kwargs.update(context) + return super(UserGroupGrantedAssetView, self).get_context_data(**kwargs) + + class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): form_class = forms.FileForm From 99c36f2a2c959e5789de55049823c87e9def03b8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 15 Nov 2016 19:44:08 +0800 Subject: [PATCH 18/23] Fix bug --- apps/perms/api.py | 2 +- apps/users/templates/users/user_granted_asset.html | 6 +++--- apps/users/templates/users/user_group_granted_asset.html | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/perms/api.py b/apps/perms/api.py index 2da014580..bb499bc95 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -133,7 +133,7 @@ class MyGrantedAssetsGroupsApi(APIView): 'id': asset_group.id, 'name': asset_group.name, 'comment': asset_group.comment, - 'asset_amount': 1 + 'assets_amount': 1 } asset_groups_json = asset_groups.values() return Response(asset_groups_json, status=200) diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index 1009d6540..be5258c99 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -133,7 +133,7 @@ } }} ], - ajax_url: '{% url "perms:api-user-assets" %}', + ajax_url: '{% url "perms:api-user-assets" pk=user.id %}', columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port"}, {data: "system_users_join"}, {data: "is_active"}] }; @@ -148,8 +148,8 @@ $(td).html(detail_btn.replace('99991937', rowData.id)); }} ], - ajax_url: '{% url "perms:api-user-asset-groups" %}', - columns: [{data: function(){return ""}}, {data: "name" }, {data: "asset_amount" }] + ajax_url: '{% url "perms:api-user-asset-groups" pk=user.id %}', + columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }] }; jumpserver.initDataTable(options); jumpserver.initDataTable(options2); diff --git a/apps/users/templates/users/user_group_granted_asset.html b/apps/users/templates/users/user_group_granted_asset.html index dbe031904..2283fd91b 100644 --- a/apps/users/templates/users/user_group_granted_asset.html +++ b/apps/users/templates/users/user_group_granted_asset.html @@ -133,7 +133,7 @@ } }} ], - ajax_url: '{% url "perms:api-user-assets" %}', + ajax_url: '{% url "perms:api-user-assets" pk=user.id %}', columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port"}, {data: "system_users_join"}, {data: "is_active"}] }; @@ -148,7 +148,7 @@ $(td).html(detail_btn.replace('99991937', rowData.id)); }} ], - ajax_url: '{% url "perms:api-user-asset-groups" %}', + ajax_url: '{% url "perms:api-user-asset-groups" pk=user.id %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "asset_amount" }] }; jumpserver.initDataTable(options); From 3b30eb3278bd5650127156777395ff2f3d0bbeb7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 15 Nov 2016 23:09:36 +0800 Subject: [PATCH 19/23] Weiteng --- apps/perms/api.py | 32 ++++++++++++++++++- apps/perms/urls.py | 8 ++++- .../users/user_group_asset_permission.html | 2 +- .../templates/users/user_group_detail.html | 2 +- apps/users/urls.py | 2 +- apps/users/views.py | 2 +- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/apps/perms/api.py b/apps/perms/api.py index bb499bc95..b84901241 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -7,7 +7,7 @@ from rest_framework import viewsets from users.backends import IsValidUser, IsSuperUser 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_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 @@ -155,3 +155,33 @@ class MyAssetGroupAssetsApi(ListAPIView): 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(User, 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(User, 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/urls.py b/apps/perms/urls.py index 1c23b5987..cdd6e67ae 100644 --- a/apps/perms/urls.py +++ b/apps/perms/urls.py @@ -31,11 +31,17 @@ urlpatterns += [ url(r'^v1/user/my/asset-group/(?P[0-9]+)/assets/$', api.MyAssetGroupAssetsApi.as_view(), name='user-my-asset-group-assets'), - # Select user or user group permission of asset or asset group + # Select user permission of asset and asset group url(r'^v1/user/(?P[0-9]+)/assets/$', api.UserGrantedAssetsApi.as_view(), name='api-user-assets'), url(r'^v1/user/(?P[0-9]+)/asset-groups/$', api.UserGrantedAssetGroupsApi.as_view(), name='api-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='api-user-group-assets'), + url(r'^v1/user-group/(?P[0-9]+)/asset-groups/$', api.UserGroupGrantedAssetGroupsApi.as_view(), + name='api-user-group-asset-groups'), + + # Revoke permission api url(r'^v1/asset-permissions/user/revoke/', api.RevokeUserAssetPermission.as_view(), name='revoke-user-asset-permission'), diff --git a/apps/users/templates/users/user_group_asset_permission.html b/apps/users/templates/users/user_group_asset_permission.html index e2458ffa4..d9edcdd35 100644 --- a/apps/users/templates/users/user_group_asset_permission.html +++ b/apps/users/templates/users/user_group_asset_permission.html @@ -23,7 +23,7 @@ {% trans 'Asset permission' %}
  • - {% trans 'Asset granted' %} + {% trans 'Asset granted' %}
  • diff --git a/apps/users/templates/users/user_group_detail.html b/apps/users/templates/users/user_group_detail.html index cffa9930a..909d13f55 100644 --- a/apps/users/templates/users/user_group_detail.html +++ b/apps/users/templates/users/user_group_detail.html @@ -25,7 +25,7 @@ {% trans 'Asset permission' %}
  • - {% trans 'Asset granted' %} + {% trans 'Asset granted' %}
  • Update diff --git a/apps/users/urls.py b/apps/users/urls.py index 7ede4e38c..3e939eb3a 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -36,7 +36,7 @@ urlpatterns = [ url(r'^user-group/(?P[0-9]+)/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'), url(r'^user-group/(?P[0-9]+)/asset-permission$', views.UserGroupAssetPermissionView.as_view(), name='user-group-asset-permission'), - url(r'^user-group/(?P[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(), + url(r'^user-group/(?P[0-9]+)/asset-permission/create$', views.UserGroupAssetPermissionCreateView.as_view(), name='user-group-asset-permission-create'), url(r'^user-group/(?P[0-9]+)/assets', views.UserGroupGrantedAssetView.as_view(), name='user-group-granted-asset'), diff --git a/apps/users/views.py b/apps/users/views.py index 0a0f4ff75..3203363f8 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -414,7 +414,7 @@ class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView): class UserGroupAssetPermissionCreateView(AdminUserRequiredMixin, CreateView): - form_class = forms.UserPrivateAssetPermissionForm + form_class = forms.UserGroupPrivateAssetPermissionForm model = AssetPermission def get(self, request, *args, **kwargs): From 32d12b7f78be0e1938510c56f51e2e86455200ac Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 16 Nov 2016 12:33:54 +0800 Subject: [PATCH 20/23] Update user group asset permission --- apps/audits/views.py | 6 ++++-- apps/users/templates/users/user_group_asset_permission.html | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/audits/views.py b/apps/audits/views.py index cc9755107..61fb1ff97 100644 --- a/apps/audits/views.py +++ b/apps/audits/views.py @@ -14,8 +14,9 @@ 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): @@ -53,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'), diff --git a/apps/users/templates/users/user_group_asset_permission.html b/apps/users/templates/users/user_group_asset_permission.html index e2458ffa4..d9edcdd35 100644 --- a/apps/users/templates/users/user_group_asset_permission.html +++ b/apps/users/templates/users/user_group_asset_permission.html @@ -23,7 +23,7 @@ {% trans 'Asset permission' %}
  • - {% trans 'Asset granted' %} + {% trans 'Asset granted' %}
  • From a5e487441f4c22f54c98896259bdde4c457da6c1 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 16 Nov 2016 17:38:03 +0800 Subject: [PATCH 21/23] prepare change api name --- apps/assets/urls.py | 80 +++++++++---------- apps/audits/urls.py | 4 +- apps/jumpserver/urls.py | 15 ++-- apps/perms/api.py | 6 +- apps/perms/urls.py | 10 +-- apps/static/js/jumpserver.js | 36 ++------- .../users/user_group_asset_permission.html | 5 +- .../users/user_group_granted_asset.html | 6 +- apps/users/templates/users/user_list.html | 34 +++++--- apps/users/views.py | 14 +++- 10 files changed, 110 insertions(+), 100 deletions(-) diff --git a/apps/assets/urls.py b/apps/assets/urls.py index be53759ca..7852bd98b 100644 --- a/apps/assets/urls.py +++ b/apps/assets/urls.py @@ -9,66 +9,66 @@ 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/$', 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'^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'), + 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'), + 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'), + 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/$', 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'), ] router = routers.DefaultRouter() -router.register(r'v1/asset-groups', api.AssetGroupViewSet, 'api-asset-group') -router.register(r'v1/assets', api.AssetViewSet, 'api-asset') -router.register(r'v1/idc', api.IDCViewSet, 'api-idc') -router.register(r'v1/admin-user', api.AdminUserViewSet, 'api-admin-user') -router.register(r'v1/system-user', api.SystemUserViewSet, 'api-system-user') +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='api-asset-bulk-update'), + 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='api-system-user-auth'), + url(r'^v1/system-user/auth/', api.SystemUserAuthApi.as_view(), name='system-user-auth'), ] urlpatterns += router.urls diff --git a/apps/audits/urls.py b/apps/audits/urls.py index b3556733f..696bd5151 100644 --- a/apps/audits/urls.py +++ b/apps/audits/urls.py @@ -16,7 +16,7 @@ urlpatterns = [ ] router = routers.DefaultRouter() -router.register(r'v1/proxy-log', api.ProxyLogViewSet, 'api-proxy-log') -router.register(r'v1/command-log', api.CommandLogViewSet, 'api-command-log') +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/jumpserver/urls.py b/apps/jumpserver/urls.py index 81a5f9d15..8bd94764c 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', namespace='users')), + url(r'^assets/', include('assets.urls', namespace='assets')), + url(r'^perms/', include('perms.urls', namespace='perms')), + url(r'^audits/', include('audits.urls', namespace='audits')), + url(r'^terminal/', include('terminal.urls', namespace='terminal')), + url(r'^api/users/', include('users.urls', namespace='api-users')), + url(r'^api/assets/', include('assets.urls', namespace='api-assets')), + url(r'^api/perms/', include('perms.urls', namespace='api-perms')), + url(r'^api/audits/', include('audits.urls', namespace='api-audits')), + url(r'^api/terminal/', include('terminal.urls', namespace='api-terminal')), ] diff --git a/apps/perms/api.py b/apps/perms/api.py index b84901241..a2522f44b 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -21,7 +21,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): 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', '') + user_group_id = self.request.query_params.get('user_group', '') if user_id and user_id.isdigit(): user = get_object_or_404(User, id=int(user_id)) @@ -165,7 +165,7 @@ class UserGroupGrantedAssetsApi(ListAPIView): user_group_id = self.kwargs.get('pk', '') if user_group_id: - user_group = get_object_or_404(User, id=user_group_id) + user_group = get_object_or_404(UserGroup, id=user_group_id) queryset = get_user_group_granted_assets(user_group) else: queryset = [] @@ -180,7 +180,7 @@ class UserGroupGrantedAssetGroupsApi(ListAPIView): user_group_id = self.kwargs.get('pk', '') if user_group_id: - user_group = get_object_or_404(User, id=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 = [] diff --git a/apps/perms/urls.py b/apps/perms/urls.py index cdd6e67ae..dcaecd321 100644 --- a/apps/perms/urls.py +++ b/apps/perms/urls.py @@ -23,21 +23,21 @@ urlpatterns = [ ] router = routers.DefaultRouter() -router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'api-asset-permission') +router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission') urlpatterns += [ - url(r'^v1/user/my/assets/$', api.MyGrantedAssetsApi.as_view(), name='api-my-assets'), - url(r'^v1/user/my/asset-groups/$', api.MyGrantedAssetsGroupsApi.as_view(), name='api-my-asset-groups'), + 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='api-user-assets'), + 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='api-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='api-user-group-assets'), + 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='api-user-group-asset-groups'), diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index a4c8930b6..e107832f4 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -265,6 +265,7 @@ $.fn.serializeObject = function() }; var jumpserver = {}; jumpserver.checked = false; +jumpserver.selected = {}; jumpserver.initDataTable = function (options) { // options = { // ele *: $('#dataTable_id'), @@ -283,10 +284,9 @@ 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; @@ -298,31 +298,7 @@ jumpserver.initDataTable = function (options) { }, order: options.order || [[ 1, 'asc' ]], select: options.select || 'multi', - 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'); - } - } - ], + buttons: [], columnDefs: columnDefs, ajax: { url: options.ajax_url , @@ -334,9 +310,11 @@ jumpserver.initDataTable = function (options) { 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 || ''); diff --git a/apps/users/templates/users/user_group_asset_permission.html b/apps/users/templates/users/user_group_asset_permission.html index d9edcdd35..8ee12fdb9 100644 --- a/apps/users/templates/users/user_group_asset_permission.html +++ b/apps/users/templates/users/user_group_asset_permission.html @@ -154,7 +154,7 @@ $(td).html(btn) }} ], - ajax_url: '{% url "perms:api-asset-permission-list" %}?user-group={{ user_group.id }}', + ajax_url: '{% url "perms:api-asset-permission-list" %}?user_group={{ user_group.id }}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets" }, {data: "asset_groups"}, {data: "system_users"}, {data: "is_active"}, {data: "id"}] }; @@ -165,7 +165,6 @@ id: $this.attr('id'), user_group_id: {{ user_group.id }} }; - console.log(body); var the_url = "{% url 'perms:revoke-user-group-asset-permission' %}"; var success = function () { $this.closest('tr').remove(); @@ -177,6 +176,8 @@ success_message: '{% trans "Revoke Successfully!" %}', success: success }); + }).on('click', 'buttons-excel', function () { + console.log('click excel') }) {% endblock %} \ No newline at end of file diff --git a/apps/users/templates/users/user_group_granted_asset.html b/apps/users/templates/users/user_group_granted_asset.html index 2283fd91b..b214b5ca9 100644 --- a/apps/users/templates/users/user_group_granted_asset.html +++ b/apps/users/templates/users/user_group_granted_asset.html @@ -133,7 +133,7 @@ } }} ], - ajax_url: '{% url "perms:api-user-assets" pk=user.id %}', + 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"}] }; @@ -148,8 +148,8 @@ $(td).html(detail_btn.replace('99991937', rowData.id)); }} ], - ajax_url: '{% url "perms:api-user-asset-groups" pk=user.id %}', - columns: [{data: function(){return ""}}, {data: "name" }, {data: "asset_amount" }] + ajax_url: '{% url "perms:api-user-group-asset-groups" pk=user_group.id %}', + columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }] }; jumpserver.initDataTable(options); jumpserver.initDataTable(options2); diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index c260bd25b..85bfdacfa 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -1,6 +1,16 @@ {% extends '_base_list.html' %} {% load i18n static %} -{% block table_search %}{% endblock %} +{% block table_search %} + +{% endblock %} {% block table_container %} @@ -72,11 +82,19 @@ $(document).ready(function(){ } }}], ajax_url: '{% url "users:api-user-list" %}', - columns: [{data: function(){return ""}}, {data: "username" }, {data: "name" }, {data: "get_role_display" }, - {data: "groups_display" }, {data: "is_valid" }, {data: "id" }], - op_html: $('#actions').html() + columns: [{data: "id"}, {data: "username" }, {data: "name" }, {data: "get_role_display" }, + {data: "groups_display" }, {data: "is_valid" }, {data: "id" }] }; - jumpserver.initDataTable(options); + var table = jumpserver.initDataTable(options); + + $('.buttons-pdf').click(function () { + var users = []; + var rows = table.rows('.selected').data(); + $.each(rows, function (index, obj) { + users.push(obj.id) + }) + }); + }).on('click', '#btn_bulk_update', function(){ var action = $('#slct_bulk_update').val(); var $data_table = $('#user_list_table').DataTable(); @@ -216,15 +234,11 @@ $(document).ready(function(){ } else { $('#user_import_modal').modal('hide'); var $data_table = $('#user_list_table').DataTable(); - toastr.success("{% trans 'Import User Success.' %}") + toastr.success("{% trans 'Import User Success.' %}"); $data_table.ajax.reload(); } } $form.ajaxSubmit({success: success}); -}).on('change', '#id_excel', function() { - $(this).siblings('.help-block').remove(); -}).on('click', '.ipt_check', function () { - console.log('Hello') }) {% endblock %} diff --git a/apps/users/views.py b/apps/users/views.py index 3203363f8..642a824ef 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -2,13 +2,14 @@ from __future__ import unicode_literals +import csv from django import forms from django.conf import settings from django.contrib.auth import login as auth_login, logout as auth_logout from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.core.files.storage import default_storage -from django.http import HttpResponseRedirect +from django.http import HttpResponseRedirect, HttpResponse from django.shortcuts import reverse, redirect from django.utils.decorators import method_decorator from django.utils.translation import ugettext as _ @@ -530,3 +531,14 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): 'msg': 'ok' if not errors else '
    '.join(errors) } return self.render_json_response(data) + + +def down_csv(request, xx): + print(xx) + response = HttpResponse(content_type='application/csv') + response['Content-Disposition'] = 'attachment; filename="somefile.csv"' + writer = csv.writer(response) + writer.writerow(['First row', 'Foo', 'Bar', 'Baz']) + writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"]) + return response + From 5745c8cc4aa9bde9e8fb6c834febe2de411cce5b Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 16 Nov 2016 17:45:46 +0800 Subject: [PATCH 22/23] Finish url namespace change --- .../assets/templates/assets/admin_user_list.html | 2 +- apps/assets/templates/assets/asset_list.html | 4 ++-- apps/assets/templates/assets/idc_assets.html | 2 +- apps/assets/templates/assets/idc_list.html | 2 +- apps/perms/urls.py | 4 ++-- .../templates/terminal/terminal_list.html | 4 ++-- apps/terminal/urls.py | 4 ++-- .../templates/users/user_asset_permission.html | 2 +- apps/users/templates/users/user_detail.html | 12 ++++++------ .../templates/users/user_granted_asset.html | 4 ++-- .../users/user_group_asset_permission.html | 2 +- .../users/templates/users/user_group_detail.html | 2 +- .../users/user_group_granted_asset.html | 2 +- apps/users/templates/users/user_group_list.html | 6 +++--- apps/users/templates/users/user_list.html | 8 ++++---- apps/users/urls.py | 16 ++++++++-------- 16 files changed, 38 insertions(+), 38 deletions(-) diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html index fec3e6368..ea35972a0 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -52,7 +52,7 @@ $(document).ready(function(){ var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); $(td).html(script_btn + update_btn + del_btn) }}], - ajax_url: '{% url "assets:api-admin-user-list" %}', + 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() diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index 549a6bab3..aadd5c388 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -72,7 +72,7 @@ {% trans 'Update' %} - + {% trans 'Delete' %} @@ -190,7 +190,7 @@ var column2 = table.rows('.selected').data(); var id_list = []; var plain_id_list = []; - var the_url = "{% url 'assets:api-asset-bulk-update' %}"; + var the_url = "{% url 'api-assets:asset-bulk-update' %}"; for(var i=0;i') } }}], - ajax_url: '{% url "assets:api-asset-list" %}?idc={{ idc.id }}', + 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" }] }; diff --git a/apps/assets/templates/assets/idc_list.html b/apps/assets/templates/assets/idc_list.html index 924b05fc8..f9ee96bb7 100644 --- a/apps/assets/templates/assets/idc_list.html +++ b/apps/assets/templates/assets/idc_list.html @@ -50,7 +50,7 @@ $(document).ready(function(){ var del_btn = '{% trans "Delete" %}'.replace('99991937', cellData); $(td).html(update_btn + del_btn) }}], - ajax_url: '{% url "assets:api-idc-list" %}', + 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() diff --git a/apps/perms/urls.py b/apps/perms/urls.py index dcaecd321..fc6ef4855 100644 --- a/apps/perms/urls.py +++ b/apps/perms/urls.py @@ -34,12 +34,12 @@ urlpatterns += [ # 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='api-user-asset-groups'), + 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='api-user-group-asset-groups'), + name='user-group-asset-groups'), # Revoke permission api diff --git a/apps/terminal/templates/terminal/terminal_list.html b/apps/terminal/templates/terminal/terminal_list.html index 0440a2d0c..a0eaf8c5f 100644 --- a/apps/terminal/templates/terminal/terminal_list.html +++ b/apps/terminal/templates/terminal/terminal_list.html @@ -73,7 +73,7 @@ $(document).ready(function(){ $(td).html(update_btn + delete_btn) }} ], - ajax_url: '{% url "terminal:api-terminal-list" %}', + ajax_url: '{% url "api-terminal:terminal-list" %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "ip" }, {data: "get_type_display" }, {data: "proxy_online"}, {data: "is_active" }, {data: 'is_active'}, {data: "id"}], op_html: $('#actions').html() @@ -83,7 +83,7 @@ $(document).ready(function(){ var $this = $(this); var uid = $this.data('uid'); var name = $(this).data('name'); - var the_url = '{% url "terminal:api-terminal-detail" pk=99991937 %}'.replace('99991937', uid); + var the_url = '{% url "api-terminal:terminal-detail" pk=99991937 %}'.replace('99991937', uid); objectDelete($this, name, the_url) }) diff --git a/apps/terminal/urls.py b/apps/terminal/urls.py index 03bc6ce9e..b083b224f 100644 --- a/apps/terminal/urls.py +++ b/apps/terminal/urls.py @@ -16,8 +16,8 @@ urlpatterns = [ ] router = routers.DefaultRouter() -router.register(r'v1/terminal/heatbeat', api.TerminalHeatbeatViewSet, 'api-terminal-heatbeat') -router.register(r'v1/terminal', api.TerminalViewSet, 'api-terminal') +router.register(r'v1/terminal/heatbeat', api.TerminalHeatbeatViewSet, 'terminal-heatbeat') +router.register(r'v1/terminal', api.TerminalViewSet, 'terminal') # urlpatterns += [ # url(r'v1/terminal/heatbeat/', api.TerminalHeatbeatApi.as_view(), name='api-terminal-heatbeat') # ] diff --git a/apps/users/templates/users/user_asset_permission.html b/apps/users/templates/users/user_asset_permission.html index 9937b5af8..2704061b1 100644 --- a/apps/users/templates/users/user_asset_permission.html +++ b/apps/users/templates/users/user_asset_permission.html @@ -158,7 +158,7 @@ } }} ], - ajax_url: '{% url "perms:api-asset-permission-list" %}?user={{ user.id }}', + ajax_url: '{% url "api-perms:asset-permission-list" %}?user={{ user.id }}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets" }, {data: "asset_groups"}, {data: "system_users"}, {data: "is_active"}, {data: "id"}] }; diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 3b05450b6..f43bde4f5 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -228,7 +228,7 @@ jumpserver.groups_selected = {}; function updateUserGroups(groups) { - var the_url = "{% url 'users:api-user-update-group' pk=user.id %}"; + var the_url = "{% url 'api-users:user-update-group' pk=user.id %}"; var body = { groups: Object.assign([], groups) }; @@ -265,7 +265,7 @@ $(document).ready(function() { delete jumpserver.groups_selected[data.id] }) }).on('click', '#is_active', function() { - var the_url = "{% url 'users:api-user-detail' pk=user.id %}"; + var the_url = "{% url 'api-users:user-detail' pk=user.id %}"; var checked = $(this).prop('checked'); var body = { 'is_active': checked @@ -277,7 +277,7 @@ $(document).ready(function() { success_message: success }); }).on('click', '#enable_otp', function() { - var the_url = "{% url 'users:api-user-detail' pk=user.id %}"; + var the_url = "{% url 'api-users:user-detail' pk=user.id %}"; var checked = $(this).prop('checked'); var body = { 'enable_otp': checked @@ -316,7 +316,7 @@ $(document).ready(function() { updateUserGroups(groups) }).on('click', '#btn_reset_password', function() { function doReset() { - var the_url = '{% url "users:api-user-reset-password" pk=user.id %}'; + var the_url = '{% url "api-users:user-reset-password" pk=user.id %}'; var body = {}; var success = function() { var msg = "{% trans "An e-mail has been sent to the user\'s mailbox." %}"; @@ -341,7 +341,7 @@ $(document).ready(function() { }); }).on('click', '#btn_reset_pk', function() { function doReset() { - var the_url = '{% url "users:api-user-reset-pk" pk=user.id %}'; + var the_url = '{% url "api-users:user-reset-pk" pk=user.id %}'; var body = {}; var success = function() { var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}"; @@ -367,7 +367,7 @@ $(document).ready(function() { }).on('click', '#btn_user_update_pk', function(){ var $this = $(this); var pk = $('#txt_pk').val(); - var the_url = '{% url "users:api-user-update-pk" pk=user.id %}'; + var the_url = '{% url "api-users:user-update-pk" pk=user.id %}'; var body = {'_public_key': pk}; var success = function() { $('#txt_pk').val(''); diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index be5258c99..4315fe5a1 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -133,7 +133,7 @@ } }} ], - ajax_url: '{% url "perms:api-user-assets" pk=user.id %}', + ajax_url: '{% url "api-perms:user-assets" pk=user.id %}', columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port"}, {data: "system_users_join"}, {data: "is_active"}] }; @@ -148,7 +148,7 @@ $(td).html(detail_btn.replace('99991937', rowData.id)); }} ], - ajax_url: '{% url "perms:api-user-asset-groups" pk=user.id %}', + ajax_url: '{% url "api-perms:user-asset-groups" pk=user.id %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }] }; jumpserver.initDataTable(options); diff --git a/apps/users/templates/users/user_group_asset_permission.html b/apps/users/templates/users/user_group_asset_permission.html index 8ee12fdb9..5ef8adeb8 100644 --- a/apps/users/templates/users/user_group_asset_permission.html +++ b/apps/users/templates/users/user_group_asset_permission.html @@ -154,7 +154,7 @@ $(td).html(btn) }} ], - ajax_url: '{% url "perms:api-asset-permission-list" %}?user_group={{ user_group.id }}', + ajax_url: '{% url "api-perms:asset-permission-list" %}?user_group={{ user_group.id }}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets" }, {data: "asset_groups"}, {data: "system_users"}, {data: "is_active"}, {data: "id"}] }; diff --git a/apps/users/templates/users/user_group_detail.html b/apps/users/templates/users/user_group_detail.html index 909d13f55..145d9bfe8 100644 --- a/apps/users/templates/users/user_group_detail.html +++ b/apps/users/templates/users/user_group_detail.html @@ -119,7 +119,7 @@ jumpserver.users_selected = {}; function updateGroupMember(users) { - var the_url = "{% url 'users:api-user-group-update-user' pk=user_group.id %}"; + var the_url = "{% url 'api-users:user-group-update-user' pk=user_group.id %}"; var body = { users: Object.assign([], users) }; diff --git a/apps/users/templates/users/user_group_granted_asset.html b/apps/users/templates/users/user_group_granted_asset.html index b214b5ca9..293bb1511 100644 --- a/apps/users/templates/users/user_group_granted_asset.html +++ b/apps/users/templates/users/user_group_granted_asset.html @@ -148,7 +148,7 @@ $(td).html(detail_btn.replace('99991937', rowData.id)); }} ], - ajax_url: '{% url "perms:api-user-group-asset-groups" pk=user_group.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); diff --git a/apps/users/templates/users/user_group_list.html b/apps/users/templates/users/user_group_list.html index 2ff66b848..fc88a4abc 100644 --- a/apps/users/templates/users/user_group_list.html +++ b/apps/users/templates/users/user_group_list.html @@ -55,7 +55,7 @@ $(document).ready(function() { $(td).html(update_btn + del_btn) } }}], - ajax_url: '{% url "users:api-user-group-list" %}', + ajax_url: '{% url "api-users:user-group-list" %}', columns: [{data: function(){return ""}}, {data: "name" }, {data: "user_amount"}, {data: "comment"}, {data: "id" }], op_html: $('#actions').html() @@ -65,7 +65,7 @@ $(document).ready(function() { var $this = $(this); function doDelete() { var group_id = $this.data('gid'); - var the_url = "{% url 'users:api-user-group-detail' pk=99991937 %}".replace('99991937', group_id); + var the_url = "{% url 'api-users:user-group-detail' pk=99991937 %}".replace('99991937', group_id); var body = {}; var success = function() { var msg = "{% trans 'Group Deleted.' %}"; @@ -105,7 +105,7 @@ $(document).ready(function() { if (plain_id_list === []) { return false; } - var the_url = "{% url 'users:api-user-group-list' %}"; + var the_url = "{% url 'api-users:user-group-list' %}"; function doDelete() { swal({ title: "{% trans 'Are you sure?' %}", diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index 85bfdacfa..b0c8cee92 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -81,7 +81,7 @@ $(document).ready(function(){ $(td).html(update_btn + del_btn) } }}], - ajax_url: '{% url "users:api-user-list" %}', + ajax_url: '{% url "api-users:user-list" %}', columns: [{data: "id"}, {data: "username" }, {data: "name" }, {data: "get_role_display" }, {data: "groups_display" }, {data: "is_valid" }, {data: "id" }] }; @@ -107,7 +107,7 @@ $(document).ready(function(){ if (id_list === []) { return false; } - var the_url = "{% url 'users:api-user-list' %}"; + var the_url = "{% url 'api-users:user-list' %}"; function doDeactive() { var body = $.each(id_list, function(index, user_object) { user_object['is_active'] = false; @@ -160,7 +160,7 @@ $(document).ready(function(){ var $this = $(this); function doDelete() { var uid = $this.data('uid'); - var the_url = '{% url "users:api-user-detail" pk=99991937 %}'.replace('99991937', uid); + var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid); var body = {}; var success = function() { var msg = "{% trans 'User Deleted.' %}"; @@ -215,7 +215,7 @@ $(document).ready(function(){ if (post_list === []) { return false } - var the_url = "{% url 'users:api-user-list' %}"; + var the_url = "{% url 'api-users:user-list' %}"; var success = function() { var msg = "{% trans 'The selected users has been updated successfully.' %}"; swal("{% trans 'User Updated' %}", msg, "success"); diff --git a/apps/users/urls.py b/apps/users/urls.py index 3e939eb3a..065bf24cb 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -44,25 +44,25 @@ urlpatterns = [ router = BulkRouter() -router.register(r'v1/users', api.UserViewSet, 'api-user') -router.register(r'v1/user-groups', api.UserGroupViewSet, 'api-user-group') +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='api-user-token'), - url(r'^v1/users/(?P\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='api-user-reset-password'), - url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='api-user-reset-pk'), - url(r'^v1/users/(?P\d+)/update-pk/$', api.UserUpdatePKApi.as_view(), name='api-user-update-pk'), + url(r'^v1/users/token/$', api.UserAuthApi.as_view(), name='user-token'), + url(r'^v1/users/(?P\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'), + url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='user-reset-pk'), + url(r'^v1/users/(?P\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\d+)/$', api.GroupDetailApi.as_view(), name='user-group-detail-api'), # url(r'^v1/user-groups/(?P\d+)/user/(?P\d+)/$', # api.DeleteUserFromGroupApi.as_view(), name='delete-user-from-group-api'), url(r'^v1/users/(?P\d+)/groups/$', - api.UserUpdateGroupApi.as_view(), name='api-user-update-group'), + api.UserUpdateGroupApi.as_view(), name='user-update-group'), url(r'^v1/user-groups/(?P\d+)/users/$', - api.UserGroupUpdateUserApi.as_view(), name='api-user-group-update-user'), + api.UserGroupUpdateUserApi.as_view(), name='user-group-update-user'), ] urlpatterns += router.urls From aff37092bf7f1f3d691a81761dd9775710976e5e Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 16 Nov 2016 18:12:14 +0800 Subject: [PATCH 23/23] Rename urls --- apps/assets/urls/__init__.py | 1 + apps/assets/urls/api_urls.py | 23 ++++++++++++++ apps/assets/{urls.py => urls/views_urls.py} | 20 ++---------- apps/audits/urls/__init__.py | 1 + apps/audits/urls/api_urls.py | 14 +++++++++ apps/audits/{urls.py => urls/views_urls.py} | 10 +----- apps/jumpserver/urls.py | 20 ++++++------ apps/ops/urls/__init__.py | 1 + apps/ops/urls/api_urls.py | 7 +++++ apps/ops/urls/views_urls.py | 8 +++++ apps/perms/urls/__init__.py | 1 + apps/perms/{urls.py => urls/api_urls.py} | 21 ++----------- apps/perms/urls/views_urls.py | 23 ++++++++++++++ apps/terminal/urls/__init__.py | 1 + apps/terminal/urls/api_urls.py | 16 ++++++++++ apps/terminal/urls/views_urls.py | 14 +++++++++ apps/users/urls/__init__.py | 1 + apps/users/urls/api_urls.py | 34 +++++++++++++++++++++ apps/users/{urls.py => urls/views_urls.py} | 31 ++----------------- 19 files changed, 164 insertions(+), 83 deletions(-) create mode 100644 apps/assets/urls/__init__.py create mode 100644 apps/assets/urls/api_urls.py rename apps/assets/{urls.py => urls/views_urls.py} (82%) create mode 100644 apps/audits/urls/__init__.py create mode 100644 apps/audits/urls/api_urls.py rename apps/audits/{urls.py => urls/views_urls.py} (67%) create mode 100644 apps/ops/urls/__init__.py create mode 100644 apps/ops/urls/api_urls.py create mode 100644 apps/ops/urls/views_urls.py create mode 100644 apps/perms/urls/__init__.py rename apps/perms/{urls.py => urls/api_urls.py} (61%) create mode 100644 apps/perms/urls/views_urls.py create mode 100644 apps/terminal/urls/__init__.py create mode 100644 apps/terminal/urls/api_urls.py create mode 100644 apps/terminal/urls/views_urls.py create mode 100644 apps/users/urls/__init__.py create mode 100644 apps/users/urls/api_urls.py rename apps/users/{urls.py => urls/views_urls.py} (65%) 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.py b/apps/assets/urls/views_urls.py similarity index 82% rename from apps/assets/urls.py rename to apps/assets/urls/views_urls.py index 7852bd98b..7b1f4e1bc 100644 --- a/apps/assets/urls.py +++ b/apps/assets/urls/views_urls.py @@ -1,8 +1,6 @@ # coding:utf-8 -from django.conf.urls import url, include -import views -import api -from rest_framework import routers +from django.conf.urls import url +from .. import views app_name = 'assets' @@ -58,17 +56,3 @@ urlpatterns = [ ] -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/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 67% rename from apps/audits/urls.py rename to apps/audits/urls/views_urls.py index 696bd5151..67d5d35c6 100644 --- a/apps/audits/urls.py +++ b/apps/audits/urls/views_urls.py @@ -1,9 +1,5 @@ from django.conf.urls import url - -from rest_framework import routers - -import api -import views +from .. import views app_name = 'audits' @@ -15,8 +11,4 @@ urlpatterns = [ url(r'^login-log$', views.LoginLogListView.as_view(), name='login-log-list'), ] -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/jumpserver/urls.py b/apps/jumpserver/urls.py index 8bd94764c..50a4bb77d 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -22,16 +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'^users/', include('users.urls', namespace='users')), - url(r'^assets/', include('assets.urls', namespace='assets')), - url(r'^perms/', include('perms.urls', namespace='perms')), - url(r'^audits/', include('audits.urls', namespace='audits')), - url(r'^terminal/', include('terminal.urls', namespace='terminal')), - url(r'^api/users/', include('users.urls', namespace='api-users')), - url(r'^api/assets/', include('assets.urls', namespace='api-assets')), - url(r'^api/perms/', include('perms.urls', namespace='api-perms')), - url(r'^api/audits/', include('audits.urls', namespace='api-audits')), - url(r'^api/terminal/', include('terminal.urls', namespace='api-terminal')), + 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/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/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.py b/apps/perms/urls/api_urls.py similarity index 61% rename from apps/perms/urls.py rename to apps/perms/urls/api_urls.py index fc6ef4855..faac46676 100644 --- a/apps/perms/urls.py +++ b/apps/perms/urls/api_urls.py @@ -2,30 +2,14 @@ from django.conf.urls import url from rest_framework import routers -import views -import api +from .. import api app_name = 'perms' -urlpatterns = [ - url(r'^asset-permission$', views.AssetPermissionListView.as_view(), name='asset-permission-list'), - url(r'^asset-permission/create$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'), - url(r'^asset-permission/(?P[0-9]+)/update$', views.AssetPermissionUpdateView.as_view(), - name='asset-permission-update'), - url(r'^asset-permission/(?P[0-9]+)$', views.AssetPermissionDetailView.as_view(), - name='asset-permission-detail'), - url(r'^asset-permission/(?P[0-9]+)/delete$', views.AssetPermissionDeleteView.as_view(), - name='asset-permission-delete'), - url(r'^asset-permission/(?P[0-9]+)/user$', views.AssetPermissionUserView.as_view(), - name='asset-permission-user-list'), - url(r'^asset-permission/(?P[0-9]+)/asset$', views.AssetPermissionAssetView.as_view(), - name='asset-permission-asset-list'), -] - router = routers.DefaultRouter() router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission') -urlpatterns += [ +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(), @@ -50,3 +34,4 @@ urlpatterns += [ ] urlpatterns += router.urls + diff --git a/apps/perms/urls/views_urls.py b/apps/perms/urls/views_urls.py new file mode 100644 index 000000000..b73d39fab --- /dev/null +++ b/apps/perms/urls/views_urls.py @@ -0,0 +1,23 @@ +# coding:utf-8 + +from django.conf.urls import url +from .. import views + +app_name = 'perms' + +urlpatterns = [ + url(r'^asset-permission$', views.AssetPermissionListView.as_view(), name='asset-permission-list'), + url(r'^asset-permission/create$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'), + url(r'^asset-permission/(?P[0-9]+)/update$', views.AssetPermissionUpdateView.as_view(), + name='asset-permission-update'), + url(r'^asset-permission/(?P[0-9]+)$', views.AssetPermissionDetailView.as_view(), + name='asset-permission-detail'), + url(r'^asset-permission/(?P[0-9]+)/delete$', views.AssetPermissionDeleteView.as_view(), + name='asset-permission-delete'), + url(r'^asset-permission/(?P[0-9]+)/user$', views.AssetPermissionUserView.as_view(), + name='asset-permission-user-list'), + url(r'^asset-permission/(?P[0-9]+)/asset$', views.AssetPermissionAssetView.as_view(), + name='asset-permission-asset-list'), +] + + diff --git a/apps/terminal/urls/__init__.py b/apps/terminal/urls/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apps/terminal/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/terminal/urls/api_urls.py b/apps/terminal/urls/api_urls.py new file mode 100644 index 000000000..316e67e1e --- /dev/null +++ b/apps/terminal/urls/api_urls.py @@ -0,0 +1,16 @@ +#!/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 + diff --git a/apps/terminal/urls/views_urls.py b/apps/terminal/urls/views_urls.py new file mode 100644 index 000000000..ac5f1e827 --- /dev/null +++ b/apps/terminal/urls/views_urls.py @@ -0,0 +1,14 @@ +#!/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\d+)/update$', views.TerminalUpdateView.as_view(), name='terminal-update'), +] diff --git a/apps/users/urls/__init__.py b/apps/users/urls/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apps/users/urls/__init__.py @@ -0,0 +1 @@ + diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py new file mode 100644 index 000000000..bc6f3fcb0 --- /dev/null +++ b/apps/users/urls/api_urls.py @@ -0,0 +1,34 @@ +#!/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\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'), + url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='user-reset-pk'), + url(r'^v1/users/(?P\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\d+)/$', api.GroupDetailApi.as_view(), name='user-group-detail-api'), + # url(r'^v1/user-groups/(?P\d+)/user/(?P\d+)/$', + # api.DeleteUserFromGroupApi.as_view(), name='delete-user-from-group-api'), + url(r'^v1/users/(?P\d+)/groups/$', + api.UserUpdateGroupApi.as_view(), name='user-update-group'), + url(r'^v1/user-groups/(?P\d+)/users/$', + api.UserGroupUpdateUserApi.as_view(), name='user-group-update-user'), +] + +urlpatterns += router.urls diff --git a/apps/users/urls.py b/apps/users/urls/views_urls.py similarity index 65% rename from apps/users/urls.py rename to apps/users/urls/views_urls.py index 065bf24cb..b5f23c7f2 100644 --- a/apps/users/urls.py +++ b/apps/users/urls/views_urls.py @@ -1,7 +1,7 @@ +from __future__ import absolute_import + from django.conf.urls import url -from rest_framework_bulk.routes import BulkRouter -import views -import api +from .. import views app_name = 'users' @@ -41,28 +41,3 @@ urlpatterns = [ url(r'^user-group/(?P[0-9]+)/assets', views.UserGroupGrantedAssetView.as_view(), name='user-group-granted-asset'), ] - - -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\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'), - url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='user-reset-pk'), - url(r'^v1/users/(?P\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\d+)/$', api.GroupDetailApi.as_view(), name='user-group-detail-api'), - # url(r'^v1/user-groups/(?P\d+)/user/(?P\d+)/$', - # api.DeleteUserFromGroupApi.as_view(), name='delete-user-from-group-api'), - url(r'^v1/users/(?P\d+)/groups/$', - api.UserUpdateGroupApi.as_view(), name='user-update-group'), - url(r'^v1/user-groups/(?P\d+)/users/$', - api.UserGroupUpdateUserApi.as_view(), name='user-group-update-user'), -] - -urlpatterns += router.urls