From ad3214641d6156dd5e166de2a3a43bb8f8563a44 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 13 Jul 2018 00:00:35 +0800 Subject: [PATCH 01/63] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0org?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/asset.py | 10 ++-- apps/assets/models/base.py | 3 +- apps/assets/models/domain.py | 3 +- apps/assets/models/node.py | 4 +- apps/jumpserver/settings.py | 3 ++ apps/orgs/__init__.py | 0 apps/orgs/admin.py | 3 ++ apps/orgs/apps.py | 5 ++ apps/orgs/context_processor.py | 14 ++++++ apps/orgs/middleware.py | 15 ++++++ apps/orgs/mixins.py | 65 ++++++++++++++++++++++++ apps/orgs/models.py | 92 ++++++++++++++++++++++++++++++++++ apps/orgs/tests.py | 3 ++ apps/orgs/utils.py | 44 ++++++++++++++++ apps/orgs/views.py | 3 ++ 15 files changed, 259 insertions(+), 8 deletions(-) create mode 100644 apps/orgs/__init__.py create mode 100644 apps/orgs/admin.py create mode 100644 apps/orgs/apps.py create mode 100644 apps/orgs/context_processor.py create mode 100644 apps/orgs/middleware.py create mode 100644 apps/orgs/mixins.py create mode 100644 apps/orgs/models.py create mode 100644 apps/orgs/tests.py create mode 100644 apps/orgs/utils.py create mode 100644 apps/orgs/views.py diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 7a2b3fe57..c344fa82f 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -13,6 +13,7 @@ from django.core.cache import cache from ..const import ASSET_ADMIN_CONN_CACHE_KEY from .user import AdminUser, SystemUser +from orgs.mixins import OrgModelMixin, OrgQuerySet, OrgManager __all__ = ['Asset'] logger = logging.getLogger(__name__) @@ -36,7 +37,7 @@ def default_node(): return None -class AssetQuerySet(models.QuerySet): +class AssetQuerySet(OrgQuerySet): def active(self): return self.filter(is_active=True) @@ -44,12 +45,11 @@ class AssetQuerySet(models.QuerySet): return self.active() -class AssetManager(models.Manager): - def get_queryset(self): - return AssetQuerySet(self.model, using=self._db) +class AssetManager(OrgManager): + pass -class Asset(models.Model): +class Asset(OrgModelMixin): # Important PLATFORM_CHOICES = ( ('Linux', 'Linux'), diff --git a/apps/assets/models/base.py b/apps/assets/models/base.py index 908e6b647..3698f3a05 100644 --- a/apps/assets/models/base.py +++ b/apps/assets/models/base.py @@ -11,12 +11,13 @@ from django.conf import settings from common.utils import get_signer, ssh_key_string_to_obj, ssh_key_gen from common.validators import alphanumeric +from orgs.mixins import OrgModelMixin from .utils import private_key_validator signer = get_signer() -class AssetUser(models.Model): +class AssetUser(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric]) diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py index 6f29a0381..67283bd80 100644 --- a/apps/assets/models/domain.py +++ b/apps/assets/models/domain.py @@ -7,12 +7,13 @@ import random from django.db import models from django.utils.translation import ugettext_lazy as _ +from orgs.mixins import OrgModelMixin from .base import AssetUser __all__ = ['Domain', 'Gateway'] -class Domain(models.Model): +class Domain(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) comment = models.TextField(blank=True, verbose_name=_('Comment')) diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 4f4f9ad8b..74156daa6 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -5,12 +5,14 @@ import uuid from django.db import models, transaction from django.db.models import Q from django.utils.translation import ugettext_lazy as _ + +from orgs.mixins import OrgModelMixin from common.utils import with_cache __all__ = ['Node'] -class Node(models.Model): +class Node(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1' value = models.CharField(max_length=128, verbose_name=_("Value")) diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index c0a536276..77a37839d 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -56,6 +56,7 @@ ALLOWED_HOSTS = CONFIG.ALLOWED_HOSTS or [] # Application definition INSTALLED_APPS = [ + 'orgs.apps.OrgsConfig', 'users.apps.UsersConfig', 'assets.apps.AssetsConfig', 'perms.apps.PermsConfig', @@ -87,6 +88,7 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'jumpserver.middleware.TimezoneMiddleware', 'jumpserver.middleware.DemoMiddleware', + 'orgs.middleware.OrgMiddleware', ] ROOT_URLCONF = 'jumpserver.urls' @@ -107,6 +109,7 @@ TEMPLATES = [ 'django.template.context_processors.static', 'django.template.context_processors.request', 'django.template.context_processors.media', + 'orgs.context_processor.org_processor', ], }, }, diff --git a/apps/orgs/__init__.py b/apps/orgs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/orgs/admin.py b/apps/orgs/admin.py new file mode 100644 index 000000000..8c38f3f3d --- /dev/null +++ b/apps/orgs/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/orgs/apps.py b/apps/orgs/apps.py new file mode 100644 index 000000000..fdd6dc1b6 --- /dev/null +++ b/apps/orgs/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class OrgsConfig(AppConfig): + name = 'orgs' diff --git a/apps/orgs/context_processor.py b/apps/orgs/context_processor.py new file mode 100644 index 000000000..aa89d3a54 --- /dev/null +++ b/apps/orgs/context_processor.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# + +from .utils import get_current_org +from .models import Organization + + +def org_processor(request): + context = { + 'ADMIN_ORGS': Organization.get_user_admin_orgs(request.user), + 'CURRENT_ORG': get_current_org(), + } + return context + diff --git a/apps/orgs/middleware.py b/apps/orgs/middleware.py new file mode 100644 index 000000000..527797857 --- /dev/null +++ b/apps/orgs/middleware.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# + +from .utils import get_org_from_request + + +class OrgMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + org = get_org_from_request(request) + request.current_org = org + response = self.get_response(request) + return response diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py new file mode 100644 index 000000000..5bcf75f2e --- /dev/null +++ b/apps/orgs/mixins.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# +from django.db import models + +from common.utils import get_logger +from .utils import get_current_org, get_model_by_db_table + +logger = get_logger(__file__) + + +class OrgQuerySet(models.QuerySet): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + +class OrgManager(OrgQuerySet.as_manager().__class__): + def get_queryset(self): + current_org = get_current_org() + kwargs = {} + + if not current_org: + kwargs['id'] = None + elif current_org.is_real: + kwargs['org'] = current_org + elif current_org.is_default(): + kwargs['org'] = None + print("GET QUWRYSET ") + print(kwargs) + return super().get_queryset().filter(**kwargs) + + +class OrgModelMixin(models.Model): + org = models.ForeignKey('orgs.Organization', on_delete=models.PROTECT, null=True) + + objects = OrgManager() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def _do_update(self, base_qs, using, pk_val, values, update_fields, forced_update): + current_org = get_current_org() + + if current_org and current_org.is_real(): + kwargs = {'org': current_org} + base_qs = base_qs.filter(**kwargs) + else: + logger.warn( + 'Attempting to update %s instance "%s" without a current tenant ' + 'set. This may cause issues in a partitioned environment. ' + 'Recommend calling set_current_org() before performing this ' + 'operation.', self._meta.model.__name__, self + ) + return super()._do_update(base_qs, using, pk_val, values, update_fields, forced_update) + + def save(self, force_insert=False, force_update=False, using=None, + update_fields=None): + current_org = get_current_org() + if current_org and not current_org.is_real(): + self.org = current_org + return super().save(force_insert=force_insert, force_update=force_update, + using=using, update_fields=update_fields) + + class Meta: + abstract = True diff --git a/apps/orgs/models.py b/apps/orgs/models.py new file mode 100644 index 000000000..3f43c6bc6 --- /dev/null +++ b/apps/orgs/models.py @@ -0,0 +1,92 @@ +import uuid + +from django.db import models +from django.core.cache import cache +from django.utils.translation import ugettext_lazy as _ + + +class Organization(models.Model): + id = models.UUIDField(default=uuid.uuid4, primary_key=True) + name = models.CharField(max_length=128, verbose_name=_("Name")) + users = models.ManyToManyField('users.User', related_name='orgs', blank=True) + admins = models.ManyToManyField('users.User', related_name='admin_orgs', blank=True) + created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by')) + date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created')) + comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment')) + + CACHE_PREFIX = 'JMS_ORG_{}' + ROOT_ID = 'ROOT' + DEFAULT_ID = 'DEFAULT' + + def __str__(self): + return self.name + + def set_to_cache(self): + key = self.CACHE_PREFIX.format(self.id) + cache.set(key, self, 3600) + + def expire_cache(self): + key = self.CACHE_PREFIX.format(self.id) + cache.set(key, self, 0) + + @classmethod + def get_instance_from_cache(cls, oid): + key = cls.CACHE_PREFIX.format(oid) + return cache.get(key, None) + + @classmethod + def get_instance(cls, oid, default=True): + cached = cls.get_instance_from_cache(oid) + if cached: + return cached + + if oid == cls.DEFAULT_ID: + return cls.default() + elif oid == cls.ROOT_ID: + return cls.root() + + try: + org = cls.objects.get(id=oid) + org.set_to_cache() + except cls.DoesNotExist: + org = cls.default() if default else None + return org + + def get_org_users(self): + from users.models import User + if self.is_default(): + return User.objects.all() + else: + return self.users.all() + + def get_org_admins(self): + pass + + def is_real(self): + return len(str(self.id)) == 32 + + @classmethod + def get_user_admin_orgs(cls, user): + admin_orgs = [] + if user.is_anonymous: + return admin_orgs + elif user.is_superuser: + admin_orgs = list(cls.objects.all()) + admin_orgs.append(cls.default()) + elif user.is_org_admin: + admin_orgs = user.admin_orgs.all() + return admin_orgs + + @classmethod + def default(cls): + return cls(id=cls.DEFAULT_ID, name="Default") + + @classmethod + def root(cls): + return cls(id=cls.ROOT_ID, name='Root') + + def is_default(self): + if self.id is self.DEFAULT_ID: + return True + else: + return False diff --git a/apps/orgs/tests.py b/apps/orgs/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/apps/orgs/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/orgs/utils.py b/apps/orgs/utils.py new file mode 100644 index 000000000..ee82fc01e --- /dev/null +++ b/apps/orgs/utils.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# +import re +from django.apps import apps + +from .models import Organization + +try: + from threading import local +except ImportError: + from django.utils._threading_local import local + +_thread_locals = local() + + +def get_org_from_request(request): + oid = request.session.get("oid") + org = Organization.get_instance(oid) + return org + + +def get_current_request(): + return getattr(_thread_locals, 'request', None) + + +def get_current_org(): + return getattr(_thread_locals, 'current_org', None) + + +def get_current_user(): + return getattr(_thread_locals, 'user', None) + + +def set_current_org(org): + setattr(_thread_locals, 'current_org', org) + + +def get_model_by_db_table(db_table): + for model in apps.get_models(): + if model._meta.db_table == db_table: + return model + else: + # here you can do fallback logic if no model with db_table found + raise ValueError('No model found with db_table {}!'.format(db_table)) diff --git a/apps/orgs/views.py b/apps/orgs/views.py new file mode 100644 index 000000000..91ea44a21 --- /dev/null +++ b/apps/orgs/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From d6ec92d82db0da878d1bfbde5d48b6a9beb2dc56 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 13 Jul 2018 00:31:50 +0800 Subject: [PATCH 02/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/urls.py | 66 ++++++++++++++++++++++++++++++------ apps/orgs/urls/__init__.py | 2 ++ apps/orgs/urls/api_urls.py | 11 ++++++ apps/orgs/urls/views_urls.py | 13 +++++++ apps/orgs/views.py | 17 ++++++++-- apps/templates/_nav.html | 16 +++++++++ 6 files changed, 112 insertions(+), 13 deletions(-) create mode 100644 apps/orgs/urls/__init__.py create mode 100644 apps/orgs/urls/api_urls.py create mode 100644 apps/orgs/urls/views_urls.py diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index 8300ebe06..921481c27 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -1,9 +1,14 @@ # ~*~ coding: utf-8 ~*~ from __future__ import unicode_literals +import re from django.conf.urls import url, include from django.conf import settings from django.conf.urls.static import static +from rest_framework.response import Response +from django.views.decorators.csrf import csrf_exempt +from django.http import HttpResponse +from django.utils.encoding import iri_to_uri from rest_framework.schemas import get_schema_view from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer @@ -11,30 +16,69 @@ from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer from .views import IndexView, LunaView schema_view = get_schema_view(title='Users API', renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer]) -urlpatterns = [ - url(r'^$', IndexView.as_view(), name='index'), - url(r'^luna/', LunaView.as_view(), name='luna-error'), +api_url_pattern = re.compile(r'^/api/(?P\w+)/(?P\w+)/(?P.*)$') + + +class HttpResponseTemporaryRedirect(HttpResponse): + status_code = 307 + + def __init__(self, redirect_to): + HttpResponse.__init__(self) + self['Location'] = iri_to_uri(redirect_to) + + +@csrf_exempt +def redirect_old_format_api(request, *args, **kwargs): + path, query = request.path, request.GET.urlencode() + matched = api_url_pattern.match(path) + if matched: + app, version, extra = matched.groups() + path = '/api/{version}/{app}/{extra}?{query}'.format(**{ + "app": app, "version": version, "extra": extra, + "query": query + }) + return HttpResponseTemporaryRedirect(path) + else: + return Response({"msg": "Redirect url failed: {}".format(path)}, status=404) + + +v1_api_patterns = [ + url(r'^users/', include('users.urls.api_urls', namespace='api-users')), + url(r'^assets/', include('assets.urls.api_urls', namespace='api-assets')), + url(r'^perms/', include('perms.urls.api_urls', namespace='api-perms')), + url(r'^terminal/', include('terminal.urls.api_urls', namespace='api-terminal')), + url(r'^ops/', include('ops.urls.api_urls', namespace='api-ops')), + url(r'^audits/', include('audits.urls.api_urls', namespace='api-audits')), + url(r'^orgs/', include('orgs.urls.api_urls', namespace='api-orgs')), + url(r'^common/', include('common.urls.api_urls', namespace='api-common')), +] + +app_view_patterns = [ 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'^terminal/', include('terminal.urls.views_urls', namespace='terminal')), url(r'^ops/', include('ops.urls.view_urls', namespace='ops')), url(r'^audits/', include('audits.urls.view_urls', namespace='audits')), + url(r'^orgs/', include('orgs.urls.views_urls', namespace='orgs')), +] + + +urlpatterns = [ + url(r'^$', IndexView.as_view(), name='index'), + url(r'^luna/', LunaView.as_view(), name='luna-error'), url(r'^settings/', include('common.urls.view_urls', namespace='settings')), url(r'^common/', include('common.urls.view_urls', namespace='common')), + url(r'^api/v1/', include(v1_api_patterns)), + url(r'^api/(?P.*)/v1/.*', redirect_old_format_api), # Api url view map - 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/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')), - url(r'^api/ops/', include('ops.urls.api_urls', namespace='api-ops')), - url(r'^api/audits/', include('audits.urls.api_urls', namespace='api-audits')), - url(r'^api/common/', include('common.urls.api_urls', namespace='api-common')), - # External apps url url(r'^captcha/', include('captcha.urls')), ] +urlpatterns += app_view_patterns + +# urlpatterns = wrapper_patterns_with_org(urlpatterns) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/apps/orgs/urls/__init__.py b/apps/orgs/urls/__init__.py new file mode 100644 index 000000000..ec51c5a2b --- /dev/null +++ b/apps/orgs/urls/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +# diff --git a/apps/orgs/urls/api_urls.py b/apps/orgs/urls/api_urls.py new file mode 100644 index 000000000..8c672a159 --- /dev/null +++ b/apps/orgs/urls/api_urls.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# + +from django.conf.urls import url +from .. import views + +app_name = 'orgs' + +urlpatterns = [ + # url(r'^(?P[0-9a-zA-Z\-]{36})/$', views.OrgDetailView.as_view(), name='asset-index') +] \ No newline at end of file diff --git a/apps/orgs/urls/views_urls.py b/apps/orgs/urls/views_urls.py new file mode 100644 index 000000000..a0f21cf32 --- /dev/null +++ b/apps/orgs/urls/views_urls.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# + +from django.conf.urls import url +from .. import views + +app_name = 'orgs' + + +urlpatterns = [ + url(r'^(?P.*)/switch/$', views.SwitchOrgView.as_view(), name='org-switch') +] + diff --git a/apps/orgs/views.py b/apps/orgs/views.py index 91ea44a21..ec2136716 100644 --- a/apps/orgs/views.py +++ b/apps/orgs/views.py @@ -1,3 +1,16 @@ -from django.shortcuts import render +from django.shortcuts import redirect -# Create your views here. +from django.views.generic import DetailView + +from .models import Organization + + +class SwitchOrgView(DetailView): + model = Organization + object = None + + def get(self, request, *args, **kwargs): + pk = kwargs.get('pk') + self.object = Organization.get_instance(pk) + request.session['oid'] = self.object.id.__str__() + return redirect('index') diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index e0d58080b..60f38b92c 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -1,4 +1,20 @@ {% load i18n %} +{% if ADMIN_ORGS %} +
  • + + +
  • +
    +{% endif %}
  • {% trans 'Dashboard' %} Date: Fri, 13 Jul 2018 15:05:46 +0800 Subject: [PATCH 03/63] =?UTF-8?q?[Update]=20=E5=AE=8C=E6=88=90=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/asset.py | 4 ++-- apps/common/mixins.py | 2 +- apps/jumpserver/views.py | 3 ++- apps/orgs/middleware.py | 3 ++- apps/orgs/mixins.py | 37 +++++++++++++++++++++++++++--------- apps/orgs/models.py | 2 +- apps/orgs/urls/views_urls.py | 4 ++-- apps/orgs/views.py | 18 ++++++++++++++++-- apps/templates/_nav.html | 19 ++++++++++++------ apps/users/api.py | 10 ++++++++++ apps/users/models/user.py | 22 ++++++++++++++++++--- 11 files changed, 96 insertions(+), 28 deletions(-) diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index c344fa82f..6de5c2059 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -13,7 +13,7 @@ from django.core.cache import cache from ..const import ASSET_ADMIN_CONN_CACHE_KEY from .user import AdminUser, SystemUser -from orgs.mixins import OrgModelMixin, OrgQuerySet, OrgManager +from orgs.mixins import OrgModelMixin,OrgManager __all__ = ['Asset'] logger = logging.getLogger(__name__) @@ -37,7 +37,7 @@ def default_node(): return None -class AssetQuerySet(OrgQuerySet): +class AssetQuerySet(models.QuerySet): def active(self): return self.filter(is_active=True) diff --git a/apps/common/mixins.py b/apps/common/mixins.py index 243ee93c6..29e0ad876 100644 --- a/apps/common/mixins.py +++ b/apps/common/mixins.py @@ -120,7 +120,7 @@ class AdminUserRequiredMixin(UserPassesTestMixin): def test_func(self): if not self.request.user.is_authenticated: return False - elif not self.request.user.is_superuser: + elif not self.request.user: self.raise_exception = True return False return True diff --git a/apps/jumpserver/views.py b/apps/jumpserver/views.py index c6a2dc4f3..1ad2bb20f 100644 --- a/apps/jumpserver/views.py +++ b/apps/jumpserver/views.py @@ -10,9 +10,10 @@ from django.shortcuts import redirect from users.models import User from assets.models import Asset from terminal.models import Session +from orgs.mixins import OrgViewGenericMixin -class IndexView(LoginRequiredMixin, TemplateView): +class IndexView(LoginRequiredMixin, OrgViewGenericMixin, TemplateView): template_name = 'index.html' session_week = None diff --git a/apps/orgs/middleware.py b/apps/orgs/middleware.py index 527797857..04a2d6f7a 100644 --- a/apps/orgs/middleware.py +++ b/apps/orgs/middleware.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # -from .utils import get_org_from_request +from .utils import get_org_from_request, set_current_org class OrgMiddleware: @@ -11,5 +11,6 @@ class OrgMiddleware: def __call__(self, request): org = get_org_from_request(request) request.current_org = org + set_current_org(org) response = self.get_response(request) return response diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index 5bcf75f2e..f5d59e686 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # from django.db import models +from django.shortcuts import redirect +from django.contrib.auth import get_user_model from common.utils import get_logger from .utils import get_current_org, get_model_by_db_table @@ -8,24 +10,27 @@ from .utils import get_current_org, get_model_by_db_table logger = get_logger(__file__) -class OrgQuerySet(models.QuerySet): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) +__all__ = ['OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin'] -class OrgManager(OrgQuerySet.as_manager().__class__): +class OrgManager(models.Manager): def get_queryset(self): current_org = get_current_org() + user_model = get_user_model() kwargs = {} + print("Get queryset ") + print(self.model) + print(current_org) + if not current_org: kwargs['id'] = None - elif current_org.is_real: + elif issubclass(self.model, user_model): + kwargs['orgs'] = current_org + elif current_org.is_real(): kwargs['org'] = current_org elif current_org.is_default(): kwargs['org'] = None - print("GET QUWRYSET ") print(kwargs) return super().get_queryset().filter(**kwargs) @@ -55,11 +60,25 @@ class OrgModelMixin(models.Model): def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + user_model = get_user_model() current_org = get_current_org() if current_org and not current_org.is_real(): self.org = current_org - return super().save(force_insert=force_insert, force_update=force_update, - using=using, update_fields=update_fields) + instance = super().save( + force_insert=force_insert, force_update=force_update, + using=using, update_fields=update_fields + ) + if isinstance(instance, user_model): + instance.orgs.add(current_org) + return instance class Meta: abstract = True + + +class OrgViewGenericMixin: + def dispatch(self, request, *args, **kwargs): + current_org = get_current_org() + if not current_org: + return redirect('orgs:switch-a-org') + return super().dispatch(request, *args, **kwargs) diff --git a/apps/orgs/models.py b/apps/orgs/models.py index 3f43c6bc6..afdf2ef10 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -63,7 +63,7 @@ class Organization(models.Model): pass def is_real(self): - return len(str(self.id)) == 32 + return len(str(self.id)) == 36 @classmethod def get_user_admin_orgs(cls, user): diff --git a/apps/orgs/urls/views_urls.py b/apps/orgs/urls/views_urls.py index a0f21cf32..e3408e741 100644 --- a/apps/orgs/urls/views_urls.py +++ b/apps/orgs/urls/views_urls.py @@ -8,6 +8,6 @@ app_name = 'orgs' urlpatterns = [ - url(r'^(?P.*)/switch/$', views.SwitchOrgView.as_view(), name='org-switch') + url(r'^(?P.*)/switch/$', views.SwitchOrgView.as_view(), name='org-switch'), + url(r'^switch-a-org/$', views.SwitchToAOrgView.as_view(), name='switch-a-org') ] - diff --git a/apps/orgs/views.py b/apps/orgs/views.py index ec2136716..374f34812 100644 --- a/apps/orgs/views.py +++ b/apps/orgs/views.py @@ -1,6 +1,7 @@ -from django.shortcuts import redirect +from django.shortcuts import redirect, reverse +from django.http import HttpResponseForbidden -from django.views.generic import DetailView +from django.views.generic import DetailView, View from .models import Organization @@ -14,3 +15,16 @@ class SwitchOrgView(DetailView): self.object = Organization.get_instance(pk) request.session['oid'] = self.object.id.__str__() return redirect('index') + + +class SwitchToAOrgView(View): + def get(self, request, *args, **kwargs): + admin_orgs = Organization.get_user_admin_orgs(request.user) + if not admin_orgs: + return HttpResponseForbidden() + default_org = Organization.default() + if default_org in admin_orgs: + redirect_org = default_org + else: + redirect_org = admin_orgs[0] + return redirect(reverse('orgs:org-switch', kwargs={'pk': redirect_org.id})) diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 60f38b92c..ed2e67921 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -1,5 +1,5 @@ {% load i18n %} -{% if ADMIN_ORGS %} +{% if ADMIN_ORGS and ADMIN_ORGS|length > 1 %}
  • @@ -17,8 +17,8 @@ {% endif %}
  • - {% trans 'Dashboard' %} + {% trans 'Dashboard' %} +
  • @@ -96,4 +96,11 @@ {% trans 'Settings' %} -
  • \ No newline at end of file + + + \ No newline at end of file diff --git a/apps/users/api.py b/apps/users/api.py index c23112384..10a12ee66 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -20,6 +20,7 @@ from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly, \ IsSuperUserOrAppUser from .utils import check_user_valid, generate_token, get_login_ip, \ check_otp_code, set_user_login_failed_count_to_cache, is_block_login +from orgs.utils import get_current_org from common.mixins import IDInFilterMixin from common.utils import get_logger @@ -33,6 +34,15 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet): permission_classes = (IsSuperUser,) filter_fields = ('username', 'email', 'name', 'id') + def get_queryset(self): + queryset = super().get_queryset() + current_org = get_current_org() + if current_org.is_real(): + queryset = queryset.filter(orgs=current_org) + elif current_org.is_default(): + queryset = queryset.filter(orgs=None) + return queryset + def get_permissions(self): if self.action == "retrieve": self.permission_classes = (IsSuperUserOrAppUser,) diff --git a/apps/users/models/user.py b/apps/users/models/user.py index d69151f55..30054f137 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -6,7 +6,7 @@ from collections import OrderedDict from django.conf import settings from django.contrib.auth.hashers import make_password -from django.contrib.auth.models import AbstractUser +from django.contrib.auth.models import AbstractUser, UserManager from django.core import signing from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -15,6 +15,7 @@ from django.shortcuts import reverse from common.utils import get_signer, date_expired_default from common.models import Setting +from orgs.utils import get_current_org __all__ = ['User'] @@ -186,6 +187,18 @@ class User(AbstractUser): else: self.role = 'User' + @property + def admin_orgs(self): + from orgs.models import Organization + return Organization.get_user_admin_orgs(self) + + @property + def is_org_admin(self): + if self.is_superuser or self.admin_orgs: + return True + else: + return False + @property def is_app(self): return self.role == 'App' @@ -207,8 +220,11 @@ class User(AbstractUser): if self.username == 'admin': self.role = 'Admin' self.is_active = True - - super().save(*args, **kwargs) + instance = super().save(*args, **kwargs) + current_org = get_current_org() + if current_org and current_org.is_real(): + instance.orgs.add(current_org) + return instance @property def private_token(self): From 28e47f33c11989d99864585639b4dc13fdffed67 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sat, 14 Jul 2018 00:47:21 +0800 Subject: [PATCH 04/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/admin_user.py | 8 ++-- apps/assets/api/asset.py | 12 +++--- apps/assets/api/domain.py | 4 +- apps/assets/api/node.py | 12 +++--- apps/assets/api/system_user.py | 8 ++-- apps/assets/forms/asset.py | 4 +- apps/assets/forms/domain.py | 2 +- apps/assets/forms/label.py | 2 +- apps/assets/serializers/admin_user.py | 2 +- apps/assets/serializers/node.py | 2 +- apps/assets/urls/api_urls.py | 54 +++++++++++++-------------- apps/assets/views/admin_user.py | 2 +- apps/audits/api.py | 2 +- apps/audits/urls/api_urls.py | 4 +- apps/common/urls/api_urls.py | 6 +-- apps/ops/api.py | 10 ++--- apps/ops/urls/api_urls.py | 10 ++--- apps/orgs/mixins.py | 41 ++++---------------- apps/perms/api.py | 10 ++--- apps/perms/urls/api_urls.py | 36 +++++++++--------- apps/perms/views.py | 4 +- apps/terminal/api.py | 6 +-- apps/terminal/urls/api_urls.py | 22 +++++------ apps/users/api.py | 18 +++++---- apps/users/forms.py | 17 +++++---- apps/users/models/group.py | 4 +- apps/users/models/user.py | 5 +-- apps/users/serializers.py | 6 +-- apps/users/urls/api_urls.py | 26 ++++++------- apps/users/views/group.py | 13 ++++--- 30 files changed, 167 insertions(+), 185 deletions(-) diff --git a/apps/assets/api/admin_user.py b/apps/assets/api/admin_user.py index 968cd6594..74c44b48b 100644 --- a/apps/assets/api/admin_user.py +++ b/apps/assets/api/admin_user.py @@ -37,19 +37,19 @@ class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet): """ Admin user api set, for add,delete,update,list,retrieve resource """ - queryset = AdminUser.objects.all() + queryset = AdminUser.objects serializer_class = serializers.AdminUserSerializer permission_classes = (IsSuperUser,) class AdminUserAuthApi(generics.UpdateAPIView): - queryset = AdminUser.objects.all() + queryset = AdminUser.objects serializer_class = serializers.AdminUserAuthSerializer permission_classes = (IsSuperUser,) class ReplaceNodesAdminUserApi(generics.UpdateAPIView): - queryset = AdminUser.objects.all() + queryset = AdminUser.objects serializer_class = serializers.ReplaceNodeAdminUserSerializer permission_classes = (IsSuperUser,) @@ -74,7 +74,7 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView): """ Test asset admin user connectivity """ - queryset = AdminUser.objects.all() + queryset = AdminUser.objects permission_classes = (IsSuperUser,) def retrieve(self, request, *args, **kwargs): diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py index 8c1f3d726..ad42aeeba 100644 --- a/apps/assets/api/asset.py +++ b/apps/assets/api/asset.py @@ -36,7 +36,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): filter_fields = ("hostname", "ip") search_fields = filter_fields ordering_fields = ("hostname", "ip", "port", "cpu_cores") - queryset = Asset.objects.all() + queryset = Asset.objects serializer_class = serializers.AssetSerializer pagination_class = LimitOffsetPagination permission_classes = (IsSuperUserOrAppUser,) @@ -65,7 +65,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): if node_id and not show_current_asset: node = get_object_or_404(Node, id=node_id) if node.is_root(): - queryset = Asset.objects.all() + queryset = Asset.objects else: queryset = queryset.filter( nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key), @@ -77,7 +77,7 @@ class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): """ Asset bulk update api """ - queryset = Asset.objects.all() + queryset = Asset.objects serializer_class = serializers.AssetSerializer permission_classes = (IsSuperUser,) @@ -86,7 +86,7 @@ class AssetRefreshHardwareApi(generics.RetrieveAPIView): """ Refresh asset hardware info """ - queryset = Asset.objects.all() + queryset = Asset.objects serializer_class = serializers.AssetSerializer permission_classes = (IsSuperUser,) @@ -101,7 +101,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView): """ Test asset admin user connectivity """ - queryset = Asset.objects.all() + queryset = Asset.objects permission_classes = (IsSuperUser,) def retrieve(self, request, *args, **kwargs): @@ -112,7 +112,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView): class AssetGatewayApi(generics.RetrieveAPIView): - queryset = Asset.objects.all() + queryset = Asset.objects permission_classes = (IsSuperUserOrAppUser,) def retrieve(self, request, *args, **kwargs): diff --git a/apps/assets/api/domain.py b/apps/assets/api/domain.py index 5114b5561..88a5ba2e0 100644 --- a/apps/assets/api/domain.py +++ b/apps/assets/api/domain.py @@ -18,7 +18,7 @@ __all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"] class DomainViewSet(BulkModelViewSet): - queryset = Domain.objects.all() + queryset = Domain.objects permission_classes = (IsSuperUser,) serializer_class = serializers.DomainSerializer @@ -36,7 +36,7 @@ class DomainViewSet(BulkModelViewSet): class GatewayViewSet(BulkModelViewSet): filter_fields = ("domain",) search_fields = filter_fields - queryset = Gateway.objects.all() + queryset = Gateway.objects permission_classes = (IsSuperUser,) serializer_class = serializers.GatewaySerializer diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index e5ace021e..8cd9c557e 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -40,7 +40,7 @@ __all__ = [ class NodeViewSet(BulkModelViewSet): - queryset = Node.objects.all() + queryset = Node.objects permission_classes = (IsSuperUser,) serializer_class = serializers.NodeSerializer @@ -79,7 +79,7 @@ class NodeViewSet(BulkModelViewSet): class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView): - queryset = Node.objects.all() + queryset = Node.objects permission_classes = (IsSuperUser,) serializer_class = serializers.NodeSerializer instance = None @@ -166,7 +166,7 @@ class NodeAssetsApi(generics.ListAPIView): class NodeAddChildrenApi(generics.UpdateAPIView): - queryset = Node.objects.all() + queryset = Node.objects permission_classes = (IsSuperUser,) serializer_class = serializers.NodeAddChildrenSerializer instance = None @@ -184,7 +184,7 @@ class NodeAddChildrenApi(generics.UpdateAPIView): class NodeAddAssetsApi(generics.UpdateAPIView): serializer_class = serializers.NodeAssetsSerializer - queryset = Node.objects.all() + queryset = Node.objects permission_classes = (IsSuperUser,) instance = None @@ -196,7 +196,7 @@ class NodeAddAssetsApi(generics.UpdateAPIView): class NodeRemoveAssetsApi(generics.UpdateAPIView): serializer_class = serializers.NodeAssetsSerializer - queryset = Node.objects.all() + queryset = Node.objects permission_classes = (IsSuperUser,) instance = None @@ -212,7 +212,7 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView): class NodeReplaceAssetsApi(generics.UpdateAPIView): serializer_class = serializers.NodeAssetsSerializer - queryset = Node.objects.all() + queryset = Node.objects permission_classes = (IsSuperUser,) instance = None diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index 66d62232d..1fb77f01f 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -35,7 +35,7 @@ class SystemUserViewSet(BulkModelViewSet): """ System user api set, for add,delete,update,list,retrieve resource """ - queryset = SystemUser.objects.all() + queryset = SystemUser.objects serializer_class = serializers.SystemUserSerializer permission_classes = (IsSuperUserOrAppUser,) @@ -44,7 +44,7 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView): """ Get system user auth info """ - queryset = SystemUser.objects.all() + queryset = SystemUser.objects permission_classes = (IsSuperUserOrAppUser,) serializer_class = serializers.SystemUserAuthSerializer @@ -58,7 +58,7 @@ class SystemUserPushApi(generics.RetrieveAPIView): """ Push system user to cluster assets api """ - queryset = SystemUser.objects.all() + queryset = SystemUser.objects permission_classes = (IsSuperUser,) def retrieve(self, request, *args, **kwargs): @@ -74,7 +74,7 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView): """ Push system user to cluster assets api """ - queryset = SystemUser.objects.all() + queryset = SystemUser.objects permission_classes = (IsSuperUser,) def retrieve(self, request, *args, **kwargs): diff --git a/apps/assets/forms/asset.py b/apps/assets/forms/asset.py index 5000c087d..1f32219a7 100644 --- a/apps/assets/forms/asset.py +++ b/apps/assets/forms/asset.py @@ -93,7 +93,7 @@ class AssetUpdateForm(forms.ModelForm): class AssetBulkUpdateForm(forms.ModelForm): assets = forms.ModelMultipleChoiceField( required=True, help_text='* required', - label=_('Select assets'), queryset=Asset.objects.all(), + label=_('Select assets'), queryset = Asset.objects, widget=forms.SelectMultiple( attrs={ 'class': 'select2', @@ -105,7 +105,7 @@ class AssetBulkUpdateForm(forms.ModelForm): label=_('Port'), required=False, min_value=1, max_value=65535, ) admin_user = forms.ModelChoiceField( - required=False, queryset=AdminUser.objects.all(), + required=False, queryset = AdminUser.objects, label=_("Admin user"), widget=forms.Select( attrs={ diff --git a/apps/assets/forms/domain.py b/apps/assets/forms/domain.py index ec3af8f2e..2ad06fd78 100644 --- a/apps/assets/forms/domain.py +++ b/apps/assets/forms/domain.py @@ -11,7 +11,7 @@ __all__ = ['DomainForm', 'GatewayForm'] class DomainForm(forms.ModelForm): assets = forms.ModelMultipleChoiceField( - queryset=Asset.objects.all(), label=_('Asset'), required=False, + queryset = Asset.objects, label=_('Asset'), required=False, widget=forms.SelectMultiple( attrs={'class': 'select2', 'data-placeholder': _('Select assets')} ) diff --git a/apps/assets/forms/label.py b/apps/assets/forms/label.py index ebdc9384e..8d7ef4c44 100644 --- a/apps/assets/forms/label.py +++ b/apps/assets/forms/label.py @@ -10,7 +10,7 @@ __all__ = ['LabelForm'] class LabelForm(forms.ModelForm): assets = forms.ModelMultipleChoiceField( - queryset=Asset.objects.all(), label=_('Asset'), required=False, + queryset = Asset.objects, label=_('Asset'), required=False, widget=forms.SelectMultiple( attrs={'class': 'select2', 'data-placeholder': _('Select assets')} ) diff --git a/apps/assets/serializers/admin_user.py b/apps/assets/serializers/admin_user.py index dbd0d1b39..f7156f049 100644 --- a/apps/assets/serializers/admin_user.py +++ b/apps/assets/serializers/admin_user.py @@ -58,7 +58,7 @@ class ReplaceNodeAdminUserSerializer(serializers.ModelSerializer): 管理用户更新关联到的集群 """ nodes = serializers.PrimaryKeyRelatedField( - many=True, queryset=Node.objects.all() + many=True, queryset = Node.objects ) class Meta: diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py index 56e01f742..a5788e229 100644 --- a/apps/assets/serializers/node.py +++ b/apps/assets/serializers/node.py @@ -78,7 +78,7 @@ class NodeSerializer(serializers.ModelSerializer): class NodeAssetsSerializer(serializers.ModelSerializer): - assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) + assets = serializers.PrimaryKeyRelatedField(many=True, queryset = Asset.objects) class Meta: model = Node diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index cf55d08ba..29d347c04 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -7,53 +7,53 @@ app_name = 'assets' router = BulkRouter() -router.register(r'v1/assets', api.AssetViewSet, 'asset') -router.register(r'v1/admin-user', api.AdminUserViewSet, 'admin-user') -router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user') -router.register(r'v1/labels', api.LabelViewSet, 'label') -router.register(r'v1/nodes', api.NodeViewSet, 'node') -router.register(r'v1/domain', api.DomainViewSet, 'domain') -router.register(r'v1/gateway', api.GatewayViewSet, 'gateway') +router.register(r'assets', api.AssetViewSet, 'asset') +router.register(r'admin-user', api.AdminUserViewSet, 'admin-user') +router.register(r'system-user', api.SystemUserViewSet, 'system-user') +router.register(r'labels', api.LabelViewSet, 'label') +router.register(r'nodes', api.NodeViewSet, 'node') +router.register(r'domain', api.DomainViewSet, 'domain') +router.register(r'gateway', api.GatewayViewSet, 'gateway') urlpatterns = [ - url(r'^v1/assets-bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'), - url(r'^v1/system-user/(?P[0-9a-zA-Z\-]{36})/auth-info/', api.SystemUserAuthInfoApi.as_view(), + url(r'^assets-bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'), + url(r'^system-user/(?P[0-9a-zA-Z\-]{36})/auth-info/', api.SystemUserAuthInfoApi.as_view(), name='system-user-auth-info'), - url(r'^v1/assets/(?P[0-9a-zA-Z\-]{36})/refresh/$', + url(r'^assets/(?P[0-9a-zA-Z\-]{36})/refresh/$', api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'), - url(r'^v1/assets/(?P[0-9a-zA-Z\-]{36})/alive/$', + url(r'^assets/(?P[0-9a-zA-Z\-]{36})/alive/$', api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'), - url(r'^v1/assets/(?P[0-9a-zA-Z\-]{36})/gateway/$', + url(r'^assets/(?P[0-9a-zA-Z\-]{36})/gateway/$', api.AssetGatewayApi.as_view(), name='asset-gateway'), - url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/nodes/$', + url(r'^admin-user/(?P[0-9a-zA-Z\-]{36})/nodes/$', api.ReplaceNodesAdminUserApi.as_view(), name='replace-nodes-admin-user'), - url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/auth/$', + url(r'^admin-user/(?P[0-9a-zA-Z\-]{36})/auth/$', api.AdminUserAuthApi.as_view(), name='admin-user-auth'), - url(r'^v1/admin-user/(?P[0-9a-zA-Z\-]{36})/connective/$', + url(r'^admin-user/(?P[0-9a-zA-Z\-]{36})/connective/$', api.AdminUserTestConnectiveApi.as_view(), name='admin-user-connective'), - url(r'^v1/system-user/(?P[0-9a-zA-Z\-]{36})/push/$', + url(r'^system-user/(?P[0-9a-zA-Z\-]{36})/push/$', api.SystemUserPushApi.as_view(), name='system-user-push'), - url(r'^v1/system-user/(?P[0-9a-zA-Z\-]{36})/connective/$', + url(r'^system-user/(?P[0-9a-zA-Z\-]{36})/connective/$', api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/$', + url(r'^nodes/(?P[0-9a-zA-Z\-]{36})/children/$', api.NodeChildrenApi.as_view(), name='node-children'), - url(r'^v1/nodes/children/$', api.NodeChildrenApi.as_view(), name='node-children-2'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/children/add/$', + url(r'^nodes/children/$', api.NodeChildrenApi.as_view(), name='node-children-2'), + url(r'^nodes/(?P[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', + url(r'^nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.NodeAssetsApi.as_view(), name='node-assets'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/add/$', + url(r'^nodes/(?P[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/replace/$', + url(r'^nodes/(?P[0-9a-zA-Z\-]{36})/assets/replace/$', api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/assets/remove/$', + url(r'^nodes/(?P[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', + url(r'^nodes/(?P[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'), - url(r'^v1/nodes/(?P[0-9a-zA-Z\-]{36})/test-connective/$', + url(r'^nodes/(?P[0-9a-zA-Z\-]{36})/test-connective/$', api.TestNodeConnectiveApi.as_view(), name='node-test-connective'), - url(r'^v1/gateway/(?P[0-9a-zA-Z\-]{36})/test-connective/$', + url(r'^gateway/(?P[0-9a-zA-Z\-]{36})/test-connective/$', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'), ] diff --git a/apps/assets/views/admin_user.py b/apps/assets/views/admin_user.py index 7d7878e88..053e54e39 100644 --- a/apps/assets/views/admin_user.py +++ b/apps/assets/views/admin_user.py @@ -90,7 +90,7 @@ class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView): object = None def get(self, request, *args, **kwargs): - self.object = self.get_object(queryset=AdminUser.objects.all()) + self.object = self.get_object(queryset = AdminUser.objects) return super().get(request, *args, **kwargs) def get_queryset(self): diff --git a/apps/audits/api.py b/apps/audits/api.py index 0d583d246..9161ed7f3 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -9,6 +9,6 @@ from .serializers import FTPLogSerializer class FTPLogViewSet(viewsets.ModelViewSet): - queryset = FTPLog.objects.all() + queryset = FTPLog.objects serializer_class = FTPLogSerializer permission_classes = (IsSuperUserOrAppUser,) diff --git a/apps/audits/urls/api_urls.py b/apps/audits/urls/api_urls.py index f90057ca2..911d04c48 100644 --- a/apps/audits/urls/api_urls.py +++ b/apps/audits/urls/api_urls.py @@ -9,10 +9,10 @@ from .. import api app_name = "audits" router = DefaultRouter() -router.register(r'v1/ftp-log', api.FTPLogViewSet, 'ftp-log') +router.register(r'ftp-log', api.FTPLogViewSet, 'ftp-log') urlpatterns = [ -# url(r'^v1/celery/task/(?P[0-9a-zA-Z\-]{36})/log/$', api.CeleryTaskLogApi.as_view(), name='celery-task-log'), +# url(r'^celery/task/(?P[0-9a-zA-Z\-]{36})/log/$', api.CeleryTaskLogApi.as_view(), name='celery-task-log'), ] urlpatterns += router.urls diff --git a/apps/common/urls/api_urls.py b/apps/common/urls/api_urls.py index a9075e66e..ffc472ba0 100644 --- a/apps/common/urls/api_urls.py +++ b/apps/common/urls/api_urls.py @@ -7,7 +7,7 @@ from .. import api app_name = 'common' urlpatterns = [ - url(r'^v1/mail/testing/$', api.MailTestingAPI.as_view(), name='mail-testing'), - url(r'^v1/ldap/testing/$', api.LDAPTestingAPI.as_view(), name='ldap-testing'), - url(r'^v1/django-settings/$', api.DjangoSettingsAPI.as_view(), name='django-settings'), + url(r'^mail/testing/$', api.MailTestingAPI.as_view(), name='mail-testing'), + url(r'^ldap/testing/$', api.LDAPTestingAPI.as_view(), name='ldap-testing'), + url(r'^django-settings/$', api.DjangoSettingsAPI.as_view(), name='django-settings'), ] diff --git a/apps/ops/api.py b/apps/ops/api.py index 35c9b8dc5..5480ef5e7 100644 --- a/apps/ops/api.py +++ b/apps/ops/api.py @@ -16,13 +16,13 @@ from .tasks import run_ansible_task class TaskViewSet(viewsets.ModelViewSet): - queryset = Task.objects.all() + queryset = Task.objects serializer_class = TaskSerializer permission_classes = (IsSuperUser,) class TaskRun(generics.RetrieveAPIView): - queryset = Task.objects.all() + queryset = Task.objects serializer_class = TaskViewSet permission_classes = (IsSuperUser,) @@ -33,7 +33,7 @@ class TaskRun(generics.RetrieveAPIView): class AdHocViewSet(viewsets.ModelViewSet): - queryset = AdHoc.objects.all() + queryset = AdHoc.objects serializer_class = AdHocSerializer permission_classes = (IsSuperUser,) @@ -46,7 +46,7 @@ class AdHocViewSet(viewsets.ModelViewSet): class AdHocRunHistorySet(viewsets.ModelViewSet): - queryset = AdHocRunHistory.objects.all() + queryset = AdHocRunHistory.objects serializer_class = AdHocRunHistorySerializer permission_classes = (IsSuperUser,) @@ -68,7 +68,7 @@ class CeleryTaskLogApi(generics.RetrieveAPIView): permission_classes = (IsSuperUser,) buff_size = 1024 * 10 end = False - queryset = CeleryTask.objects.all() + queryset = CeleryTask.objects def get(self, request, *args, **kwargs): mark = request.query_params.get("mark") or str(uuid.uuid4()) diff --git a/apps/ops/urls/api_urls.py b/apps/ops/urls/api_urls.py index d6390fed9..ed963621e 100644 --- a/apps/ops/urls/api_urls.py +++ b/apps/ops/urls/api_urls.py @@ -9,13 +9,13 @@ from .. import api app_name = "ops" router = DefaultRouter() -router.register(r'v1/tasks', api.TaskViewSet, 'task') -router.register(r'v1/adhoc', api.AdHocViewSet, 'adhoc') -router.register(r'v1/history', api.AdHocRunHistorySet, 'history') +router.register(r'tasks', api.TaskViewSet, 'task') +router.register(r'adhoc', api.AdHocViewSet, 'adhoc') +router.register(r'history', api.AdHocRunHistorySet, 'history') urlpatterns = [ - url(r'^v1/tasks/(?P[0-9a-zA-Z\-]{36})/run/$', api.TaskRun.as_view(), name='task-run'), - url(r'^v1/celery/task/(?P[0-9a-zA-Z\-]{36})/log/$', api.CeleryTaskLogApi.as_view(), name='celery-task-log'), + url(r'^tasks/(?P[0-9a-zA-Z\-]{36})/run/$', api.TaskRun.as_view(), name='task-run'), + url(r'^celery/task/(?P[0-9a-zA-Z\-]{36})/log/$', api.CeleryTaskLogApi.as_view(), name='celery-task-log'), ] urlpatterns += router.urls diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index f5d59e686..a0e2bc3d8 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -15,62 +15,35 @@ __all__ = ['OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin'] class OrgManager(models.Manager): def get_queryset(self): + print("GET CURR") current_org = get_current_org() - user_model = get_user_model() kwargs = {} print("Get queryset ") - print(self.model) print(current_org) if not current_org: + return super().get_queryset().filter(**kwargs) kwargs['id'] = None - elif issubclass(self.model, user_model): - kwargs['orgs'] = current_org elif current_org.is_real(): kwargs['org'] = current_org elif current_org.is_default(): kwargs['org'] = None + queryset = super().get_queryset().filter(**kwargs) print(kwargs) - return super().get_queryset().filter(**kwargs) + print(queryset) + return queryset class OrgModelMixin(models.Model): org = models.ForeignKey('orgs.Organization', on_delete=models.PROTECT, null=True) - objects = OrgManager() - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _do_update(self, base_qs, using, pk_val, values, update_fields, forced_update): + def save(self, *args, **kwargs): current_org = get_current_org() - if current_org and current_org.is_real(): - kwargs = {'org': current_org} - base_qs = base_qs.filter(**kwargs) - else: - logger.warn( - 'Attempting to update %s instance "%s" without a current tenant ' - 'set. This may cause issues in a partitioned environment. ' - 'Recommend calling set_current_org() before performing this ' - 'operation.', self._meta.model.__name__, self - ) - return super()._do_update(base_qs, using, pk_val, values, update_fields, forced_update) - - def save(self, force_insert=False, force_update=False, using=None, - update_fields=None): - user_model = get_user_model() - current_org = get_current_org() - if current_org and not current_org.is_real(): self.org = current_org - instance = super().save( - force_insert=force_insert, force_update=force_update, - using=using, update_fields=update_fields - ) - if isinstance(instance, user_model): - instance.orgs.add(current_org) - return instance + return super(OrgModelMixin, self).save(*args, **kwargs) class Meta: abstract = True diff --git a/apps/perms/api.py b/apps/perms/api.py index 40366a19b..50b81c22d 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -19,7 +19,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): """ 资产授权列表的增删改查api """ - queryset = AssetPermission.objects.all() + queryset = AssetPermission.objects serializer_class = serializers.AssetPermissionCreateUpdateSerializer permission_classes = (IsSuperUser,) @@ -268,7 +268,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView): """ permission_classes = (IsSuperUser,) serializer_class = serializers.AssetPermissionUpdateUserSerializer - queryset = AssetPermission.objects.all() + queryset = AssetPermission.objects def update(self, request, *args, **kwargs): perm = self.get_object() @@ -285,7 +285,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView): class AssetPermissionAddUserApi(RetrieveUpdateAPIView): permission_classes = (IsSuperUser,) serializer_class = serializers.AssetPermissionUpdateUserSerializer - queryset = AssetPermission.objects.all() + queryset = AssetPermission.objects def update(self, request, *args, **kwargs): perm = self.get_object() @@ -305,7 +305,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView): """ permission_classes = (IsSuperUser,) serializer_class = serializers.AssetPermissionUpdateAssetSerializer - queryset = AssetPermission.objects.all() + queryset = AssetPermission.objects def update(self, request, *args, **kwargs): perm = self.get_object() @@ -322,7 +322,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView): class AssetPermissionAddAssetApi(RetrieveUpdateAPIView): permission_classes = (IsSuperUser,) serializer_class = serializers.AssetPermissionUpdateAssetSerializer - queryset = AssetPermission.objects.all() + queryset = AssetPermission.objects def update(self, request, *args, **kwargs): perm = self.get_object() diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index f15450141..c41d1e291 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -7,57 +7,57 @@ from .. import api app_name = 'perms' router = routers.DefaultRouter() -router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission') +router.register('asset-permissions', api.AssetPermissionViewSet, 'asset-permission') urlpatterns = [ # 查询某个用户授权的资产和资产组 - url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/assets/$', + url(r'^user/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'), - url(r'^v1/user/assets/$', api.UserGrantedAssetsApi.as_view(), + url(r'^user/assets/$', api.UserGrantedAssetsApi.as_view(), name='my-assets'), - url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/nodes/$', + url(r'^user/(?P[0-9a-zA-Z\-]{36})/nodes/$', api.UserGrantedNodesApi.as_view(), name='user-nodes'), - url(r'^v1/user/nodes/$', api.UserGrantedNodesApi.as_view(), + url(r'^user/nodes/$', api.UserGrantedNodesApi.as_view(), name='my-nodes'), url( - r'^v1/user/(?P[0-9a-zA-Z\-]{36})/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', + r'^user/(?P[0-9a-zA-Z\-]{36})/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'), - url(r'^v1/user/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', + url(r'^user/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'), - url(r'^v1/user/(?P[0-9a-zA-Z\-]{36})/nodes-assets/$', + url(r'^user/(?P[0-9a-zA-Z\-]{36})/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes-assets'), - url(r'^v1/user/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(), + url(r'^user/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(), name='my-nodes-assets'), # 查询某个用户组授权的资产和资产组 - url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/assets/$', + url(r'^user-group/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'), - url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/nodes/$', + url(r'^user-group/(?P[0-9a-zA-Z\-]{36})/nodes/$', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'), - url(r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/nodes-assets/$', + url(r'^user-group/(?P[0-9a-zA-Z\-]{36})/nodes-assets/$', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'), url( - r'^v1/user-group/(?P[0-9a-zA-Z\-]{36})/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', + r'^user-group/(?P[0-9a-zA-Z\-]{36})/nodes/(?P[0-9a-zA-Z\-]{36})/assets/$', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'), # 用户和资产授权变更 - url(r'^v1/asset-permissions/(?P[0-9a-zA-Z\-]{36})/user/remove/$', + url(r'^asset-permissions/(?P[0-9a-zA-Z\-]{36})/user/remove/$', api.AssetPermissionRemoveUserApi.as_view(), name='asset-permission-remove-user'), - url(r'^v1/asset-permissions/(?P[0-9a-zA-Z\-]{36})/user/add/$', + url(r'^asset-permissions/(?P[0-9a-zA-Z\-]{36})/user/add/$', api.AssetPermissionAddUserApi.as_view(), name='asset-permission-add-user'), - url(r'^v1/asset-permissions/(?P[0-9a-zA-Z\-]{36})/asset/remove/$', + url(r'^asset-permissions/(?P[0-9a-zA-Z\-]{36})/asset/remove/$', api.AssetPermissionRemoveAssetApi.as_view(), name='asset-permission-remove-asset'), - url(r'^v1/asset-permissions/(?P[0-9a-zA-Z\-]{36})/asset/add/$', + url(r'^asset-permissions/(?P[0-9a-zA-Z\-]{36})/asset/add/$', api.AssetPermissionAddAssetApi.as_view(), name='asset-permission-add-asset'), # 验证用户是否有某个资产和系统用户的权限 - url(r'v1/asset-permission/user/validate/$', api.ValidateUserAssetPermissionView.as_view(), name='validate-user-asset-permission'), + url(r'asset-permission/user/validate/$', api.ValidateUserAssetPermissionView.as_view(), name='validate-user-asset-permission'), ] urlpatterns += router.urls diff --git a/apps/perms/views.py b/apps/perms/views.py index 9afbc6ffe..1a1b5cccd 100644 --- a/apps/perms/views.py +++ b/apps/perms/views.py @@ -108,7 +108,7 @@ class AssetPermissionUserView(AdminUserRequiredMixin, object = None def get(self, request, *args, **kwargs): - self.object = self.get_object(queryset=AssetPermission.objects.all()) + self.object = self.get_object(queryset = AssetPermission.objects) return super().get(request, *args, **kwargs) def get_queryset(self): @@ -138,7 +138,7 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, object = None def get(self, request, *args, **kwargs): - self.object = self.get_object(queryset=AssetPermission.objects.all()) + self.object = self.get_object(queryset = AssetPermission.objects) return super().get(request, *args, **kwargs) def get_queryset(self): diff --git a/apps/terminal/api.py b/apps/terminal/api.py index f5c76a73f..a7b2217c2 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -102,7 +102,7 @@ class TerminalTokenApi(APIView): class StatusViewSet(viewsets.ModelViewSet): - queryset = Status.objects.all() + queryset = Status.objects serializer_class = StatusSerializer permission_classes = (IsSuperUserOrAppUser,) session_serializer_class = SessionSerializer @@ -174,7 +174,7 @@ class StatusViewSet(viewsets.ModelViewSet): class SessionViewSet(viewsets.ModelViewSet): - queryset = Session.objects.all() + queryset = Session.objects serializer_class = SessionSerializer permission_classes = (IsSuperUserOrAppUser,) @@ -192,7 +192,7 @@ class SessionViewSet(viewsets.ModelViewSet): class TaskViewSet(BulkModelViewSet): - queryset = Task.objects.all() + queryset = Task.objects serializer_class = TaskSerializer permission_classes = (IsSuperUserOrAppUser,) diff --git a/apps/terminal/urls/api_urls.py b/apps/terminal/urls/api_urls.py index fa8ff5434..114ea82b3 100644 --- a/apps/terminal/urls/api_urls.py +++ b/apps/terminal/urls/api_urls.py @@ -10,22 +10,22 @@ from .. import api app_name = 'terminal' router = routers.DefaultRouter() -router.register(r'v1/terminal/(?P[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status') -router.register(r'v1/terminal/(?P[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions') -router.register(r'v1/tasks', api.TaskViewSet, 'tasks') -router.register(r'v1/terminal', api.TerminalViewSet, 'terminal') -router.register(r'v1/command', api.CommandViewSet, 'command') -router.register(r'v1/sessions', api.SessionViewSet, 'session') -router.register(r'v1/status', api.StatusViewSet, 'session') +router.register(r'terminal/(?P[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status') +router.register(r'terminal/(?P[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions') +router.register(r'tasks', api.TaskViewSet, 'tasks') +router.register(r'terminal', api.TerminalViewSet, 'terminal') +router.register(r'command', api.CommandViewSet, 'command') +router.register(r'sessions', api.SessionViewSet, 'session') +router.register(r'status', api.StatusViewSet, 'session') urlpatterns = [ - url(r'^v1/sessions/(?P[0-9a-zA-Z\-]{36})/replay/$', + url(r'^sessions/(?P[0-9a-zA-Z\-]{36})/replay/$', api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}), name='session-replay'), - url(r'^v1/tasks/kill-session/', api.KillSessionAPI.as_view(), name='kill-session'), - url(r'^v1/terminal/(?P[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), + url(r'^tasks/kill-session/', api.KillSessionAPI.as_view(), name='kill-session'), + url(r'^terminal/(?P[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key'), - url(r'^v1/terminal/config', api.TerminalConfig.as_view(), name='terminal-config'), + url(r'^terminal/config', api.TerminalConfig.as_view(), name='terminal-config'), # v2: get session's replay url(r'^v2/sessions/(?P[0-9a-zA-Z\-]{36})/replay/$', api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}), diff --git a/apps/users/api.py b/apps/users/api.py index 10a12ee66..b53db1884 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -9,6 +9,7 @@ from rest_framework import generics from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView +from rest_framework import viewsets from rest_framework_bulk import BulkModelViewSet from .serializers import UserSerializer, UserGroupSerializer, \ @@ -21,6 +22,7 @@ from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly, \ from .utils import check_user_valid, generate_token, get_login_ip, \ check_otp_code, set_user_login_failed_count_to_cache, is_block_login from orgs.utils import get_current_org +from orgs.mixins import OrgViewGenericMixin from common.mixins import IDInFilterMixin from common.utils import get_logger @@ -51,7 +53,7 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet): class ChangeUserPasswordApi(generics.RetrieveUpdateAPIView): permission_classes = (IsSuperUser,) - queryset = User.objects.all() + queryset = User.objects serializer_class = ChangeUserPasswordSerializer def perform_update(self, serializer): @@ -61,13 +63,13 @@ class ChangeUserPasswordApi(generics.RetrieveUpdateAPIView): class UserUpdateGroupApi(generics.RetrieveUpdateAPIView): - queryset = User.objects.all() + queryset = User.objects serializer_class = UserUpdateGroupSerializer permission_classes = (IsSuperUser,) class UserResetPasswordApi(generics.UpdateAPIView): - queryset = User.objects.all() + queryset = User.objects serializer_class = UserSerializer permission_classes = (IsAuthenticated,) @@ -82,7 +84,7 @@ class UserResetPasswordApi(generics.UpdateAPIView): class UserResetPKApi(generics.UpdateAPIView): - queryset = User.objects.all() + queryset = User.objects serializer_class = UserSerializer permission_classes = (IsAuthenticated,) @@ -95,7 +97,7 @@ class UserResetPKApi(generics.UpdateAPIView): class UserUpdatePKApi(generics.UpdateAPIView): - queryset = User.objects.all() + queryset = User.objects serializer_class = UserPKUpdateSerializer permission_classes = (IsCurrentUserOrReadOnly,) @@ -105,14 +107,14 @@ class UserUpdatePKApi(generics.UpdateAPIView): user.save() -class UserGroupViewSet(IDInFilterMixin, BulkModelViewSet): - queryset = UserGroup.objects.all() +class UserGroupViewSet(IDInFilterMixin, OrgViewGenericMixin, BulkModelViewSet): + queryset = UserGroup.objects serializer_class = UserGroupSerializer permission_classes = (IsSuperUser,) class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView): - queryset = UserGroup.objects.all() + queryset = UserGroup.objects serializer_class = UserGroupUpdateMemeberSerializer permission_classes = (IsSuperUser,) diff --git a/apps/users/forms.py b/apps/users/forms.py index 165694c3f..5b45abf6c 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -54,6 +54,15 @@ class UserCreateUpdateForm(forms.ModelForm): widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), help_text=_('Paste user id_rsa.pub here.') ) + # groups = forms.ModelMultipleChoiceField( + # queryset=UserGroup.objects, required=False, label=_("Groups"), + # widget=forms.SelectMultiple( + # attrs={ + # 'class': 'select2', + # 'data-placeholder': _('Join user groups') + # } + # ) + # ) class Meta: model = User @@ -67,12 +76,6 @@ class UserCreateUpdateForm(forms.ModelForm): 'email': '* required', } widgets = { - 'groups': forms.SelectMultiple( - attrs={ - 'class': 'select2', - 'data-placeholder': _('Join user groups') - } - ), 'otp_level': forms.RadioSelect(), } @@ -237,7 +240,7 @@ class UserBulkUpdateForm(forms.ModelForm): required=True, help_text='* required', label=_('Select users'), - queryset=User.objects.all(), + queryset = User.objects, widget=forms.SelectMultiple( attrs={ 'class': 'select2', diff --git a/apps/users/models/group.py b/apps/users/models/group.py index 9196156f7..7730c12d0 100644 --- a/apps/users/models/group.py +++ b/apps/users/models/group.py @@ -4,10 +4,12 @@ import uuid from django.db import models, IntegrityError from django.utils.translation import ugettext_lazy as _ +from orgs.mixins import OrgModelMixin + __all__ = ['UserGroup'] -class UserGroup(models.Model): +class UserGroup(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) comment = models.TextField(blank=True, verbose_name=_('Comment')) diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 30054f137..172a8fcde 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -220,11 +220,10 @@ class User(AbstractUser): if self.username == 'admin': self.role = 'Admin' self.is_active = True - instance = super().save(*args, **kwargs) + super().save(*args, **kwargs) current_org = get_current_org() if current_org and current_org.is_real(): - instance.orgs.add(current_org) - return instance + self.orgs.add(current_org) @property def private_token(self): diff --git a/apps/users/serializers.py b/apps/users/serializers.py index d3f3eb73f..5fbeebe4e 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -14,7 +14,7 @@ signer = get_signer() class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): groups_display = serializers.SerializerMethodField() - groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all(), required=False) + groups = serializers.PrimaryKeyRelatedField(many=True, queryset = UserGroup.objects, required=False) class Meta: model = User @@ -50,7 +50,7 @@ class UserPKUpdateSerializer(serializers.ModelSerializer): class UserUpdateGroupSerializer(serializers.ModelSerializer): - groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all()) + groups = serializers.PrimaryKeyRelatedField(many=True, queryset = UserGroup.objects) class Meta: model = User @@ -71,7 +71,7 @@ class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer): - users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all()) + users = serializers.PrimaryKeyRelatedField(many=True, queryset = User.objects) class Meta: model = UserGroup diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py index 683638a4e..58c7e43a2 100644 --- a/apps/users/urls/api_urls.py +++ b/apps/users/urls/api_urls.py @@ -10,28 +10,28 @@ from .. import api app_name = 'users' router = BulkRouter() -router.register(r'v1/users', api.UserViewSet, 'user') -router.register(r'v1/groups', api.UserGroupViewSet, 'user-group') +router.register(r'users', api.UserViewSet, 'user') +router.register(r'groups', api.UserGroupViewSet, 'user-group') urlpatterns = [ # url(r'', api.UserListView.as_view()), - url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'), - url(r'^v1/connection-token/$', api.UserConnectionTokenApi.as_view(), name='connection-token'), - url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'), - url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'), - url(r'^v1/otp/auth/$', api.UserOtpAuthApi.as_view(), name='user-otp-auth'), - url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/password/$', + url(r'^token/$', api.UserToken.as_view(), name='user-token'), + url(r'^connection-token/$', api.UserConnectionTokenApi.as_view(), name='connection-token'), + url(r'^profile/$', api.UserProfile.as_view(), name='user-profile'), + url(r'^auth/$', api.UserAuthApi.as_view(), name='user-auth'), + url(r'^otp/auth/$', api.UserOtpAuthApi.as_view(), name='user-otp-auth'), + url(r'^users/(?P[0-9a-zA-Z\-]{36})/password/$', api.ChangeUserPasswordApi.as_view(), name='change-user-password'), - url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/password/reset/$', + url(r'^users/(?P[0-9a-zA-Z\-]{36})/password/reset/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'), - url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/pubkey/reset/$', + url(r'^users/(?P[0-9a-zA-Z\-]{36})/pubkey/reset/$', api.UserResetPKApi.as_view(), name='user-public-key-reset'), - url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/pubkey/update/$', + url(r'^users/(?P[0-9a-zA-Z\-]{36})/pubkey/update/$', api.UserUpdatePKApi.as_view(), name='user-public-key-update'), - url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/groups/$', + url(r'^users/(?P[0-9a-zA-Z\-]{36})/groups/$', api.UserUpdateGroupApi.as_view(), name='user-update-group'), - url(r'^v1/groups/(?P[0-9a-zA-Z\-]{36})/users/$', + url(r'^groups/(?P[0-9a-zA-Z\-]{36})/users/$', api.UserGroupUpdateUserApi.as_view(), name='user-group-update-user'), ] diff --git a/apps/users/views/group.py b/apps/users/views/group.py index 1fcec8ca7..657acd8d9 100644 --- a/apps/users/views/group.py +++ b/apps/users/views/group.py @@ -11,6 +11,7 @@ from django.contrib.messages.views import SuccessMessageMixin from common.utils import get_logger from common.const import create_success_msg, update_success_msg +from orgs.mixins import OrgViewGenericMixin from ..models import User, UserGroup from ..utils import AdminUserRequiredMixin from .. import forms @@ -20,7 +21,7 @@ __all__ = ['UserGroupListView', 'UserGroupCreateView', 'UserGroupDetailView', logger = get_logger(__name__) -class UserGroupListView(AdminUserRequiredMixin, TemplateView): +class UserGroupListView(AdminUserRequiredMixin, OrgViewGenericMixin, TemplateView): template_name = 'users/user_group_list.html' def get_context_data(self, **kwargs): @@ -32,7 +33,8 @@ class UserGroupListView(AdminUserRequiredMixin, TemplateView): return super().get_context_data(**kwargs) -class UserGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): +class UserGroupCreateView(AdminUserRequiredMixin, OrgViewGenericMixin, + SuccessMessageMixin, CreateView): model = UserGroup form_class = forms.UserGroupForm template_name = 'users/user_group_create_update.html' @@ -48,7 +50,8 @@ class UserGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie return super().get_context_data(**kwargs) -class UserGroupUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView): +class UserGroupUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, + OrgViewGenericMixin, UpdateView): model = UserGroup form_class = forms.UserGroupForm template_name = 'users/user_group_create_update.html' @@ -68,7 +71,7 @@ class UserGroupUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateVie return super().get_context_data(**kwargs) -class UserGroupDetailView(AdminUserRequiredMixin, DetailView): +class UserGroupDetailView(AdminUserRequiredMixin, OrgViewGenericMixin, DetailView): model = UserGroup context_object_name = 'user_group' template_name = 'users/user_group_detail.html' @@ -84,7 +87,7 @@ class UserGroupDetailView(AdminUserRequiredMixin, DetailView): return super().get_context_data(**kwargs) -class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView): +class UserGroupGrantedAssetView(AdminUserRequiredMixin, OrgViewGenericMixin, DetailView): model = UserGroup template_name = 'users/user_group_granted_asset.html' context_object_name = 'user_group' From 5648dcd7e79da9232e163fec1ac10d3c9af27af7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 15 Jul 2018 00:55:05 +0800 Subject: [PATCH 05/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E6=88=B7view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/admin_user.py | 8 ++-- apps/assets/api/asset.py | 12 +++--- apps/assets/api/domain.py | 4 +- apps/assets/api/node.py | 12 +++--- apps/assets/api/system_user.py | 8 ++-- apps/assets/forms/asset.py | 4 +- apps/assets/forms/domain.py | 2 +- apps/assets/forms/label.py | 2 +- apps/assets/serializers/admin_user.py | 2 +- apps/assets/serializers/node.py | 2 +- apps/assets/views/admin_user.py | 2 +- apps/audits/api.py | 2 +- apps/jumpserver/settings.py | 1 + apps/ops/api.py | 10 ++--- apps/orgs/middleware.py | 11 ++++++ apps/orgs/mixins.py | 43 ++++++++++++++++++-- apps/perms/api.py | 10 ++--- apps/perms/views.py | 4 +- apps/terminal/api.py | 6 +-- apps/users/api.py | 15 ++++--- apps/users/forms.py | 57 +++++++++++---------------- apps/users/models/user.py | 1 + apps/users/serializers.py | 6 +-- apps/users/views/login.py | 14 ++++--- 24 files changed, 138 insertions(+), 100 deletions(-) diff --git a/apps/assets/api/admin_user.py b/apps/assets/api/admin_user.py index 74c44b48b..968cd6594 100644 --- a/apps/assets/api/admin_user.py +++ b/apps/assets/api/admin_user.py @@ -37,19 +37,19 @@ class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet): """ Admin user api set, for add,delete,update,list,retrieve resource """ - queryset = AdminUser.objects + queryset = AdminUser.objects.all() serializer_class = serializers.AdminUserSerializer permission_classes = (IsSuperUser,) class AdminUserAuthApi(generics.UpdateAPIView): - queryset = AdminUser.objects + queryset = AdminUser.objects.all() serializer_class = serializers.AdminUserAuthSerializer permission_classes = (IsSuperUser,) class ReplaceNodesAdminUserApi(generics.UpdateAPIView): - queryset = AdminUser.objects + queryset = AdminUser.objects.all() serializer_class = serializers.ReplaceNodeAdminUserSerializer permission_classes = (IsSuperUser,) @@ -74,7 +74,7 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView): """ Test asset admin user connectivity """ - queryset = AdminUser.objects + queryset = AdminUser.objects.all() permission_classes = (IsSuperUser,) def retrieve(self, request, *args, **kwargs): diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py index ad42aeeba..8c1f3d726 100644 --- a/apps/assets/api/asset.py +++ b/apps/assets/api/asset.py @@ -36,7 +36,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): filter_fields = ("hostname", "ip") search_fields = filter_fields ordering_fields = ("hostname", "ip", "port", "cpu_cores") - queryset = Asset.objects + queryset = Asset.objects.all() serializer_class = serializers.AssetSerializer pagination_class = LimitOffsetPagination permission_classes = (IsSuperUserOrAppUser,) @@ -65,7 +65,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): if node_id and not show_current_asset: node = get_object_or_404(Node, id=node_id) if node.is_root(): - queryset = Asset.objects + queryset = Asset.objects.all() else: queryset = queryset.filter( nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key), @@ -77,7 +77,7 @@ class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): """ Asset bulk update api """ - queryset = Asset.objects + queryset = Asset.objects.all() serializer_class = serializers.AssetSerializer permission_classes = (IsSuperUser,) @@ -86,7 +86,7 @@ class AssetRefreshHardwareApi(generics.RetrieveAPIView): """ Refresh asset hardware info """ - queryset = Asset.objects + queryset = Asset.objects.all() serializer_class = serializers.AssetSerializer permission_classes = (IsSuperUser,) @@ -101,7 +101,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView): """ Test asset admin user connectivity """ - queryset = Asset.objects + queryset = Asset.objects.all() permission_classes = (IsSuperUser,) def retrieve(self, request, *args, **kwargs): @@ -112,7 +112,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView): class AssetGatewayApi(generics.RetrieveAPIView): - queryset = Asset.objects + queryset = Asset.objects.all() permission_classes = (IsSuperUserOrAppUser,) def retrieve(self, request, *args, **kwargs): diff --git a/apps/assets/api/domain.py b/apps/assets/api/domain.py index 88a5ba2e0..5114b5561 100644 --- a/apps/assets/api/domain.py +++ b/apps/assets/api/domain.py @@ -18,7 +18,7 @@ __all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"] class DomainViewSet(BulkModelViewSet): - queryset = Domain.objects + queryset = Domain.objects.all() permission_classes = (IsSuperUser,) serializer_class = serializers.DomainSerializer @@ -36,7 +36,7 @@ class DomainViewSet(BulkModelViewSet): class GatewayViewSet(BulkModelViewSet): filter_fields = ("domain",) search_fields = filter_fields - queryset = Gateway.objects + queryset = Gateway.objects.all() permission_classes = (IsSuperUser,) serializer_class = serializers.GatewaySerializer diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index 8cd9c557e..e5ace021e 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -40,7 +40,7 @@ __all__ = [ class NodeViewSet(BulkModelViewSet): - queryset = Node.objects + queryset = Node.objects.all() permission_classes = (IsSuperUser,) serializer_class = serializers.NodeSerializer @@ -79,7 +79,7 @@ class NodeViewSet(BulkModelViewSet): class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView): - queryset = Node.objects + queryset = Node.objects.all() permission_classes = (IsSuperUser,) serializer_class = serializers.NodeSerializer instance = None @@ -166,7 +166,7 @@ class NodeAssetsApi(generics.ListAPIView): class NodeAddChildrenApi(generics.UpdateAPIView): - queryset = Node.objects + queryset = Node.objects.all() permission_classes = (IsSuperUser,) serializer_class = serializers.NodeAddChildrenSerializer instance = None @@ -184,7 +184,7 @@ class NodeAddChildrenApi(generics.UpdateAPIView): class NodeAddAssetsApi(generics.UpdateAPIView): serializer_class = serializers.NodeAssetsSerializer - queryset = Node.objects + queryset = Node.objects.all() permission_classes = (IsSuperUser,) instance = None @@ -196,7 +196,7 @@ class NodeAddAssetsApi(generics.UpdateAPIView): class NodeRemoveAssetsApi(generics.UpdateAPIView): serializer_class = serializers.NodeAssetsSerializer - queryset = Node.objects + queryset = Node.objects.all() permission_classes = (IsSuperUser,) instance = None @@ -212,7 +212,7 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView): class NodeReplaceAssetsApi(generics.UpdateAPIView): serializer_class = serializers.NodeAssetsSerializer - queryset = Node.objects + queryset = Node.objects.all() permission_classes = (IsSuperUser,) instance = None diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index 1fb77f01f..66d62232d 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -35,7 +35,7 @@ class SystemUserViewSet(BulkModelViewSet): """ System user api set, for add,delete,update,list,retrieve resource """ - queryset = SystemUser.objects + queryset = SystemUser.objects.all() serializer_class = serializers.SystemUserSerializer permission_classes = (IsSuperUserOrAppUser,) @@ -44,7 +44,7 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView): """ Get system user auth info """ - queryset = SystemUser.objects + queryset = SystemUser.objects.all() permission_classes = (IsSuperUserOrAppUser,) serializer_class = serializers.SystemUserAuthSerializer @@ -58,7 +58,7 @@ class SystemUserPushApi(generics.RetrieveAPIView): """ Push system user to cluster assets api """ - queryset = SystemUser.objects + queryset = SystemUser.objects.all() permission_classes = (IsSuperUser,) def retrieve(self, request, *args, **kwargs): @@ -74,7 +74,7 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView): """ Push system user to cluster assets api """ - queryset = SystemUser.objects + queryset = SystemUser.objects.all() permission_classes = (IsSuperUser,) def retrieve(self, request, *args, **kwargs): diff --git a/apps/assets/forms/asset.py b/apps/assets/forms/asset.py index 1f32219a7..516ce4afc 100644 --- a/apps/assets/forms/asset.py +++ b/apps/assets/forms/asset.py @@ -93,7 +93,7 @@ class AssetUpdateForm(forms.ModelForm): class AssetBulkUpdateForm(forms.ModelForm): assets = forms.ModelMultipleChoiceField( required=True, help_text='* required', - label=_('Select assets'), queryset = Asset.objects, + label=_('Select assets'), queryset=Asset.objects.all(), widget=forms.SelectMultiple( attrs={ 'class': 'select2', @@ -105,7 +105,7 @@ class AssetBulkUpdateForm(forms.ModelForm): label=_('Port'), required=False, min_value=1, max_value=65535, ) admin_user = forms.ModelChoiceField( - required=False, queryset = AdminUser.objects, + required=False, queryset=AdminUser.objects, label=_("Admin user"), widget=forms.Select( attrs={ diff --git a/apps/assets/forms/domain.py b/apps/assets/forms/domain.py index 2ad06fd78..ec3af8f2e 100644 --- a/apps/assets/forms/domain.py +++ b/apps/assets/forms/domain.py @@ -11,7 +11,7 @@ __all__ = ['DomainForm', 'GatewayForm'] class DomainForm(forms.ModelForm): assets = forms.ModelMultipleChoiceField( - queryset = Asset.objects, label=_('Asset'), required=False, + queryset=Asset.objects.all(), label=_('Asset'), required=False, widget=forms.SelectMultiple( attrs={'class': 'select2', 'data-placeholder': _('Select assets')} ) diff --git a/apps/assets/forms/label.py b/apps/assets/forms/label.py index 8d7ef4c44..ebdc9384e 100644 --- a/apps/assets/forms/label.py +++ b/apps/assets/forms/label.py @@ -10,7 +10,7 @@ __all__ = ['LabelForm'] class LabelForm(forms.ModelForm): assets = forms.ModelMultipleChoiceField( - queryset = Asset.objects, label=_('Asset'), required=False, + queryset=Asset.objects.all(), label=_('Asset'), required=False, widget=forms.SelectMultiple( attrs={'class': 'select2', 'data-placeholder': _('Select assets')} ) diff --git a/apps/assets/serializers/admin_user.py b/apps/assets/serializers/admin_user.py index f7156f049..e1ecdf1c3 100644 --- a/apps/assets/serializers/admin_user.py +++ b/apps/assets/serializers/admin_user.py @@ -58,7 +58,7 @@ class ReplaceNodeAdminUserSerializer(serializers.ModelSerializer): 管理用户更新关联到的集群 """ nodes = serializers.PrimaryKeyRelatedField( - many=True, queryset = Node.objects + many=True, queryset = Node.objects.all() ) class Meta: diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py index a5788e229..0352dfecc 100644 --- a/apps/assets/serializers/node.py +++ b/apps/assets/serializers/node.py @@ -78,7 +78,7 @@ class NodeSerializer(serializers.ModelSerializer): class NodeAssetsSerializer(serializers.ModelSerializer): - assets = serializers.PrimaryKeyRelatedField(many=True, queryset = Asset.objects) + assets = serializers.PrimaryKeyRelatedField(many=True, queryset = Asset.objects.all()) class Meta: model = Node diff --git a/apps/assets/views/admin_user.py b/apps/assets/views/admin_user.py index 053e54e39..7d7878e88 100644 --- a/apps/assets/views/admin_user.py +++ b/apps/assets/views/admin_user.py @@ -90,7 +90,7 @@ class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView): object = None def get(self, request, *args, **kwargs): - self.object = self.get_object(queryset = AdminUser.objects) + self.object = self.get_object(queryset=AdminUser.objects.all()) return super().get(request, *args, **kwargs) def get_queryset(self): diff --git a/apps/audits/api.py b/apps/audits/api.py index 9161ed7f3..0d583d246 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -9,6 +9,6 @@ from .serializers import FTPLogSerializer class FTPLogViewSet(viewsets.ModelViewSet): - queryset = FTPLog.objects + queryset = FTPLog.objects.all() serializer_class = FTPLogSerializer permission_classes = (IsSuperUserOrAppUser,) diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 77a37839d..de79cd97b 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -78,6 +78,7 @@ INSTALLED_APPS = [ ] MIDDLEWARE = [ + 'orgs.middleware.OrgPreMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', diff --git a/apps/ops/api.py b/apps/ops/api.py index 5480ef5e7..35c9b8dc5 100644 --- a/apps/ops/api.py +++ b/apps/ops/api.py @@ -16,13 +16,13 @@ from .tasks import run_ansible_task class TaskViewSet(viewsets.ModelViewSet): - queryset = Task.objects + queryset = Task.objects.all() serializer_class = TaskSerializer permission_classes = (IsSuperUser,) class TaskRun(generics.RetrieveAPIView): - queryset = Task.objects + queryset = Task.objects.all() serializer_class = TaskViewSet permission_classes = (IsSuperUser,) @@ -33,7 +33,7 @@ class TaskRun(generics.RetrieveAPIView): class AdHocViewSet(viewsets.ModelViewSet): - queryset = AdHoc.objects + queryset = AdHoc.objects.all() serializer_class = AdHocSerializer permission_classes = (IsSuperUser,) @@ -46,7 +46,7 @@ class AdHocViewSet(viewsets.ModelViewSet): class AdHocRunHistorySet(viewsets.ModelViewSet): - queryset = AdHocRunHistory.objects + queryset = AdHocRunHistory.objects.all() serializer_class = AdHocRunHistorySerializer permission_classes = (IsSuperUser,) @@ -68,7 +68,7 @@ class CeleryTaskLogApi(generics.RetrieveAPIView): permission_classes = (IsSuperUser,) buff_size = 1024 * 10 end = False - queryset = CeleryTask.objects + queryset = CeleryTask.objects.all() def get(self, request, *args, **kwargs): mark = request.query_params.get("mark") or str(uuid.uuid4()) diff --git a/apps/orgs/middleware.py b/apps/orgs/middleware.py index 04a2d6f7a..102178575 100644 --- a/apps/orgs/middleware.py +++ b/apps/orgs/middleware.py @@ -2,6 +2,17 @@ # from .utils import get_org_from_request, set_current_org +from .models import Organization + + +class OrgPreMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + set_current_org(Organization.root()) + response = self.get_response(request) + return response class OrgMiddleware: diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index a0e2bc3d8..d9f55d301 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -2,18 +2,26 @@ # from django.db import models from django.shortcuts import redirect +import warnings from django.contrib.auth import get_user_model +from django.forms import ModelForm from common.utils import get_logger -from .utils import get_current_org, get_model_by_db_table +from .utils import get_current_org, get_model_by_db_table, set_current_org logger = get_logger(__file__) -__all__ = ['OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin'] +__all__ = [ + 'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm' +] class OrgManager(models.Manager): + def __init__(self, *args, **kwargs): + print("INit manager") + super().__init__(*args, **kwargs) + def get_queryset(self): print("GET CURR") current_org = get_current_org() @@ -22,9 +30,9 @@ class OrgManager(models.Manager): print("Get queryset ") print(current_org) + print(self.model) if not current_org: - return super().get_queryset().filter(**kwargs) - kwargs['id'] = None + pass elif current_org.is_real(): kwargs['org'] = current_org elif current_org.is_default(): @@ -34,6 +42,19 @@ class OrgManager(models.Manager): print(queryset) return queryset + def all(self): + current_org = get_current_org() + if not current_org: + msg = 'You should `objects.set_current_org(org).all()` then run it' + warnings.warn(msg) + return self + else: + return super().all() + + def set_current_org(self, org): + set_current_org(org) + return self + class OrgModelMixin(models.Model): org = models.ForeignKey('orgs.Organization', on_delete=models.PROTECT, null=True) @@ -55,3 +76,17 @@ class OrgViewGenericMixin: if not current_org: return redirect('orgs:switch-a-org') return super().dispatch(request, *args, **kwargs) + + +class OrgModelForm(ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if 'initial' not in kwargs: + return + for name, field in self.fields.items(): + if not hasattr(field, 'queryset'): + continue + print(field) + model = field.queryset.model + field.queryset = model.objects.all() + diff --git a/apps/perms/api.py b/apps/perms/api.py index 50b81c22d..40366a19b 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -19,7 +19,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet): """ 资产授权列表的增删改查api """ - queryset = AssetPermission.objects + queryset = AssetPermission.objects.all() serializer_class = serializers.AssetPermissionCreateUpdateSerializer permission_classes = (IsSuperUser,) @@ -268,7 +268,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView): """ permission_classes = (IsSuperUser,) serializer_class = serializers.AssetPermissionUpdateUserSerializer - queryset = AssetPermission.objects + queryset = AssetPermission.objects.all() def update(self, request, *args, **kwargs): perm = self.get_object() @@ -285,7 +285,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView): class AssetPermissionAddUserApi(RetrieveUpdateAPIView): permission_classes = (IsSuperUser,) serializer_class = serializers.AssetPermissionUpdateUserSerializer - queryset = AssetPermission.objects + queryset = AssetPermission.objects.all() def update(self, request, *args, **kwargs): perm = self.get_object() @@ -305,7 +305,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView): """ permission_classes = (IsSuperUser,) serializer_class = serializers.AssetPermissionUpdateAssetSerializer - queryset = AssetPermission.objects + queryset = AssetPermission.objects.all() def update(self, request, *args, **kwargs): perm = self.get_object() @@ -322,7 +322,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView): class AssetPermissionAddAssetApi(RetrieveUpdateAPIView): permission_classes = (IsSuperUser,) serializer_class = serializers.AssetPermissionUpdateAssetSerializer - queryset = AssetPermission.objects + queryset = AssetPermission.objects.all() def update(self, request, *args, **kwargs): perm = self.get_object() diff --git a/apps/perms/views.py b/apps/perms/views.py index 1a1b5cccd..a0095a3a5 100644 --- a/apps/perms/views.py +++ b/apps/perms/views.py @@ -108,7 +108,7 @@ class AssetPermissionUserView(AdminUserRequiredMixin, object = None def get(self, request, *args, **kwargs): - self.object = self.get_object(queryset = AssetPermission.objects) + self.object = self.get_object(queryset = AssetPermission.objects.all()) return super().get(request, *args, **kwargs) def get_queryset(self): @@ -138,7 +138,7 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, object = None def get(self, request, *args, **kwargs): - self.object = self.get_object(queryset = AssetPermission.objects) + self.object = self.get_object(queryset = AssetPermission.objects.all()) return super().get(request, *args, **kwargs) def get_queryset(self): diff --git a/apps/terminal/api.py b/apps/terminal/api.py index a7b2217c2..f5c76a73f 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -102,7 +102,7 @@ class TerminalTokenApi(APIView): class StatusViewSet(viewsets.ModelViewSet): - queryset = Status.objects + queryset = Status.objects.all() serializer_class = StatusSerializer permission_classes = (IsSuperUserOrAppUser,) session_serializer_class = SessionSerializer @@ -174,7 +174,7 @@ class StatusViewSet(viewsets.ModelViewSet): class SessionViewSet(viewsets.ModelViewSet): - queryset = Session.objects + queryset = Session.objects.all() serializer_class = SessionSerializer permission_classes = (IsSuperUserOrAppUser,) @@ -192,7 +192,7 @@ class SessionViewSet(viewsets.ModelViewSet): class TaskViewSet(BulkModelViewSet): - queryset = Task.objects + queryset = Task.objects.all() serializer_class = TaskSerializer permission_classes = (IsSuperUserOrAppUser,) diff --git a/apps/users/api.py b/apps/users/api.py index b53db1884..28e2e95d8 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -9,7 +9,6 @@ from rest_framework import generics from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework import viewsets from rest_framework_bulk import BulkModelViewSet from .serializers import UserSerializer, UserGroupSerializer, \ @@ -53,7 +52,7 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet): class ChangeUserPasswordApi(generics.RetrieveUpdateAPIView): permission_classes = (IsSuperUser,) - queryset = User.objects + queryset = User.objects.all() serializer_class = ChangeUserPasswordSerializer def perform_update(self, serializer): @@ -63,13 +62,13 @@ class ChangeUserPasswordApi(generics.RetrieveUpdateAPIView): class UserUpdateGroupApi(generics.RetrieveUpdateAPIView): - queryset = User.objects + queryset = User.objects.all() serializer_class = UserUpdateGroupSerializer permission_classes = (IsSuperUser,) class UserResetPasswordApi(generics.UpdateAPIView): - queryset = User.objects + queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (IsAuthenticated,) @@ -84,7 +83,7 @@ class UserResetPasswordApi(generics.UpdateAPIView): class UserResetPKApi(generics.UpdateAPIView): - queryset = User.objects + queryset = User.objects.all() serializer_class = UserSerializer permission_classes = (IsAuthenticated,) @@ -97,7 +96,7 @@ class UserResetPKApi(generics.UpdateAPIView): class UserUpdatePKApi(generics.UpdateAPIView): - queryset = User.objects + queryset = User.objects.all() serializer_class = UserPKUpdateSerializer permission_classes = (IsCurrentUserOrReadOnly,) @@ -108,13 +107,13 @@ class UserUpdatePKApi(generics.UpdateAPIView): class UserGroupViewSet(IDInFilterMixin, OrgViewGenericMixin, BulkModelViewSet): - queryset = UserGroup.objects + queryset = UserGroup.objects.all() serializer_class = UserGroupSerializer permission_classes = (IsSuperUser,) class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView): - queryset = UserGroup.objects + queryset = UserGroup.objects.all() serializer_class = UserGroupUpdateMemeberSerializer permission_classes = (IsSuperUser,) diff --git a/apps/users/forms.py b/apps/users/forms.py index 5b45abf6c..f30997c4c 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -6,6 +6,8 @@ from django.utils.translation import gettext_lazy as _ from captcha.fields import CaptchaField from common.utils import validate_ssh_public_key +from orgs.mixins import OrgModelForm +from orgs.utils import get_current_org from .models import User, UserGroup @@ -39,7 +41,7 @@ class UserCheckOtpCodeForm(forms.Form): otp_code = forms.CharField(label=_('MFA code'), max_length=6) -class UserCreateUpdateForm(forms.ModelForm): +class UserCreateUpdateForm(OrgModelForm): role_choices = ((i, n) for i, n in User.ROLE_CHOICES if i != User.ROLE_APP) password = forms.CharField( label=_('Password'), widget=forms.PasswordInput, @@ -54,15 +56,6 @@ class UserCreateUpdateForm(forms.ModelForm): widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), help_text=_('Paste user id_rsa.pub here.') ) - # groups = forms.ModelMultipleChoiceField( - # queryset=UserGroup.objects, required=False, label=_("Groups"), - # widget=forms.SelectMultiple( - # attrs={ - # 'class': 'select2', - # 'data-placeholder': _('Join user groups') - # } - # ) - # ) class Meta: model = User @@ -77,6 +70,12 @@ class UserCreateUpdateForm(forms.ModelForm): } widgets = { 'otp_level': forms.RadioSelect(), + 'groups': forms.SelectMultiple( + attrs={ + 'class': 'select2', + 'data-placeholder': _('Join user groups') + } + ) } def clean_public_key(self): @@ -240,7 +239,7 @@ class UserBulkUpdateForm(forms.ModelForm): required=True, help_text='* required', label=_('Select users'), - queryset = User.objects, + queryset = User.objects.all(), widget=forms.SelectMultiple( attrs={ 'class': 'select2', @@ -279,6 +278,11 @@ class UserBulkUpdateForm(forms.ModelForm): return users +def user_limit_to(): + org = get_current_org() + return {"orgs": org} + + class UserGroupForm(forms.ModelForm): users = forms.ModelMultipleChoiceField( queryset=User.objects.exclude(role=User.ROLE_APP), @@ -290,6 +294,7 @@ class UserGroupForm(forms.ModelForm): } ), required=False, + limit_choices_to=user_limit_to ) def __init__(self, **kwargs): @@ -318,30 +323,12 @@ class UserGroupForm(forms.ModelForm): } -# class UserGroupPrivateAssetPermissionForm(forms.ModelForm): -# def save(self, commit=True): -# self.instance = super(UserGroupPrivateAssetPermissionForm, self)\ -# .save(commit=commit) -# self.instance.user_groups = [self.user_group] -# self.instance.save() -# return self.instance -# -# class Meta: -# model = AssetPermission -# fields = [ -# 'assets', 'asset_groups', 'system_users', 'name', -# ] -# widgets = { -# 'assets': forms.SelectMultiple( -# attrs={'class': 'select2', -# 'data-placeholder': _('Select assets')}), -# 'asset_groups': forms.SelectMultiple( -# attrs={'class': 'select2', -# 'data-placeholder': _('Select asset groups')}), -# 'system_users': forms.SelectMultiple( -# attrs={'class': 'select2', -# 'data-placeholder': _('Select system users')}), -# } +class OrgUserField(forms.ModelMultipleChoiceField): + + def get_limit_choices_to(self): + + return {"orgs"} + class FileForm(forms.Form): diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 172a8fcde..a61d5e10b 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -15,6 +15,7 @@ from django.shortcuts import reverse from common.utils import get_signer, date_expired_default from common.models import Setting +from orgs.mixins import OrgManager from orgs.utils import get_current_org diff --git a/apps/users/serializers.py b/apps/users/serializers.py index 5fbeebe4e..21103c6a8 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -14,7 +14,7 @@ signer = get_signer() class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): groups_display = serializers.SerializerMethodField() - groups = serializers.PrimaryKeyRelatedField(many=True, queryset = UserGroup.objects, required=False) + groups = serializers.PrimaryKeyRelatedField(many=True, queryset = UserGroup.objects.all(), required=False) class Meta: model = User @@ -50,7 +50,7 @@ class UserPKUpdateSerializer(serializers.ModelSerializer): class UserUpdateGroupSerializer(serializers.ModelSerializer): - groups = serializers.PrimaryKeyRelatedField(many=True, queryset = UserGroup.objects) + groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all()) class Meta: model = User @@ -71,7 +71,7 @@ class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): class UserGroupUpdateMemeberSerializer(serializers.ModelSerializer): - users = serializers.PrimaryKeyRelatedField(many=True, queryset = User.objects) + users = serializers.PrimaryKeyRelatedField(many=True, queryset = User.objects.all()) class Meta: model = UserGroup diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 94071924f..7cbf4dfed 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -23,7 +23,7 @@ from django.conf import settings from common.utils import get_object_or_none from common.mixins import DatetimeSearchMixin, AdminUserRequiredMixin -from common.models import Setting +from orgs.utils import get_current_org from ..models import User, LoginLog from ..utils import send_reset_password_mail, check_otp_code, get_login_ip, \ redirect_user_first_login_or_index, get_user_or_tmp_user, \ @@ -365,11 +365,17 @@ class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): user = keyword = "" date_to = date_from = None + def get_allow_users(self): + current_org = get_current_org() + users = current_org.get_org_users().values_list('username', flat=True) + return users + def get_queryset(self): + users = self.get_allow_users() + queryset = super().get_queryset().filter(username__in=users) self.user = self.request.GET.get('user', '') self.keyword = self.request.GET.get("keyword", '') - queryset = super().get_queryset() queryset = queryset.filter( datetime__gt=self.date_from, datetime__lt=self.date_to ) @@ -391,9 +397,7 @@ class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): 'date_to': self.date_to, 'user': self.user, 'keyword': self.keyword, - 'user_list': set( - LoginLog.objects.all().values_list('username', flat=True) - ) + 'user_list': self.get_allow_users(), } kwargs.update(context) return super().get_context_data(**kwargs) \ No newline at end of file From 3e17e942451c2d2133f54ea5ee25ca2a1d761967 Mon Sep 17 00:00:00 2001 From: ibuler Date: Sun, 15 Jul 2018 18:39:11 +0800 Subject: [PATCH 06/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E8=B5=84?= =?UTF-8?q?=E4=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/forms/asset.py | 11 ++++++---- apps/assets/models/asset.py | 22 +++++++------------- apps/assets/models/node.py | 40 +++++++++++++++++++++++++++---------- apps/orgs/mixins.py | 13 +----------- 4 files changed, 45 insertions(+), 41 deletions(-) diff --git a/apps/assets/forms/asset.py b/apps/assets/forms/asset.py index 516ce4afc..39ad7713e 100644 --- a/apps/assets/forms/asset.py +++ b/apps/assets/forms/asset.py @@ -3,14 +3,17 @@ from django import forms from django.utils.translation import gettext_lazy as _ -from ..models import Asset, AdminUser from common.utils import get_logger +from orgs.mixins import OrgModelForm + +from ..models import Asset, AdminUser + logger = get_logger(__file__) __all__ = ['AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm'] -class AssetCreateForm(forms.ModelForm): +class AssetCreateForm(OrgModelForm): class Meta: model = Asset fields = [ @@ -50,7 +53,7 @@ class AssetCreateForm(forms.ModelForm): } -class AssetUpdateForm(forms.ModelForm): +class AssetUpdateForm(OrgModelForm): class Meta: model = Asset fields = [ @@ -90,7 +93,7 @@ class AssetUpdateForm(forms.ModelForm): } -class AssetBulkUpdateForm(forms.ModelForm): +class AssetBulkUpdateForm(OrgModelForm): assets = forms.ModelMultipleChoiceField( required=True, help_text='* required', label=_('Select assets'), queryset=Asset.objects.all(), diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 6de5c2059..073ad3b32 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -71,16 +71,11 @@ class Asset(OrgModelMixin): ) id = models.UUIDField(default=uuid.uuid4, primary_key=True) - ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), - db_index=True) - hostname = models.CharField(max_length=128, unique=True, - verbose_name=_('Hostname')) - protocol = models.CharField(max_length=128, default=SSH_PROTOCOL, - choices=PROTOCOL_CHOICES, - verbose_name=_('Protocol')) + ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True) + hostname = models.CharField(max_length=128, verbose_name=_('Hostname')) + protocol = models.CharField(max_length=128, default=SSH_PROTOCOL, choices=PROTOCOL_CHOICES, verbose_name=_('Protocol')) port = models.IntegerField(default=22, verbose_name=_('Port')) - platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, - default='Linux', verbose_name=_('Platform')) + platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform')) domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL) @@ -94,11 +89,8 @@ class Asset(OrgModelMixin): null=True, verbose_name=_("Admin user")) # Some information - public_ip = models.GenericIPAddressField(max_length=32, blank=True, - null=True, - verbose_name=_('Public IP')) - number = models.CharField(max_length=32, null=True, blank=True, - verbose_name=_('Asset number')) + public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP')) + number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number')) # Collect vendor = models.CharField(max_length=64, null=True, blank=True, @@ -233,7 +225,7 @@ class Asset(OrgModelMixin): return data class Meta: - unique_together = ('ip', 'port') + unique_together = ('org', 'hostname') verbose_name = _("Asset") @classmethod diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 74156daa6..7b5b9dd48 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -7,7 +7,8 @@ from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from orgs.mixins import OrgModelMixin -from common.utils import with_cache +from orgs.utils import get_current_org, set_current_org +from orgs.models import Organization __all__ = ['Node'] @@ -119,7 +120,11 @@ class Node(OrgModelMixin): return self.get_all_assets().valid() def is_root(self): - return self.key == '0' + root = self.__class__.root() + if self == root: + return True + else: + return False @property def parent(self): @@ -148,8 +153,8 @@ class Node(OrgModelMixin): def get_ancestor(self, with_self=False): if self.is_root(): - ancestor = self.__class__.objects.filter(key='0') - return ancestor + root = self.__class__.root() + return [root] _key = self.key.split(':') if not with_self: @@ -164,10 +169,25 @@ class Node(OrgModelMixin): return ancestor @classmethod - def root(cls): - obj, created = cls.objects.get_or_create( - key='0', defaults={"key": '0', 'value': "ROOT"} - ) - print(obj) - return obj + def create_root_node(cls): + with transaction.atomic(): + org = get_current_org() + set_current_org(Organization.root()) + org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$') + org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) + max_value = max([int(k) for k in org_nodes_roots_keys]) if org_nodes_roots_keys else 0 + set_current_org(org) + root = cls.objects.create(key=max_value+1, value=org.name) + return root + + @classmethod + def root(cls): + root = cls.objects.filter(key__regex=r'^[0-9]+$') + print("GET ROOT NODE") + if len(root) == 1: + return root.get() + else: + return cls.create_root_node() + + diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index d9f55d301..d7d4b0d23 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -18,28 +18,17 @@ __all__ = [ class OrgManager(models.Manager): - def __init__(self, *args, **kwargs): - print("INit manager") - super().__init__(*args, **kwargs) - def get_queryset(self): - print("GET CURR") current_org = get_current_org() kwargs = {} - print("Get queryset ") - print(current_org) - - print(self.model) if not current_org: - pass + kwargs['id'] = None elif current_org.is_real(): kwargs['org'] = current_org elif current_org.is_default(): kwargs['org'] = None queryset = super().get_queryset().filter(**kwargs) - print(kwargs) - print(queryset) return queryset def all(self): From 4d1da5687256cb07b6d6cfe205fac72b8e33d955 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 16 Jul 2018 14:47:06 +0800 Subject: [PATCH 07/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9asset=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/node.py | 6 ++---- apps/assets/models/node.py | 12 ++++++++---- apps/assets/templates/assets/asset_list.html | 2 ++ apps/assets/views/asset.py | 2 +- apps/orgs/mixins.py | 12 ++++++++---- apps/orgs/utils.py | 13 ++++++------- apps/users/models/group.py | 3 ++- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index e5ace021e..c159798f3 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -30,10 +30,8 @@ from .. import serializers logger = get_logger(__file__) __all__ = [ - 'NodeViewSet', 'NodeChildrenApi', - 'NodeAssetsApi', - 'NodeAddAssetsApi', 'NodeRemoveAssetsApi', - 'NodeReplaceAssetsApi', + 'NodeViewSet', 'NodeChildrenApi', 'NodeAssetsApi', + 'NodeAddAssetsApi', 'NodeRemoveAssetsApi', 'NodeReplaceAssetsApi', 'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi', 'TestNodeConnectiveApi' ] diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 7b5b9dd48..b75bea863 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -64,6 +64,9 @@ class Node(OrgModelMixin): def create_child(self, value): with transaction.atomic(): child_key = self.get_next_child_key() + print("Create child") + print(self.key) + print(child_key) child = self.__class__.objects.create(key=child_key, value=value) return child @@ -120,15 +123,16 @@ class Node(OrgModelMixin): return self.get_all_assets().valid() def is_root(self): - root = self.__class__.root() - if self == root: + print(type(self.key)) + print(self.key) + if self.key.isdigit(): return True else: return False @property def parent(self): - if self.key == "0" or not self.key.startswith("0"): + if self.is_root(): return self.__class__.root() parent_key = ":".join(self.key.split(":")[:-1]) try: @@ -155,7 +159,7 @@ class Node(OrgModelMixin): if self.is_root(): root = self.__class__.root() return [root] - + print(self.key) _key = self.key.split(':') if not with_self: _key.pop() diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index b5e53aaba..01c325946 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -411,6 +411,8 @@ function initTree() { zNodes = data; $.fn.zTree.init($("#assetTree"), setting, zNodes); zTree = $.fn.zTree.getZTreeObj("assetTree"); + var root = zTree.getNodes()[0]; + zTree.expandNode(root); rMenu = $("#rMenu"); selectQueryNode(); }); diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py index 273323193..e6d19b75f 100644 --- a/apps/assets/views/asset.py +++ b/apps/assets/views/asset.py @@ -44,7 +44,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView): template_name = 'assets/asset_list.html' def get_context_data(self, **kwargs): - Node.root() + print(Node.root().name) context = { 'app': _('Assets'), 'action': _('Asset list'), diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index d7d4b0d23..bd5f7f461 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -7,7 +7,7 @@ from django.contrib.auth import get_user_model from django.forms import ModelForm from common.utils import get_logger -from .utils import get_current_org, get_model_by_db_table, set_current_org +from .utils import get_current_org, set_current_org logger = get_logger(__file__) @@ -22,23 +22,27 @@ class OrgManager(models.Manager): current_org = get_current_org() kwargs = {} + print(">>>>>>>>>> Get query set") if not current_org: kwargs['id'] = None elif current_org.is_real(): kwargs['org'] = current_org elif current_org.is_default(): kwargs['org'] = None - queryset = super().get_queryset().filter(**kwargs) + queryset = super(OrgManager, self).get_queryset() + queryset = queryset.filter(**kwargs) + # print(kwargs) + print(queryset.query) return queryset def all(self): current_org = get_current_org() if not current_org: - msg = 'You should `objects.set_current_org(org).all()` then run it' + msg = 'You can `objects.set_current_org(org).all()` then run it' warnings.warn(msg) return self else: - return super().all() + return super(OrgManager, self).all() def set_current_org(self, org): set_current_org(org) diff --git a/apps/orgs/utils.py b/apps/orgs/utils.py index ee82fc01e..9f6098c77 100644 --- a/apps/orgs/utils.py +++ b/apps/orgs/utils.py @@ -35,10 +35,9 @@ def set_current_org(org): setattr(_thread_locals, 'current_org', org) -def get_model_by_db_table(db_table): - for model in apps.get_models(): - if model._meta.db_table == db_table: - return model - else: - # here you can do fallback logic if no model with db_table found - raise ValueError('No model found with db_table {}!'.format(db_table)) +def set_to_default_org(): + set_current_org(Organization.default()) + + +def set_to_root_org(): + set_current_org(Organization.root()) diff --git a/apps/users/models/group.py b/apps/users/models/group.py index 7730c12d0..77106e80e 100644 --- a/apps/users/models/group.py +++ b/apps/users/models/group.py @@ -11,7 +11,7 @@ __all__ = ['UserGroup'] class UserGroup(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) - name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) + name = models.CharField(max_length=128, verbose_name=_('Name')) comment = models.TextField(blank=True, verbose_name=_('Comment')) date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date created')) @@ -22,6 +22,7 @@ class UserGroup(OrgModelMixin): class Meta: ordering = ['name'] + unique_together = ['org', 'name'] verbose_name = _("User group") @classmethod From 790652ff4ddef8e5bd08b78246981157756645f2 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 17 Jul 2018 17:09:36 +0800 Subject: [PATCH 08/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E5=B0=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/forms/domain.py | 3 ++- apps/assets/models/base.py | 2 +- apps/assets/models/domain.py | 4 +++- apps/assets/models/node.py | 7 ------- apps/assets/models/user.py | 2 ++ 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/assets/forms/domain.py b/apps/assets/forms/domain.py index ec3af8f2e..1b005ec2f 100644 --- a/apps/assets/forms/domain.py +++ b/apps/assets/forms/domain.py @@ -3,6 +3,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ +from orgs.mixins import OrgModelForm from ..models import Domain, Asset, Gateway from .user import PasswordAndKeyAuthForm @@ -34,7 +35,7 @@ class DomainForm(forms.ModelForm): return instance -class GatewayForm(PasswordAndKeyAuthForm): +class GatewayForm(PasswordAndKeyAuthForm, OrgModelForm): def save(self, commit=True): # Because we define custom field, so we need rewrite :method: `save` diff --git a/apps/assets/models/base.py b/apps/assets/models/base.py index 3698f3a05..b03010905 100644 --- a/apps/assets/models/base.py +++ b/apps/assets/models/base.py @@ -19,7 +19,7 @@ signer = get_signer() class AssetUser(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) - name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) + name = models.CharField(max_length=128, verbose_name=_('Name')) username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric]) _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) _private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ]) diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py index 67283bd80..8a25cb69e 100644 --- a/apps/assets/models/domain.py +++ b/apps/assets/models/domain.py @@ -34,7 +34,7 @@ class Domain(OrgModelMixin): return random.choice(self.gateways) -class Gateway(AssetUser): +class Gateway(AssetUser, OrgModelMixin): SSH_PROTOCOL = 'ssh' RDP_PROTOCOL = 'rdp' PROTOCOL_CHOICES = ( @@ -51,3 +51,5 @@ class Gateway(AssetUser): def __str__(self): return self.name + class Meta: + unique_together = ['name', 'org'] diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index b75bea863..34fc20680 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -64,9 +64,6 @@ class Node(OrgModelMixin): def create_child(self, value): with transaction.atomic(): child_key = self.get_next_child_key() - print("Create child") - print(self.key) - print(child_key) child = self.__class__.objects.create(key=child_key, value=value) return child @@ -123,8 +120,6 @@ class Node(OrgModelMixin): return self.get_all_assets().valid() def is_root(self): - print(type(self.key)) - print(self.key) if self.key.isdigit(): return True else: @@ -159,7 +154,6 @@ class Node(OrgModelMixin): if self.is_root(): root = self.__class__.root() return [root] - print(self.key) _key = self.key.split(':') if not with_self: _key.pop() @@ -187,7 +181,6 @@ class Node(OrgModelMixin): @classmethod def root(cls): root = cls.objects.filter(key__regex=r'^[0-9]+$') - print("GET ROOT NODE") if len(root) == 1: return root.get() else: diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index 5faca5da8..ffe5cc44d 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -69,6 +69,7 @@ class AdminUser(AssetUser): class Meta: ordering = ['name'] + unique_together = ['name', 'org'] verbose_name = _("Admin user") @classmethod @@ -156,6 +157,7 @@ class SystemUser(AssetUser): class Meta: ordering = ['name'] + unique_together = ['name', 'org'] verbose_name = _("System user") @classmethod From fd5f562cbf59f26ee7a705f6a25f737b30d182c2 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 18 Jul 2018 12:57:08 +0800 Subject: [PATCH 09/63] [Update] stash it --- apps/orgs/mixins.py | 1 - apps/perms/forms.py | 13 ++++++++++++- apps/users/forms.py | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index bd5f7f461..5bc465ff2 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -79,7 +79,6 @@ class OrgModelForm(ModelForm): for name, field in self.fields.items(): if not hasattr(field, 'queryset'): continue - print(field) model = field.queryset.model field.queryset = model.objects.all() diff --git a/apps/perms/forms.py b/apps/perms/forms.py index c418160dd..6c63982ae 100644 --- a/apps/perms/forms.py +++ b/apps/perms/forms.py @@ -4,11 +4,13 @@ from __future__ import absolute_import, unicode_literals from django import forms from django.utils.translation import ugettext_lazy as _ +from orgs.mixins import OrgModelForm +from orgs.utils import get_current_org from .hands import User from .models import AssetPermission -class AssetPermissionForm(forms.ModelForm): +class AssetPermissionForm(OrgModelForm): users = forms.ModelMultipleChoiceField( queryset=User.objects.exclude(role=User.ROLE_APP), label=_("User"), @@ -21,6 +23,15 @@ class AssetPermissionForm(forms.ModelForm): required=False, ) + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if 'initial' not in kwargs: + return + users_field = self.fields.get('users') + if hasattr(users_field, 'queryset'): + current_org = get_current_org() + users_field.queryset = User.objects.filter(orgs=current_org) + class Meta: model = AssetPermission exclude = ( diff --git a/apps/users/forms.py b/apps/users/forms.py index f30997c4c..220509dd7 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -306,6 +306,12 @@ class UserGroupForm(forms.ModelForm): }) kwargs['initial'] = initial super().__init__(**kwargs) + if 'initial' not in kwargs: + return + users_field = self.fields.get('users') + if hasattr(users_field, 'queryset'): + current_org = get_current_org() + users_field.queryset = User.objects.filter(orgs=current_org) def save(self, commit=True): group = super().save(commit=commit) From c24f1a05176c5376b1b61f7c9593ddfb59e5bfe8 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 19 Jul 2018 19:24:29 +0800 Subject: [PATCH 10/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=BA=A6?= =?UTF-8?q?=E6=9D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/node.py | 12 ++++++++++-- apps/assets/models/asset.py | 2 +- apps/assets/models/domain.py | 2 +- apps/assets/models/label.py | 2 +- apps/assets/models/node.py | 7 ++++--- apps/assets/models/user.py | 4 ++-- apps/assets/serializers/node.py | 2 ++ apps/jumpserver/settings.py | 8 ++++---- apps/orgs/mixins.py | 21 ++++++++++++--------- apps/orgs/models.py | 2 +- apps/users/models/group.py | 2 +- 11 files changed, 39 insertions(+), 25 deletions(-) diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index c159798f3..178642170 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from rest_framework import generics, mixins +from rest_framework import generics, mixins, viewsets from rest_framework.serializers import ValidationError from rest_framework.views import APIView from rest_framework.response import Response @@ -22,6 +22,7 @@ from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 from common.utils import get_logger, get_object_or_none +from orgs.utils import get_current_org from ..hands import IsSuperUser from ..models import Node from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util @@ -37,11 +38,18 @@ __all__ = [ ] -class NodeViewSet(BulkModelViewSet): +class NodeViewSet(viewsets.ModelViewSet): queryset = Node.objects.all() permission_classes = (IsSuperUser,) serializer_class = serializers.NodeSerializer + def get_queryset(self): + # queryset = super(NodeViewSet, self).get_queryset() + print("API GET QUWRYSET") + # print(get_current_org()) + # print(queryset) + return Node.objects.all() + def perform_create(self, serializer): child_key = Node.root().get_next_child_key() serializer.validated_data["key"] = child_key diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 073ad3b32..cfe73842f 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -225,7 +225,7 @@ class Asset(OrgModelMixin): return data class Meta: - unique_together = ('org', 'hostname') + unique_together = [('org', 'hostname'),] verbose_name = _("Asset") @classmethod diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py index 8a25cb69e..376c032d7 100644 --- a/apps/assets/models/domain.py +++ b/apps/assets/models/domain.py @@ -52,4 +52,4 @@ class Gateway(AssetUser, OrgModelMixin): return self.name class Meta: - unique_together = ['name', 'org'] + unique_together = [('name', 'org')] diff --git a/apps/assets/models/label.py b/apps/assets/models/label.py index 990a71ca8..6a2cefde9 100644 --- a/apps/assets/models/label.py +++ b/apps/assets/models/label.py @@ -34,4 +34,4 @@ class Label(models.Model): class Meta: db_table = "assets_label" - unique_together = ('name', 'value') + unique_together = [('name', 'value'),] diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 34fc20680..43f8d480c 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -23,7 +23,8 @@ class Node(OrgModelMixin): is_node = True def __str__(self): - return self.full_value + return self.value + # return self.full_value def __eq__(self, other): return self.key == other.key @@ -181,8 +182,8 @@ class Node(OrgModelMixin): @classmethod def root(cls): root = cls.objects.filter(key__regex=r'^[0-9]+$') - if len(root) == 1: - return root.get() + if root: + return root[0] else: return cls.create_root_node() diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index ffe5cc44d..8fb415a85 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -69,7 +69,7 @@ class AdminUser(AssetUser): class Meta: ordering = ['name'] - unique_together = ['name', 'org'] + unique_together = [('name', 'org'),] verbose_name = _("Admin user") @classmethod @@ -157,7 +157,7 @@ class SystemUser(AssetUser): class Meta: ordering = ['name'] - unique_together = ['name', 'org'] + unique_together = [('name', 'org'),] verbose_name = _("System user") @classmethod diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py index 0352dfecc..65eb16dda 100644 --- a/apps/assets/serializers/node.py +++ b/apps/assets/serializers/node.py @@ -31,6 +31,7 @@ class NodeGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer): @staticmethod def get_assets_amount(obj): + return 1 return len(obj.assets_granted) @staticmethod @@ -39,6 +40,7 @@ class NodeGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer): @staticmethod def get_parent(obj): + return '0' return obj.parent.id diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index de79cd97b..7be5b7613 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -234,10 +234,10 @@ LOGGING = { 'handlers': ['console', 'ansible_logs'], 'level': "INFO", }, - # 'django.db': { - # 'handlers': ['console', 'file'], - # 'level': 'DEBUG' - # } + 'django.db': { + 'handlers': ['console', 'file'], + 'level': 'DEBUG' + } } } diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index 5bc465ff2..4d8097d99 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -8,6 +8,7 @@ from django.forms import ModelForm from common.utils import get_logger from .utils import get_current_org, set_current_org +from .models import Organization logger = get_logger(__file__) @@ -32,19 +33,21 @@ class OrgManager(models.Manager): queryset = super(OrgManager, self).get_queryset() queryset = queryset.filter(**kwargs) # print(kwargs) - print(queryset.query) + # print(queryset.query) return queryset - def all(self): - current_org = get_current_org() - if not current_org: - msg = 'You can `objects.set_current_org(org).all()` then run it' - warnings.warn(msg) - return self - else: - return super(OrgManager, self).all() + # def all(self): + # current_org = get_current_org() + # if not current_org: + # msg = 'You can `objects.set_current_org(org).all()` then run it' + # warnings.warn(msg) + # return self + # else: + # return super(OrgManager, self).all() def set_current_org(self, org): + if isinstance(org, str): + org = Organization.objects.get(name=org) set_current_org(org) return self diff --git a/apps/orgs/models.py b/apps/orgs/models.py index afdf2ef10..a71f3a897 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _ class Organization(models.Model): id = models.UUIDField(default=uuid.uuid4, primary_key=True) - name = models.CharField(max_length=128, verbose_name=_("Name")) + name = models.CharField(max_length=128, unique=True, verbose_name=_("Name")) users = models.ManyToManyField('users.User', related_name='orgs', blank=True) admins = models.ManyToManyField('users.User', related_name='admin_orgs', blank=True) created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by')) diff --git a/apps/users/models/group.py b/apps/users/models/group.py index 77106e80e..1a8ccac2b 100644 --- a/apps/users/models/group.py +++ b/apps/users/models/group.py @@ -22,7 +22,7 @@ class UserGroup(OrgModelMixin): class Meta: ordering = ['name'] - unique_together = ['org', 'name'] + unique_together = [('org', 'name'),] verbose_name = _("User group") @classmethod From b6523da60378088416741d4202829ca55aaf5db6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 20 Jul 2018 10:54:16 +0800 Subject: [PATCH 11/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E5=A4=96?= =?UTF-8?q?=E9=94=AE=E4=B8=BAorg=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/asset.py | 2 +- apps/assets/models/domain.py | 4 ++-- apps/assets/models/label.py | 2 +- apps/assets/models/user.py | 4 ++-- apps/orgs/mixins.py | 8 ++++---- apps/users/models/group.py | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index cfe73842f..c44239f55 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -225,7 +225,7 @@ class Asset(OrgModelMixin): return data class Meta: - unique_together = [('org', 'hostname'),] + unique_together = [('org_id', 'hostname')] verbose_name = _("Asset") @classmethod diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py index 376c032d7..c0a4ee638 100644 --- a/apps/assets/models/domain.py +++ b/apps/assets/models/domain.py @@ -34,7 +34,7 @@ class Domain(OrgModelMixin): return random.choice(self.gateways) -class Gateway(AssetUser, OrgModelMixin): +class Gateway(AssetUser): SSH_PROTOCOL = 'ssh' RDP_PROTOCOL = 'rdp' PROTOCOL_CHOICES = ( @@ -52,4 +52,4 @@ class Gateway(AssetUser, OrgModelMixin): return self.name class Meta: - unique_together = [('name', 'org')] + unique_together = [('name', 'org_id')] diff --git a/apps/assets/models/label.py b/apps/assets/models/label.py index 6a2cefde9..41f0f8724 100644 --- a/apps/assets/models/label.py +++ b/apps/assets/models/label.py @@ -34,4 +34,4 @@ class Label(models.Model): class Meta: db_table = "assets_label" - unique_together = [('name', 'value'),] + unique_together = [('name', 'value')] diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index 8fb415a85..be7a98ebf 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -69,7 +69,7 @@ class AdminUser(AssetUser): class Meta: ordering = ['name'] - unique_together = [('name', 'org'),] + unique_together = [('name', 'org_id')] verbose_name = _("Admin user") @classmethod @@ -157,7 +157,7 @@ class SystemUser(AssetUser): class Meta: ordering = ['name'] - unique_together = [('name', 'org'),] + unique_together = [('name', 'org_id')] verbose_name = _("System user") @classmethod diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index 4d8097d99..194e1ef37 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -27,9 +27,9 @@ class OrgManager(models.Manager): if not current_org: kwargs['id'] = None elif current_org.is_real(): - kwargs['org'] = current_org + kwargs['org_id'] = current_org.id elif current_org.is_default(): - kwargs['org'] = None + kwargs['org_id'] = None queryset = super(OrgManager, self).get_queryset() queryset = queryset.filter(**kwargs) # print(kwargs) @@ -53,13 +53,13 @@ class OrgManager(models.Manager): class OrgModelMixin(models.Model): - org = models.ForeignKey('orgs.Organization', on_delete=models.PROTECT, null=True) + org_id = models.CharField(max_length=36, null=True) objects = OrgManager() def save(self, *args, **kwargs): current_org = get_current_org() if current_org and current_org.is_real(): - self.org = current_org + self.org_id = current_org.id return super(OrgModelMixin, self).save(*args, **kwargs) class Meta: diff --git a/apps/users/models/group.py b/apps/users/models/group.py index 1a8ccac2b..e3d310d97 100644 --- a/apps/users/models/group.py +++ b/apps/users/models/group.py @@ -22,7 +22,7 @@ class UserGroup(OrgModelMixin): class Meta: ordering = ['name'] - unique_together = [('org', 'name'),] + unique_together = [('org_id', 'name')] verbose_name = _("User group") @classmethod From bbe4080008ba1fb18749752daaaa012d0d5591f7 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 20 Jul 2018 12:15:45 +0800 Subject: [PATCH 12/63] =?UTF-8?q?[Update]=20=E5=88=A0=E6=8E=89Premiddlewar?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/settings.py | 9 ++++----- apps/orgs/middleware.py | 11 ----------- apps/orgs/mixins.py | 19 ++++++++++--------- apps/orgs/utils.py | 3 ++- apps/users/api.py | 3 ++- 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 7be5b7613..77a37839d 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -78,7 +78,6 @@ INSTALLED_APPS = [ ] MIDDLEWARE = [ - 'orgs.middleware.OrgPreMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', @@ -234,10 +233,10 @@ LOGGING = { 'handlers': ['console', 'ansible_logs'], 'level': "INFO", }, - 'django.db': { - 'handlers': ['console', 'file'], - 'level': 'DEBUG' - } + # 'django.db': { + # 'handlers': ['console', 'file'], + # 'level': 'DEBUG' + # } } } diff --git a/apps/orgs/middleware.py b/apps/orgs/middleware.py index 102178575..04a2d6f7a 100644 --- a/apps/orgs/middleware.py +++ b/apps/orgs/middleware.py @@ -2,17 +2,6 @@ # from .utils import get_org_from_request, set_current_org -from .models import Organization - - -class OrgPreMiddleware: - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - set_current_org(Organization.root()) - response = self.get_response(request) - return response class OrgMiddleware: diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index 194e1ef37..a458d54c3 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -23,7 +23,8 @@ class OrgManager(models.Manager): current_org = get_current_org() kwargs = {} - print(">>>>>>>>>> Get query set") + # print(">>>>>>>>>> Get query set") + # print(current_org) if not current_org: kwargs['id'] = None elif current_org.is_real(): @@ -36,14 +37,14 @@ class OrgManager(models.Manager): # print(queryset.query) return queryset - # def all(self): - # current_org = get_current_org() - # if not current_org: - # msg = 'You can `objects.set_current_org(org).all()` then run it' - # warnings.warn(msg) - # return self - # else: - # return super(OrgManager, self).all() + def all(self): + current_org = get_current_org() + if not current_org: + msg = 'You can `objects.set_current_org(org).all()` then run it' + warnings.warn(msg) + return self + else: + return super(OrgManager, self).all() def set_current_org(self, org): if isinstance(org, str): diff --git a/apps/orgs/utils.py b/apps/orgs/utils.py index 9f6098c77..78e3cad1a 100644 --- a/apps/orgs/utils.py +++ b/apps/orgs/utils.py @@ -24,7 +24,8 @@ def get_current_request(): def get_current_org(): - return getattr(_thread_locals, 'current_org', None) + org = getattr(_thread_locals, 'current_org', None) + return org def get_current_user(): diff --git a/apps/users/api.py b/apps/users/api.py index 28e2e95d8..67d7627f3 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -106,12 +106,13 @@ class UserUpdatePKApi(generics.UpdateAPIView): user.save() -class UserGroupViewSet(IDInFilterMixin, OrgViewGenericMixin, BulkModelViewSet): +class UserGroupViewSet(BulkModelViewSet): queryset = UserGroup.objects.all() serializer_class = UserGroupSerializer permission_classes = (IsSuperUser,) + class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView): queryset = UserGroup.objects.all() serializer_class = UserGroupUpdateMemeberSerializer From b5f6f80ae6b2c36d219d75a2b7948cd85a4e2102 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 20 Jul 2018 13:25:50 +0800 Subject: [PATCH 13/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9Node?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/node.py | 2 -- apps/orgs/mixins.py | 12 +++++++++--- apps/orgs/models.py | 2 +- apps/users/api.py | 7 ++----- apps/users/forms.py | 6 ++---- apps/users/views/login.py | 7 ++++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py index 65eb16dda..0352dfecc 100644 --- a/apps/assets/serializers/node.py +++ b/apps/assets/serializers/node.py @@ -31,7 +31,6 @@ class NodeGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer): @staticmethod def get_assets_amount(obj): - return 1 return len(obj.assets_granted) @staticmethod @@ -40,7 +39,6 @@ class NodeGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer): @staticmethod def get_parent(obj): - return '0' return obj.parent.id diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index a458d54c3..ec77c4cfd 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -3,7 +3,6 @@ from django.db import models from django.shortcuts import redirect import warnings -from django.contrib.auth import get_user_model from django.forms import ModelForm from common.utils import get_logger @@ -12,6 +11,9 @@ from .models import Organization logger = get_logger(__file__) +from threading import local + +tl = local() __all__ = [ 'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm' @@ -19,12 +21,15 @@ __all__ = [ class OrgManager(models.Manager): + def get_queryset(self): current_org = get_current_org() kwargs = {} + if not hasattr(tl, 'times'): + tl.times = 0 - # print(">>>>>>>>>> Get query set") - # print(current_org) + print("[{}]>>>>>>>>>> Get query set".format(tl.times)) + print(current_org) if not current_org: kwargs['id'] = None elif current_org.is_real(): @@ -35,6 +40,7 @@ class OrgManager(models.Manager): queryset = queryset.filter(**kwargs) # print(kwargs) # print(queryset.query) + tl.times += 1 return queryset def all(self): diff --git a/apps/orgs/models.py b/apps/orgs/models.py index a71f3a897..80cb56e5b 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -55,7 +55,7 @@ class Organization(models.Model): def get_org_users(self): from users.models import User if self.is_default(): - return User.objects.all() + return User.objects.filter(orgs__isnull=True) else: return self.users.all() diff --git a/apps/users/api.py b/apps/users/api.py index 67d7627f3..12f45dff0 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -38,10 +38,8 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet): def get_queryset(self): queryset = super().get_queryset() current_org = get_current_org() - if current_org.is_real(): - queryset = queryset.filter(orgs=current_org) - elif current_org.is_default(): - queryset = queryset.filter(orgs=None) + org_users = current_org.get_org_users().values_list('id', flat=True) + queryset = queryset.filter(id__in=org_users) return queryset def get_permissions(self): @@ -112,7 +110,6 @@ class UserGroupViewSet(BulkModelViewSet): permission_classes = (IsSuperUser,) - class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView): queryset = UserGroup.objects.all() serializer_class = UserGroupUpdateMemeberSerializer diff --git a/apps/users/forms.py b/apps/users/forms.py index 220509dd7..82d5ba446 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -301,9 +301,7 @@ class UserGroupForm(forms.ModelForm): instance = kwargs.get('instance') if instance: initial = kwargs.get('initial', {}) - initial.update({ - 'users': instance.users.all(), - }) + initial.update({'users': instance.users.all()}) kwargs['initial'] = initial super().__init__(**kwargs) if 'initial' not in kwargs: @@ -311,7 +309,7 @@ class UserGroupForm(forms.ModelForm): users_field = self.fields.get('users') if hasattr(users_field, 'queryset'): current_org = get_current_org() - users_field.queryset = User.objects.filter(orgs=current_org) + users_field.queryset = current_org.get_org_users() def save(self, commit=True): group = super().save(commit=commit) diff --git a/apps/users/views/login.py b/apps/users/views/login.py index 7cbf4dfed..569314c99 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -365,13 +365,14 @@ class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): user = keyword = "" date_to = date_from = None - def get_allow_users(self): + @staticmethod + def get_org_users(): current_org = get_current_org() users = current_org.get_org_users().values_list('username', flat=True) return users def get_queryset(self): - users = self.get_allow_users() + users = self.get_org_users() queryset = super().get_queryset().filter(username__in=users) self.user = self.request.GET.get('user', '') self.keyword = self.request.GET.get("keyword", '') @@ -397,7 +398,7 @@ class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): 'date_to': self.date_to, 'user': self.user, 'keyword': self.keyword, - 'user_list': self.get_allow_users(), + 'user_list': self.get_org_users(), } kwargs.update(context) return super().get_context_data(**kwargs) \ No newline at end of file From e3aa18ff2d689fc8d15b8e5b991f9887cf67a2d6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 20 Jul 2018 17:49:47 +0800 Subject: [PATCH 14/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9get=5Fcurren?= =?UTF-8?q?t=5Forg=20=E4=B8=BA=20proxy=E5=AF=B9=E8=B1=A1=20current=5Forg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/node.py | 1 - apps/assets/models/node.py | 7 +- apps/assets/templates/assets/asset_list.html | 4 +- apps/assets/views/asset.py | 2 +- apps/common/utils.py | 120 +++++++++++++++++++ apps/jumpserver/views.py | 2 +- apps/orgs/context_processor.py | 4 +- apps/orgs/mixins.py | 15 ++- apps/orgs/models.py | 11 +- apps/orgs/utils.py | 15 ++- apps/perms/forms.py | 3 +- apps/templates/_header_bar.html | 2 +- apps/templates/_left_side_bar.html | 2 +- apps/users/api.py | 3 +- apps/users/forms.py | 6 +- apps/users/models/user.py | 3 +- apps/users/views/login.py | 3 +- apps/users/views/user.py | 2 +- 18 files changed, 171 insertions(+), 34 deletions(-) diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index 178642170..30887ff22 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -22,7 +22,6 @@ from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 from common.utils import get_logger, get_object_or_none -from orgs.utils import get_current_org from ..hands import IsSuperUser from ..models import Node from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 43f8d480c..b1aa5cb3d 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -7,7 +7,7 @@ from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from orgs.mixins import OrgModelMixin -from orgs.utils import get_current_org, set_current_org +from orgs.utils import current_org, set_current_org from orgs.models import Organization __all__ = ['Node'] @@ -170,13 +170,12 @@ class Node(OrgModelMixin): @classmethod def create_root_node(cls): with transaction.atomic(): - org = get_current_org() set_current_org(Organization.root()) org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$') org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) max_value = max([int(k) for k in org_nodes_roots_keys]) if org_nodes_roots_keys else 0 - set_current_org(org) - root = cls.objects.create(key=max_value+1, value=org.name) + set_current_org(current_org) + root = cls.objects.create(key=max_value+1, value=current_org.name) return root @classmethod diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index 01c325946..8e9f9d250 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -721,9 +721,7 @@ $(document).ready(function(){ return } - var data = { - 'assets': assets_selected - }; + var data = {'assets': assets_selected}; var success = function () { asset_table2.selected = []; asset_table2.ajax.reload() diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py index e6d19b75f..ff9462503 100644 --- a/apps/assets/views/asset.py +++ b/apps/assets/views/asset.py @@ -211,7 +211,7 @@ class AssetExportView(View): fields = [ field for field in Asset._meta.fields if field.name not in [ - 'date_created' + 'date_created', 'org_id' ] ] filename = 'assets-{}.csv'.format( diff --git a/apps/common/utils.py b/apps/common/utils.py index deaeb5280..7d4b9af64 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -17,6 +17,7 @@ import threading from io import StringIO import uuid from functools import wraps +import copy import paramiko import sshpubkeys @@ -410,3 +411,122 @@ def with_cache(func): cache[key] = res return res return wrapper + + +class LocalProxy(object): + + """ + Copy from werkzeug.local.LocalProxy + """ + __slots__ = ('__local', '__dict__', '__name__', '__wrapped__') + + def __init__(self, local, name=None): + object.__setattr__(self, '_LocalProxy__local', local) + object.__setattr__(self, '__name__', name) + if callable(local) and not hasattr(local, '__release_local__'): + # "local" is a callable that is not an instance of Local or + # LocalManager: mark it as a wrapped function. + object.__setattr__(self, '__wrapped__', local) + + def _get_current_object(self): + """Return the current object. This is useful if you want the real + object behind the proxy at a time for performance reasons or because + you want to pass the object into a different context. + """ + if not hasattr(self.__local, '__release_local__'): + return self.__local() + try: + return getattr(self.__local, self.__name__) + except AttributeError: + raise RuntimeError('no object bound to %s' % self.__name__) + + @property + def __dict__(self): + try: + return self._get_current_object().__dict__ + except RuntimeError: + raise AttributeError('__dict__') + + def __repr__(self): + try: + obj = self._get_current_object() + except RuntimeError: + return '<%s unbound>' % self.__class__.__name__ + return repr(obj) + + def __bool__(self): + try: + return bool(self._get_current_object()) + except RuntimeError: + return False + + def __dir__(self): + try: + return dir(self._get_current_object()) + except RuntimeError: + return [] + + def __getattr__(self, name): + if name == '__members__': + return dir(self._get_current_object()) + return getattr(self._get_current_object(), name) + + def __setitem__(self, key, value): + self._get_current_object()[key] = value + + def __delitem__(self, key): + del self._get_current_object()[key] + + __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v) + __delattr__ = lambda x, n: delattr(x._get_current_object(), n) + __str__ = lambda x: str(x._get_current_object()) + __lt__ = lambda x, o: x._get_current_object() < o + __le__ = lambda x, o: x._get_current_object() <= o + __eq__ = lambda x, o: x._get_current_object() == o + __ne__ = lambda x, o: x._get_current_object() != o + __gt__ = lambda x, o: x._get_current_object() > o + __ge__ = lambda x, o: x._get_current_object() >= o + __cmp__ = lambda x, o: cmp(x._get_current_object(), o) # noqa + __hash__ = lambda x: hash(x._get_current_object()) + __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw) + __len__ = lambda x: len(x._get_current_object()) + __getitem__ = lambda x, i: x._get_current_object()[i] + __iter__ = lambda x: iter(x._get_current_object()) + __contains__ = lambda x, i: i in x._get_current_object() + __add__ = lambda x, o: x._get_current_object() + o + __sub__ = lambda x, o: x._get_current_object() - o + __mul__ = lambda x, o: x._get_current_object() * o + __floordiv__ = lambda x, o: x._get_current_object() // o + __mod__ = lambda x, o: x._get_current_object() % o + __divmod__ = lambda x, o: x._get_current_object().__divmod__(o) + __pow__ = lambda x, o: x._get_current_object() ** o + __lshift__ = lambda x, o: x._get_current_object() << o + __rshift__ = lambda x, o: x._get_current_object() >> o + __and__ = lambda x, o: x._get_current_object() & o + __xor__ = lambda x, o: x._get_current_object() ^ o + __or__ = lambda x, o: x._get_current_object() | o + __div__ = lambda x, o: x._get_current_object().__div__(o) + __truediv__ = lambda x, o: x._get_current_object().__truediv__(o) + __neg__ = lambda x: -(x._get_current_object()) + __pos__ = lambda x: +(x._get_current_object()) + __abs__ = lambda x: abs(x._get_current_object()) + __invert__ = lambda x: ~(x._get_current_object()) + __complex__ = lambda x: complex(x._get_current_object()) + __int__ = lambda x: int(x._get_current_object()) + __float__ = lambda x: float(x._get_current_object()) + __oct__ = lambda x: oct(x._get_current_object()) + __hex__ = lambda x: hex(x._get_current_object()) + __index__ = lambda x: x._get_current_object().__index__() + __coerce__ = lambda x, o: x._get_current_object().__coerce__(x, o) + __enter__ = lambda x: x._get_current_object().__enter__() + __exit__ = lambda x, *a, **kw: x._get_current_object().__exit__(*a, **kw) + __radd__ = lambda x, o: o + x._get_current_object() + __rsub__ = lambda x, o: o - x._get_current_object() + __rmul__ = lambda x, o: o * x._get_current_object() + __rdiv__ = lambda x, o: o / x._get_current_object() + __rtruediv__ = __rdiv__ + __rfloordiv__ = lambda x, o: o // x._get_current_object() + __rmod__ = lambda x, o: o % x._get_current_object() + __rdivmod__ = lambda x, o: x._get_current_object().__rdivmod__(o) + __copy__ = lambda x: copy.copy(x._get_current_object()) + __deepcopy__ = lambda x, memo: copy.deepcopy(x._get_current_object(), memo) \ No newline at end of file diff --git a/apps/jumpserver/views.py b/apps/jumpserver/views.py index 1ad2bb20f..323f71dd2 100644 --- a/apps/jumpserver/views.py +++ b/apps/jumpserver/views.py @@ -22,7 +22,7 @@ class IndexView(LoginRequiredMixin, OrgViewGenericMixin, TemplateView): session_month_dates_archive = [] def get(self, request, *args, **kwargs): - if not request.user.is_superuser: + if not request.user.is_org_admin: return redirect('assets:user-asset-list') return super(IndexView, self).get(request, *args, **kwargs) diff --git a/apps/orgs/context_processor.py b/apps/orgs/context_processor.py index aa89d3a54..15285c99e 100644 --- a/apps/orgs/context_processor.py +++ b/apps/orgs/context_processor.py @@ -1,14 +1,16 @@ # -*- coding: utf-8 -*- # -from .utils import get_current_org +from .utils import current_org, get_current_org from .models import Organization def org_processor(request): + print('Crernt Org', current_org.name) context = { 'ADMIN_ORGS': Organization.get_user_admin_orgs(request.user), 'CURRENT_ORG': get_current_org(), + 'HAS_ORG_PERM': current_org.can_admin_by(request.user), } return context diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index ec77c4cfd..215518e68 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -4,9 +4,10 @@ from django.db import models from django.shortcuts import redirect import warnings from django.forms import ModelForm +from django.http.response import HttpResponseForbidden from common.utils import get_logger -from .utils import get_current_org, set_current_org +from .utils import current_org, set_current_org from .models import Organization logger = get_logger(__file__) @@ -23,7 +24,6 @@ __all__ = [ class OrgManager(models.Manager): def get_queryset(self): - current_org = get_current_org() kwargs = {} if not hasattr(tl, 'times'): tl.times = 0 @@ -44,7 +44,6 @@ class OrgManager(models.Manager): return queryset def all(self): - current_org = get_current_org() if not current_org: msg = 'You can `objects.set_current_org(org).all()` then run it' warnings.warn(msg) @@ -64,7 +63,6 @@ class OrgModelMixin(models.Model): objects = OrgManager() def save(self, *args, **kwargs): - current_org = get_current_org() if current_org and current_org.is_real(): self.org_id = current_org.id return super(OrgModelMixin, self).save(*args, **kwargs) @@ -75,9 +73,16 @@ class OrgModelMixin(models.Model): class OrgViewGenericMixin: def dispatch(self, request, *args, **kwargs): - current_org = get_current_org() + print("Crrent org: {}".format(current_org)) if not current_org: return redirect('orgs:switch-a-org') + + if not current_org.can_admin_by(request.user): + print("{} cannot admin {}".format(request.user, current_org)) + if request.user.is_org_admin: + print("Is org admin") + return redirect('orgs:switch-a-org') + return HttpResponseForbidden() return super().dispatch(request, *args, **kwargs) diff --git a/apps/orgs/models.py b/apps/orgs/models.py index 80cb56e5b..ec94caaf7 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -60,7 +60,16 @@ class Organization(models.Model): return self.users.all() def get_org_admins(self): - pass + if self.is_real(): + return self.admins.all() + return [] + + def can_admin_by(self, user): + if user.is_superuser: + return True + if user in list(self.get_org_admins()): + return True + return False def is_real(self): return len(str(self.id)) == 36 diff --git a/apps/orgs/utils.py b/apps/orgs/utils.py index 78e3cad1a..3df8c953f 100644 --- a/apps/orgs/utils.py +++ b/apps/orgs/utils.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- # -import re -from django.apps import apps +from functools import partial + +from common.utils import LocalProxy from .models import Organization try: @@ -42,3 +43,13 @@ def set_to_default_org(): def set_to_root_org(): set_current_org(Organization.root()) + + +def _find(attr): + if hasattr(_thread_locals, attr): + return getattr(_thread_locals, attr) + return None + + +current_org = LocalProxy(get_current_org) + diff --git a/apps/perms/forms.py b/apps/perms/forms.py index 6c63982ae..a71b25765 100644 --- a/apps/perms/forms.py +++ b/apps/perms/forms.py @@ -5,7 +5,7 @@ from django import forms from django.utils.translation import ugettext_lazy as _ from orgs.mixins import OrgModelForm -from orgs.utils import get_current_org +from orgs.utils import current_org from .hands import User from .models import AssetPermission @@ -29,7 +29,6 @@ class AssetPermissionForm(OrgModelForm): return users_field = self.fields.get('users') if hasattr(users_field, 'queryset'): - current_org = get_current_org() users_field.queryset = User.objects.filter(orgs=current_org) class Meta: diff --git a/apps/templates/_header_bar.html b/apps/templates/_header_bar.html index ae552fbb6..fc4d50689 100644 --- a/apps/templates/_header_bar.html +++ b/apps/templates/_header_bar.html @@ -35,7 +35,7 @@ +{% endif %}
  • {% trans 'Audits' %} @@ -92,11 +96,13 @@ {#
  • {% trans 'File download' %}
  • #} {# #} {##} +{% if request.user.is_superuser %}
  • {% trans 'Settings' %}
  • +{% endif %} {% endblock %} diff --git a/apps/audits/models.py b/apps/audits/models.py index 667087e18..b8d46586d 100644 --- a/apps/audits/models.py +++ b/apps/audits/models.py @@ -3,8 +3,10 @@ import uuid from django.db import models from django.utils.translation import ugettext_lazy as _ +from orgs.mixins import OrgModelMixin -class FTPLog(models.Model): + +class FTPLog(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) user = models.CharField(max_length=128, verbose_name=_('User')) remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True) diff --git a/apps/common/permissions.py b/apps/common/permissions.py index c9015844c..9d01031bc 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -80,6 +80,9 @@ class AdminUserRequiredMixin(UserPassesTestMixin): def dispatch(self, request, *args, **kwargs): print("Current org: {}".format(current_org)) + if not request.user.is_authenticated: + return super().dispatch(request, *args, **kwargs) + if not current_org: return redirect('orgs:switch-a-org') diff --git a/apps/jumpserver/views.py b/apps/jumpserver/views.py index 4dc3a3dc7..d6d74a497 100644 --- a/apps/jumpserver/views.py +++ b/apps/jumpserver/views.py @@ -175,4 +175,7 @@ class LunaView(View): Luna是单独部署的一个程序,你需要部署luna,coco,配置nginx做url分发, 如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运 """ - return HttpResponse(msg) \ No newline at end of file + return HttpResponse(msg) + + + diff --git a/apps/orgs/api.py b/apps/orgs/api.py index 14d837841..7da77cfce 100644 --- a/apps/orgs/api.py +++ b/apps/orgs/api.py @@ -3,7 +3,7 @@ from rest_framework import viewsets -from common.permissions import IsOrgAdminOrAppUser +from common.permissions import IsSuperUserOrAppUser from .models import Organization from .serializers import OrgSerializer @@ -11,4 +11,4 @@ from .serializers import OrgSerializer class OrgViewSet(viewsets.ModelViewSet): queryset = Organization.objects.all() serializer_class = OrgSerializer - permission_classes = (IsOrgAdminOrAppUser,) + permission_classes = (IsSuperUserOrAppUser,) diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index 873b25fa3..1dd08c5c5 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -23,10 +23,10 @@ __all__ = [ class OrgManager(models.Manager): def get_queryset(self): + queryset = super(OrgManager, self).get_queryset() kwargs = {} if not hasattr(tl, 'times'): tl.times = 0 - print("[{}]>>>>>>>>>> Get query set".format(tl.times)) print(current_org) if not current_org: @@ -34,11 +34,8 @@ class OrgManager(models.Manager): elif current_org.is_real(): kwargs['org_id'] = current_org.id elif current_org.is_default(): - kwargs['org_id'] = None - queryset = super(OrgManager, self).get_queryset() + queryset = queryset.filter(org_id="").filter(org_id__isnull=True) queryset = queryset.filter(**kwargs) - # print(kwargs) - # print(queryset.query) tl.times += 1 return queryset diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 4cc0a3e1e..d96c8c3d6 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -386,11 +386,10 @@ jumpserver.initServerSideDataTable = function (options) { selector: 'td:first-child' }; var table = ele.DataTable({ - // pageLength: options.pageLength || 15, - // dom: options.dom || '<"#uc.pull-left">flt<"row m-t"<"col-md-8"<"#op.col-md-6"><"col-md-6 text-center"i>><"col-md-4"p>>', - // order: options.order || [], - // select: options.select || 'multi', - // buttons: [], + pageLength: options.pageLength || 15, + dom: options.dom || '<"#uc.pull-left">fltr<"row m-t"<"col-md-8"<"#op.col-md-6"><"col-md-6 text-center"i>><"col-md-4"p>>', + order: options.order || [], + buttons: [], columnDefs: columnDefs, serverSide: true, processing: true, @@ -475,8 +474,7 @@ jumpserver.initServerSideDataTable = function (options) { } }) } - }). - on('draw', function(){ + }).on('draw', function(){ $('#op').html(options.op_html || ''); $('#uc').html(options.uc_html || ''); var table_data = []; From f17727deb9685b9f66b11fab29ea536040d5ecfc Mon Sep 17 00:00:00 2001 From: BaiJiangJie <32935519+BaiJiangJie@users.noreply.github.com> Date: Wed, 25 Jul 2018 15:13:53 +0800 Subject: [PATCH 20/63] =?UTF-8?q?[Update]=20=E5=9C=A8=E7=BA=BF/=E5=8E=86?= =?UTF-8?q?=E5=8F=B2/=E5=91=BD=E4=BB=A4model=E6=B7=BB=E5=8A=A0org=EF=BC=9B?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=91=BD=E4=BB=A4=E8=AE=B0=E5=BD=95=E4=BF=9D?= =?UTF-8?q?=E5=AD=98org=E5=A4=B1=E8=B4=A5bug=20(#1584)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Update] 修复创建授权规则,显示org_name不是有效UUID的bug * [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug; * [Update] 在线/历史/命令model添加org * [Bugfix] 修复命令记录,保存org不成功bug --- apps/terminal/backends/command/db.py | 4 ++-- apps/terminal/backends/command/models.py | 4 +++- apps/terminal/backends/command/serializers.py | 1 + apps/terminal/models.py | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/terminal/backends/command/db.py b/apps/terminal/backends/command/db.py index 6e0ac961d..29d322c1a 100644 --- a/apps/terminal/backends/command/db.py +++ b/apps/terminal/backends/command/db.py @@ -21,7 +21,7 @@ class CommandStore(CommandBase): user=command["user"], asset=command["asset"], system_user=command["system_user"], input=command["input"], output=command["output"], session=command["session"], - timestamp=command["timestamp"] + org_id=command["org_id"], timestamp=command["timestamp"] ) def bulk_save(self, commands): @@ -33,7 +33,7 @@ class CommandStore(CommandBase): _commands.append(self.model( user=c["user"], asset=c["asset"], system_user=c["system_user"], input=c["input"], output=c["output"], session=c["session"], - timestamp=c["timestamp"] + org_id=c["org_id"], timestamp=c["timestamp"] )) return self.model.objects.bulk_create(_commands) diff --git a/apps/terminal/backends/command/models.py b/apps/terminal/backends/command/models.py index 186769b48..b7416d0c3 100644 --- a/apps/terminal/backends/command/models.py +++ b/apps/terminal/backends/command/models.py @@ -4,8 +4,10 @@ import uuid from django.db import models from django.utils.translation import ugettext_lazy as _ +from orgs.mixins import OrgModelMixin -class AbstractSessionCommand(models.Model): + +class AbstractSessionCommand(OrgModelMixin): id = models.UUIDField(default=uuid.uuid4, primary_key=True) user = models.CharField(max_length=64, db_index=True, verbose_name=_("User")) asset = models.CharField(max_length=128, db_index=True, verbose_name=_("Asset")) diff --git a/apps/terminal/backends/command/serializers.py b/apps/terminal/backends/command/serializers.py index 634a81b36..c0a6e6c6e 100644 --- a/apps/terminal/backends/command/serializers.py +++ b/apps/terminal/backends/command/serializers.py @@ -12,5 +12,6 @@ class SessionCommandSerializer(serializers.Serializer): input = serializers.CharField(max_length=128) output = serializers.CharField(max_length=1024, allow_blank=True) session = serializers.CharField(max_length=36) + org_id = serializers.CharField(max_length=36) timestamp = serializers.IntegerField() diff --git a/apps/terminal/models.py b/apps/terminal/models.py index 3ae7066bb..e7b730b04 100644 --- a/apps/terminal/models.py +++ b/apps/terminal/models.py @@ -8,6 +8,7 @@ from django.utils import timezone from django.conf import settings from users.models import User +from orgs.mixins import OrgModelMixin from .backends.command.models import AbstractSessionCommand @@ -112,7 +113,7 @@ class Status(models.Model): return self.date_created.strftime("%Y-%m-%d %H:%M:%S") -class Session(models.Model): +class Session(OrgModelMixin): LOGIN_FROM_CHOICES = ( ('ST', 'SSH Terminal'), ('WT', 'Web Terminal'), From 2abb9efe9691d18855306546626a0e842dd7355e Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 25 Jul 2018 17:21:13 +0800 Subject: [PATCH 21/63] =?UTF-8?q?[Update]=20Org=E5=8A=9F=E8=83=BD=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/serializers/asset.py | 2 +- apps/common/utils.py | 4 +--- apps/orgs/mixins.py | 5 +++-- apps/terminal/api.py | 8 ++++---- apps/terminal/backends/command/serializers.py | 2 +- apps/terminal/urls/api_urls.py | 10 +++++----- 6 files changed, 15 insertions(+), 16 deletions(-) diff --git a/apps/assets/serializers/asset.py b/apps/assets/serializers/asset.py index dba6df3cf..4c648c0ab 100644 --- a/apps/assets/serializers/asset.py +++ b/apps/assets/serializers/asset.py @@ -43,7 +43,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer): fields = ( "id", "hostname", "ip", "port", "system_users_granted", "is_active", "system_users_join", "os", 'domain', - "platform", "comment", "protocol", + "platform", "comment", "protocol", "org_id", ) @staticmethod diff --git a/apps/common/utils.py b/apps/common/utils.py index 7d4b9af64..3751b6400 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -68,10 +68,8 @@ class Signer(metaclass=Singleton): self.secret_key = secret_key def sign(self, value): - if isinstance(value, bytes): - value = value.decode("utf-8") s = JSONWebSignatureSerializer(self.secret_key) - return s.dumps(value) + return s.dumps(value).decode() def unsign(self, value): if value is None: diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index 1dd08c5c5..244d967d0 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -3,6 +3,7 @@ from threading import local from django.db import models +from django.db.models import Q from django.shortcuts import redirect import warnings from django.forms import ModelForm @@ -34,7 +35,7 @@ class OrgManager(models.Manager): elif current_org.is_real(): kwargs['org_id'] = current_org.id elif current_org.is_default(): - queryset = queryset.filter(org_id="").filter(org_id__isnull=True) + queryset = queryset.filter(Q(org_id="") | Q(org_id__isnull=True)) queryset = queryset.filter(**kwargs) tl.times += 1 return queryset @@ -61,7 +62,7 @@ class OrgModelMixin(models.Model): def save(self, *args, **kwargs): if current_org and current_org.is_real(): self.org_id = current_org.id - return super(OrgModelMixin, self).save(*args, **kwargs) + return super().save(*args, **kwargs) class Meta: abstract = True diff --git a/apps/terminal/api.py b/apps/terminal/api.py index 75d20702b..3fe8340cf 100644 --- a/apps/terminal/api.py +++ b/apps/terminal/api.py @@ -24,8 +24,7 @@ from common.utils import get_object_or_none from .models import Terminal, Status, Session, Task from .serializers import TerminalSerializer, StatusSerializer, \ SessionSerializer, TaskSerializer, ReplaySerializer -from common.permissions import IsOrgAdmin, IsAppUser, IsOrgAdminOrAppUser, \ - IsOrgAdminOrAppUserOrUserReadonly +from common.permissions import IsAppUser, IsOrgAdminOrAppUser from .backends import get_command_storage, get_multi_command_storage, \ SessionCommandSerializer @@ -35,7 +34,7 @@ logger = logging.getLogger(__file__) class TerminalViewSet(viewsets.ModelViewSet): queryset = Terminal.objects.filter(is_deleted=False) serializer_class = TerminalSerializer - permission_classes = (IsOrgAdminOrAppUserOrUserReadonly,) + permission_classes = (AllowAny,) def create(self, request, *args, **kwargs): name = request.data.get('name') @@ -238,13 +237,14 @@ class CommandViewSet(viewsets.ViewSet): def create(self, request, *args, **kwargs): serializer = self.serializer_class(data=request.data, many=True) if serializer.is_valid(): + print(serializer.validated_data) ok = self.command_store.bulk_save(serializer.validated_data) if ok: return Response("ok", status=201) else: return Response("Save error", status=500) else: - msg = "Not valid: {}".format(serializer.errors) + msg = "Command not valid: {}".format(serializer.errors) logger.error(msg) return Response({"msg": msg}, status=401) diff --git a/apps/terminal/backends/command/serializers.py b/apps/terminal/backends/command/serializers.py index c0a6e6c6e..caedb04a8 100644 --- a/apps/terminal/backends/command/serializers.py +++ b/apps/terminal/backends/command/serializers.py @@ -12,6 +12,6 @@ class SessionCommandSerializer(serializers.Serializer): input = serializers.CharField(max_length=128) output = serializers.CharField(max_length=1024, allow_blank=True) session = serializers.CharField(max_length=36) - org_id = serializers.CharField(max_length=36) + org_id = serializers.CharField(max_length=36, required=False, default='') timestamp = serializers.IntegerField() diff --git a/apps/terminal/urls/api_urls.py b/apps/terminal/urls/api_urls.py index 17aeaf644..f03b381ea 100644 --- a/apps/terminal/urls/api_urls.py +++ b/apps/terminal/urls/api_urls.py @@ -10,9 +10,9 @@ from .. import api app_name = 'terminal' router = routers.DefaultRouter() -router.register(r'terminal', api.TerminalViewSet, 'terminal2') router.register(r'terminal/(?P[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status') router.register(r'terminal/(?P[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions') +router.register(r'terminal', api.TerminalViewSet, 'terminal') router.register(r'tasks', api.TaskViewSet, 'tasks') router.register(r'command', api.CommandViewSet, 'command') router.register(r'sessions', api.SessionViewSet, 'session') @@ -20,16 +20,16 @@ router.register(r'status', api.StatusViewSet, 'session') urlpatterns = [ url(r'^sessions/(?P[0-9a-zA-Z\-]{36})/replay/$', - api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}), + api.SessionReplayV2ViewSet.as_view({'get': 'retrieve', 'post': 'create'}), name='session-replay'), url(r'^tasks/kill-session/', api.KillSessionAPI.as_view(), name='kill-session'), url(r'^terminal/(?P[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key'), url(r'^terminal/config', api.TerminalConfig.as_view(), name='terminal-config'), # v2: get session's replay - url(r'^v2/sessions/(?P[0-9a-zA-Z\-]{36})/replay/$', - api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}), - name='session-replay-v2'), + # url(r'^v2/sessions/(?P[0-9a-zA-Z\-]{36})/replay/$', + # api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}), + # name='session-replay-v2'), ] urlpatterns += router.urls From 8c12c382a5168d900daa5a8dd8356da0de5a0b11 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 25 Jul 2018 18:21:37 +0800 Subject: [PATCH 22/63] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8Dmerge?= =?UTF-8?q?=E5=B8=A6=E6=9D=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/backends/command/serializers.py | 2 +- apps/users/api.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/terminal/backends/command/serializers.py b/apps/terminal/backends/command/serializers.py index caedb04a8..4545bbe65 100644 --- a/apps/terminal/backends/command/serializers.py +++ b/apps/terminal/backends/command/serializers.py @@ -12,6 +12,6 @@ class SessionCommandSerializer(serializers.Serializer): input = serializers.CharField(max_length=128) output = serializers.CharField(max_length=1024, allow_blank=True) session = serializers.CharField(max_length=36) - org_id = serializers.CharField(max_length=36, required=False, default='') + org_id = serializers.CharField(max_length=36, required=False, default='', allow_null=True) timestamp = serializers.IntegerField() diff --git a/apps/users/api.py b/apps/users/api.py index 012c2db1d..89ab0d129 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -103,7 +103,7 @@ class UserUpdatePKApi(generics.UpdateAPIView): class UserUnblockPKApi(generics.UpdateAPIView): queryset = User.objects.all() - permission_classes = (IsSuperUser,) + permission_classes = (IsOrgAdmin,) serializer_class = UserSerializer key_prefix_limit = "_LOGIN_LIMIT_{}_{}" key_prefix_block = "_LOGIN_BLOCK_{}" From 04e7f54c69bd6b3858283a6c2874b0e6f136412c Mon Sep 17 00:00:00 2001 From: BaiJiangJie <32935519+BaiJiangJie@users.noreply.github.com> Date: Thu, 26 Jul 2018 18:25:44 +0800 Subject: [PATCH 23/63] =?UTF-8?q?[Update]=20org=20admin=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=E8=AF=A6=E6=83=85=E5=8F=B3=E4=BE=A7=E9=80=89?= =?UTF-8?q?=E9=A1=B9=E5=8D=A1=EF=BC=9B=E4=BF=AE=E5=A4=8D=E8=B5=84=E4=BA=A7?= =?UTF-8?q?=E6=8E=88=E6=9D=83=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=EF=BC=8C?= =?UTF-8?q?=E4=BC=9A=E6=98=BE=E7=A4=BA=E5=85=B6=E4=BB=96org=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=9A=84bug=20(#1594)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Bugfix] 修复资产授权添加用户,显示其他org的用户bug * [Update] org admin 显示资产详情右侧选项卡 --- apps/assets/templates/assets/asset_detail.html | 2 +- apps/perms/views.py | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/assets/templates/assets/asset_detail.html b/apps/assets/templates/assets/asset_detail.html index b07a7c348..2f36688be 100644 --- a/apps/assets/templates/assets/asset_detail.html +++ b/apps/assets/templates/assets/asset_detail.html @@ -130,7 +130,7 @@ - {% if user.is_superuser %} + {% if user.is_superuser or user.is_org_admin %}
    diff --git a/apps/perms/views.py b/apps/perms/views.py index cd2481222..411405848 100644 --- a/apps/perms/views.py +++ b/apps/perms/views.py @@ -9,6 +9,7 @@ from django.urls import reverse_lazy from django.conf import settings from common.permissions import AdminUserRequiredMixin +from orgs.utils import current_org from .hands import Node, Asset, SystemUser, User, UserGroup from .models import AssetPermission from .forms import AssetPermissionForm @@ -87,7 +88,6 @@ class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView): 'system_users_remain': SystemUser.objects.exclude( granted_by_permissions=self.object ), - } kwargs.update(context) return super().get_context_data(**kwargs) @@ -108,7 +108,7 @@ class AssetPermissionUserView(AdminUserRequiredMixin, object = None def get(self, request, *args, **kwargs): - self.object = self.get_object(queryset = AssetPermission.objects.all()) + self.object = self.get_object(queryset=AssetPermission.objects.all()) return super().get(request, *args, **kwargs) def get_queryset(self): @@ -116,11 +116,13 @@ class AssetPermissionUserView(AdminUserRequiredMixin, return queryset def get_context_data(self, **kwargs): + context = { 'app': _('Perms'), 'action': _('Asset permission user list'), - 'users_remain': User.objects.exclude(asset_permissions=self.object) - .exclude(role=User.ROLE_APP), + 'users_remain': current_org.get_org_users().exclude( + asset_permissions=self.object + ), 'user_groups_remain': UserGroup.objects.exclude( asset_permissions=self.object ) From a48fb9de8d3531d0825e08e0603c2919261e4fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=81=E5=B9=BF?= Date: Thu, 26 Jul 2018 05:27:09 -0500 Subject: [PATCH 24/63] Tmp org (#1596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Update] 修改index view * [Update] 修改nav * [Update] 修改profile --- apps/jumpserver/views.py | 4 ++-- apps/static/css/jumpserver.css | 4 ++-- apps/static/img/header-profile.png | Bin 0 -> 5877 bytes apps/templates/_nav.html | 16 ------------- apps/templates/_user_profile.html | 35 ++++++++++++++++++++++++----- 5 files changed, 34 insertions(+), 25 deletions(-) create mode 100755 apps/static/img/header-profile.png diff --git a/apps/jumpserver/views.py b/apps/jumpserver/views.py index c2915ae0d..474eaf829 100644 --- a/apps/jumpserver/views.py +++ b/apps/jumpserver/views.py @@ -21,10 +21,10 @@ class IndexView(AdminUserRequiredMixin, TemplateView): session_month_dates = [] session_month_dates_archive = [] - def get(self, request, *args, **kwargs): + def dispatch(self, request, *args, **kwargs): if not request.user.is_org_admin: return redirect('assets:user-asset-list') - return super(IndexView, self).get(request, *args, **kwargs) + return super(IndexView, self).dispatch(request, *args, **kwargs) @staticmethod def get_user_count(): diff --git a/apps/static/css/jumpserver.css b/apps/static/css/jumpserver.css index cf570c4ad..13b58ac25 100644 --- a/apps/static/css/jumpserver.css +++ b/apps/static/css/jumpserver.css @@ -335,13 +335,13 @@ div.dataTables_wrapper div.dataTables_filter { .nav-header, body.mini-navbar .nav-header { padding: 0; - background: #202c37; + /*background: #202c37;*/ } .profile-element div:first-child { line-height: 60px; /*width: 70px;*/ - float: left; + /*float: left;*/ text-align: center; } diff --git a/apps/static/img/header-profile.png b/apps/static/img/header-profile.png new file mode 100755 index 0000000000000000000000000000000000000000..7dea7f2c76294013243f4956526a0409e82da9d2 GIT binary patch literal 5877 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 LNkvXXu0mjf-K0Pr literal 0 HcmV?d00001 diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index a792beb48..772c9db78 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -1,20 +1,4 @@ {% load i18n %} -{% if ADMIN_ORGS and ADMIN_ORGS|length > 1 %} -
  • - - -
  • -
    -{% endif %}
  • {% trans 'Dashboard' %} diff --git a/apps/templates/_user_profile.html b/apps/templates/_user_profile.html index d7525958b..6717fa771 100644 --- a/apps/templates/_user_profile.html +++ b/apps/templates/_user_profile.html @@ -1,15 +1,40 @@ {% load static %} {% load i18n %}
  • {% endblock %} \ No newline at end of file From 485a178c0a48196f37bfe6ea7258fd5dd866816a Mon Sep 17 00:00:00 2001 From: BaiJiangJie <32935519+BaiJiangJie@users.noreply.github.com> Date: Wed, 1 Aug 2018 10:44:43 +0800 Subject: [PATCH 46/63] =?UTF-8?q?[Update]=20=E5=88=9B=E5=BB=BA/=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=94=A8=E6=88=B7=E7=9A=84role=E9=80=89=E9=A1=B9?= =?UTF-8?q?=EF=BC=9B=E5=AF=86=E7=A0=81=E5=BC=BA=E5=BA=A6=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E4=B8=AD=E8=8B=B1=E6=96=87=EF=BC=9B=20(#1623?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Update] 修改 超级管理员/组织管理员 在 创建/更新 用户时role的选项 问题 * [Update] 用户密码强度提示信息支持中英文 --- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 37020 -> 37050 bytes apps/i18n/zh/LC_MESSAGES/django.po | 636 +++++++++--------- apps/static/js/jumpserver.js | 10 +- apps/users/forms.py | 24 + .../users/templates/users/reset_password.html | 12 +- .../templates/users/user_password_update.html | 12 +- apps/users/templates/users/user_update.html | 12 +- apps/users/views/user.py | 12 + 8 files changed, 411 insertions(+), 307 deletions(-) diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index bc860621d21695678c0861851093bcc830d23c74..903763d9bba7f3375f3f335e30acdc234491726a 100644 GIT binary patch delta 12570 zcmYk?2Yila+sE-MQxZfHLaZRRA|eE_w;{)8>RYGs0NR3C)L941YN~x8H+N-VB zDpIOOjaF3EdMH|}@9%$Khxg<9e2#w4$IuG`A1XeLWPcW0`h)2vrq$fTE_#ZJ3flK zgG_6`jCypxquz--sEO~R7J7zJn5(wiUIx`(-b|>?`KzLyRWwJvMD3BsJcqNJ(3NmmvkTM$WNojT|v$B2NuM8sFMh->n>On^{6VKZrszDf);9xTA&+hXM<56 ztMRA_=2*TIwcsYJ-;KKCs!y=U@j#0I8hWdVM)}$IMjp< z%w*IZbwq8X7iytVsFO=WeM)Ad9@QtP`BtIYH>2tgpvE0S&36)W>+^q(f_8Wty*oy2 z;29Rhp!$yYB`u5U*9Ydn&dUW4nS=M*XQP4#G z4cv(%P$!WewUKhDomWLoTpP84Ca6c2Z1r7H3-m?xAB5^R3N`-(EQ;xz)WoT%iMpT`?1Q@V5vYa6VJVz~zPQ?4i!tQu&6ABd z|4LN+MTHhD*_g>O1(R?c>O>x*7J7!-NJtaM*^YV9A5UNao<_}c9<{;jOlDj-T0R)Hz&O-9G11!RqBgV`weTv`_;nbFUs=8fwUI-pukK9L zr{X&5x5D!`1-(SMnz=_4jheVVmc&-51xBM5m|#xFAo6!m;}=`~YBR&!gWAv`RKF8g z3@@RdKL0+Pv%aYUFh3SUy<9ah2$NBFo{BoUepm|AQ18rVsEuwxEpQ&y{|0K|C#G+6 z_oRbSHx!B4^!YDHK}S*?wLm=Tt!;|Y*awT?G;=uylYfnRG{;bnFcWoh7p?wJRKL5Z z8+nYnF~1h>xJV4q=f402?WiPb$FHC|Rz}@PBh=4r3hHehj@fV&hF}_M;TfnCS%^A` zRj7VjP#f5d9JzDQ>JOtw12QRS16imI{D~TH4>i#f)LR|W(tV2)Q76?5_3`bB+URJ@ zr=xD*W7NlOgPCdd4^StbEt&Jzv(K08z60^7jkGpXP#fujI)Of@4STROF2EAF1NA5_ zq55AnZ(&aIhnOAxTe&9^hT1?>E6!g7%21(a6^A;~n%1ESRwhqH-O<~qg+4-khcCk% zxWznxx`E@U`A(xw^b+b1k!z?&@-OPdvUys&cNT@}7>}Wth}v-y>T{c7`5-JqJ`UA? z8Ft3cQR94Hbqz;tq$p~mr7#|ATKh2ce*WL2P@abOurMA#-Qi`_%aVm!=nvGL-bHQf z8R}64w{h>ZkXZ_KYw;wx|>D>FRL?P{>8aNYounMJ+fV!*LOY;TNb4>_okM z-=h|~VD&ew{t;?}+1t7s%!e9R4s~PIP>-S(Hqz%mg@Rtf4^eOPR@B5lpq|wQ)W)uw z_fZ@6ZRZ|s1ggC>YGVngJ8X`_upN%TuTk?=ZSS_%#sGc(n^4e>TcY0Tj;IrujN0i; z)JwU{>erf^&F!d(_n|iOJ?b4fgSulU#a%D}HE$^9#z^!;QYcG7&%Q2dBi%3@2bgIX zK|UAtD3+m)bQ7xo9*oDs_zFHpz5V6*DV0@F9(>9$CpwyjYlwHE=rWk?cin=sVOSJc?8B3~t3v9zMKL z6vDc=&#o+LKy}o2eQngoupVl{MyMlfiF($ZP>-NL>e-G)J=-*l#CfQ1&XuS;--jBX zi8?{gB?|c{+(SK+5N=-+N1-~FMjc@l)C7$zPencR*HI_Mp8 zYyOQqO3uHVd&eQD0l82cDTum*vZxK!MNQBObw{bFBOQV2KN0)lEUUkZn(ql}e75fH zhV!CMs5ypc{q7X>at%Q3bOP#;%t77xO4LMKP%qzp)PhG)6P-mZaLd}CqD~^Phr7}I zsClcP`Zqzn?42>%L*X?F`V>q>-ElfH#xWFJ07 zIG*}_y}21oe2stQP`?}Z;xwMmSJXG`=bq4$ew=?1Dnk47Wq}pVPFR@yEi8nenR`(i zxqv$2-zqiB@s_`B`TMAyud@1$=5F(_dB(ho`tF^D`8^(lzP9Qynhr=W!@p?2O7{je+Q&U=~z&C%wYsD;z93eL0q8*4v;I?>Zs zpM?qJH!UwdjCEPxDM!KktjtDc3#6~p7PZ0ss5?4kUO|n!Z9XucTD#wHw|^MsqF-Uu z4OK9!q4)D&pMn->W_Cd>G}Q7m)WH8({UURvxfwNnubF9PS^M9p4FrsE=ZQppffX6S z`KzI{HB>|0QGL_~nwcqPZ*v%G!HJg7Gnb&oud#fi<-0K__1~b@%e4AmN7(284=S|a zLu>FG=}r`4hNA{XSzZR!Kh~^;1<8}lzNn2%G1F1~=b`3VV);6cb=Z!jY1n7^b@L8} zQ~wb4_6Ln}Cn|?ppc;l@9n>RgZS|c{3-+{ph~=X#pMsj#lTJYcKeUDwsDW!S0=HTF z5o*qjuU7^$5CJ{V>#> zjWwrP{X3R_VEGEvxV5O0++!ZH_EV^N&Z9?#>sIl=^yNo9iuzorJ1Ap$EUJGq%TrJb z4MqLEFa_0r398?6b0g}jcemvS%^%0}`B%rwR&mRGjG8#x1oxdNfci>qjC$LX%+9E1 zJP7sq9)lV;&D!6`lH{LTehl@qdl9v6wlvN^H-$WDcBf`6szZY14bA3e8;qpABkH7v znlsI1=62LPnW&SwWcfAJ#_xNq@XQ*rO>_t3K}}o?H89cY>zl2tz6 z3okZTnHlCj)Hu&E3VPWtqZYc0+Sw!21U{48`cN!L9%*@;S>3FQny?8L$4;p6ldOIg zYMqZ!H})lR@*d})HJr1C8>pl8d($0|A2m@~)CLmFwpKqBwed8|r=rHsvwS5+lW#_S zPn<;czmLKC{eSE&@J9h<-Fc`Pg<3cUb!V}tfelb^a}uh5TU5WU7>zIfrc5VS|MyVi zmsr0I<}S4p&LRGziB6~j&!LX!isg?{6ZlMa7s!L^7mr#v(QJx3;`U}A)I6h6{paE| zd>1t?YKndSOHxpW%BTryU;-wgI!;DyU@qz_dKqfsBdBp#%|Fe1sB!<8f&61Y<8!0x zOQF7);-_-{D%7!xB(ptgBRxmZ!Q1X%(D7>Q#o1< z4B%X}U<4|UK}{Tsy3+*gkF71=W}ZZC@CK^?U#Rg<%%Hd2JkpFYW6fF~DY$bh;z≦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 YbZM`Y?qkw?Q@T%j?w2xfdc4>F0N?@S@&Et; diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index b0ef3043f..12310b65f 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Jumpserver 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-07-19 18:29+0800\n" +"POT-Creation-Date: 2018-07-31 19:18+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -17,57 +17,57 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: assets/api/node.py:99 +#: assets/api/node.py:97 msgid "New node {}" msgstr "新节点 {}" -#: assets/api/node.py:234 +#: assets/api/node.py:232 msgid "更新节点资产硬件信息: {}" msgstr "" -#: assets/api/node.py:247 +#: assets/api/node.py:245 msgid "测试节点下资产是否可连接: {}" msgstr "" -#: assets/forms/asset.py:24 assets/models/asset.py:89 assets/models/user.py:112 +#: assets/forms/asset.py:27 assets/models/asset.py:80 assets/models/user.py:113 #: assets/templates/assets/asset_detail.html:183 #: assets/templates/assets/asset_detail.html:191 -#: assets/templates/assets/system_user_detail.html:178 perms/models.py:33 +#: assets/templates/assets/system_user_detail.html:178 perms/models.py:32 msgid "Nodes" msgstr "节点管理" -#: assets/forms/asset.py:27 assets/forms/asset.py:66 assets/forms/asset.py:109 -#: assets/forms/asset.py:113 assets/models/asset.py:94 -#: assets/models/cluster.py:19 assets/models/user.py:72 +#: assets/forms/asset.py:30 assets/forms/asset.py:69 assets/forms/asset.py:112 +#: assets/forms/asset.py:116 assets/models/asset.py:85 +#: assets/models/cluster.py:19 assets/models/user.py:73 #: assets/templates/assets/asset_detail.html:73 templates/_nav.html:25 msgid "Admin user" msgstr "管理用户" -#: assets/forms/asset.py:30 assets/forms/asset.py:69 assets/forms/asset.py:125 +#: assets/forms/asset.py:33 assets/forms/asset.py:72 assets/forms/asset.py:128 #: assets/templates/assets/asset_create.html:36 #: assets/templates/assets/asset_create.html:38 -#: assets/templates/assets/asset_list.html:75 +#: assets/templates/assets/asset_list.html:80 #: assets/templates/assets/asset_update.html:41 #: assets/templates/assets/asset_update.html:43 #: assets/templates/assets/user_asset_list.html:34 msgid "Label" msgstr "标签" -#: assets/forms/asset.py:34 assets/forms/asset.py:73 assets/models/asset.py:85 -#: assets/models/domain.py:46 +#: assets/forms/asset.py:37 assets/forms/asset.py:76 assets/models/asset.py:76 +#: assets/models/domain.py:47 assets/templates/assets/user_asset_list.html:188 msgid "Domain" msgstr "网域" -#: assets/forms/asset.py:38 assets/forms/asset.py:63 assets/forms/asset.py:77 -#: assets/forms/asset.py:128 assets/templates/assets/asset_create.html:30 -#: assets/templates/assets/asset_update.html:35 perms/forms.py:40 -#: perms/forms.py:47 perms/models.py:76 +#: assets/forms/asset.py:41 assets/forms/asset.py:66 assets/forms/asset.py:80 +#: assets/forms/asset.py:131 assets/templates/assets/asset_create.html:30 +#: assets/templates/assets/asset_update.html:35 perms/forms.py:50 +#: perms/forms.py:57 perms/models.py:78 #: perms/templates/perms/asset_permission_list.html:57 #: perms/templates/perms/asset_permission_list.html:142 msgid "Node" msgstr "节点" -#: assets/forms/asset.py:45 assets/forms/asset.py:85 +#: assets/forms/asset.py:48 assets/forms/asset.py:88 msgid "" "root or other NOPASSWD sudo privilege user existed in asset,If asset is " "windows or other set any one, more see admin user left menu" @@ -75,42 +75,39 @@ msgstr "" "root或其他拥有NOPASSWD: ALL权限的用户, 如果是windows或其它硬件可以随意设置一" "个, 更多信息查看左侧 `管理用户` 菜单" -#: assets/forms/asset.py:48 assets/forms/asset.py:88 -msgid "* required Must set exact system platform, Windows, Linux ..." -msgstr "* required 必须准确设置操作系统平台,如Windows, Linux ..." - -#: assets/forms/asset.py:49 assets/forms/asset.py:89 +#: assets/forms/asset.py:52 assets/forms/asset.py:92 msgid "" "If your have some network not connect with each other, you can set domain" msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,使用网域网关跳转登录" -#: assets/forms/asset.py:96 assets/forms/asset.py:100 assets/forms/domain.py:16 +#: assets/forms/asset.py:99 assets/forms/asset.py:103 assets/forms/domain.py:17 #: assets/forms/label.py:15 #: perms/templates/perms/asset_permission_asset.html:88 msgid "Select assets" msgstr "选择资产" -#: assets/forms/asset.py:105 assets/models/asset.py:81 -#: assets/models/domain.py:44 assets/templates/assets/admin_user_assets.html:53 +#: assets/forms/asset.py:108 assets/models/asset.py:73 +#: assets/models/domain.py:45 assets/templates/assets/admin_user_assets.html:53 #: assets/templates/assets/asset_detail.html:69 #: assets/templates/assets/domain_gateway_list.html:58 #: assets/templates/assets/system_user_asset.html:51 +#: assets/templates/assets/user_asset_list.html:183 msgid "Port" msgstr "端口" -#: assets/forms/domain.py:14 assets/forms/label.py:13 -#: assets/models/asset.py:237 assets/templates/assets/admin_user_list.html:25 +#: assets/forms/domain.py:15 assets/forms/label.py:13 +#: assets/models/asset.py:225 assets/templates/assets/admin_user_list.html:25 #: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_list.html:23 #: assets/templates/assets/label_list.html:16 -#: assets/templates/assets/system_user_list.html:30 audits/models.py:11 +#: assets/templates/assets/system_user_list.html:30 audits/models.py:13 #: audits/templates/audits/ftp_log_list.html:41 -#: audits/templates/audits/ftp_log_list.html:72 perms/forms.py:37 -#: perms/models.py:32 +#: audits/templates/audits/ftp_log_list.html:72 perms/forms.py:47 +#: perms/models.py:31 #: perms/templates/perms/asset_permission_create_update.html:40 #: perms/templates/perms/asset_permission_list.html:56 #: perms/templates/perms/asset_permission_list.html:139 -#: terminal/backends/command/models.py:11 terminal/models.py:127 +#: terminal/backends/command/models.py:13 terminal/models.py:128 #: terminal/templates/terminal/command_list.html:40 #: terminal/templates/terminal/command_list.html:73 #: terminal/templates/terminal/session_list.html:41 @@ -118,9 +115,9 @@ msgstr "端口" msgid "Asset" msgstr "资产" -#: assets/forms/domain.py:54 assets/forms/user.py:79 assets/forms/user.py:139 -#: assets/models/base.py:21 assets/models/cluster.py:18 -#: assets/models/domain.py:17 assets/models/group.py:20 +#: assets/forms/domain.py:55 assets/forms/user.py:79 assets/forms/user.py:139 +#: assets/models/base.py:22 assets/models/cluster.py:18 +#: assets/models/domain.py:18 assets/models/group.py:20 #: assets/models/label.py:17 assets/templates/assets/admin_user_detail.html:56 #: assets/templates/assets/admin_user_list.html:23 #: assets/templates/assets/domain_detail.html:56 @@ -132,12 +129,13 @@ msgstr "资产" #: common/templates/common/terminal_setting.html:72 #: common/templates/common/terminal_setting.html:90 ops/models/adhoc.py:36 #: ops/templates/ops/task_detail.html:59 ops/templates/ops/task_list.html:35 -#: perms/models.py:29 perms/templates/perms/asset_permission_detail.html:62 +#: orgs/models.py:10 perms/models.py:28 +#: perms/templates/perms/asset_permission_detail.html:62 #: perms/templates/perms/asset_permission_list.html:53 -#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:16 -#: terminal/models.py:154 terminal/templates/terminal/terminal_detail.html:43 -#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:12 -#: users/models/user.py:49 users/templates/users/_select_user_modal.html:13 +#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:17 +#: terminal/models.py:155 terminal/templates/terminal/terminal_detail.html:43 +#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14 +#: users/models/user.py:51 users/templates/users/_select_user_modal.html:13 #: users/templates/users/user_detail.html:63 #: users/templates/users/user_group_detail.html:55 #: users/templates/users/user_group_list.html:12 @@ -147,14 +145,14 @@ msgstr "资产" msgid "Name" msgstr "名称" -#: assets/forms/domain.py:55 assets/forms/user.py:80 assets/forms/user.py:140 -#: assets/models/base.py:22 assets/templates/assets/admin_user_detail.html:60 +#: assets/forms/domain.py:56 assets/forms/user.py:80 assets/forms/user.py:140 +#: assets/models/base.py:23 assets/templates/assets/admin_user_detail.html:60 #: assets/templates/assets/admin_user_list.html:24 #: assets/templates/assets/domain_gateway_list.html:60 #: assets/templates/assets/system_user_detail.html:62 #: assets/templates/assets/system_user_list.html:27 -#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13 -#: users/forms.py:31 users/models/authentication.py:70 users/models/user.py:47 +#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:15 +#: users/forms.py:33 users/models/authentication.py:70 users/models/user.py:49 #: users/templates/users/_select_user_modal.html:14 #: users/templates/users/login.html:60 #: users/templates/users/login_log_list.html:49 @@ -168,8 +166,8 @@ msgstr "用户名" msgid "Password or private key passphrase" msgstr "密码或密钥密码" -#: assets/forms/user.py:25 assets/models/base.py:23 common/forms.py:113 -#: users/forms.py:15 users/forms.py:33 users/forms.py:45 +#: assets/forms/user.py:25 assets/models/base.py:24 common/forms.py:113 +#: users/forms.py:17 users/forms.py:35 users/forms.py:47 #: users/templates/users/login.html:63 #: users/templates/users/reset_password.html:53 #: users/templates/users/user_create.html:10 @@ -180,7 +178,7 @@ msgstr "密码或密钥密码" msgid "Password" msgstr "密码" -#: assets/forms/user.py:28 users/models/user.py:76 +#: assets/forms/user.py:28 users/models/user.py:78 msgid "Private key" msgstr "ssh私钥" @@ -212,14 +210,15 @@ msgid "" "password." msgstr "如果选择手动登录模式,用户名和密码则不需要填写" -#: assets/models/asset.py:74 assets/models/domain.py:43 +#: assets/models/asset.py:70 assets/models/domain.py:44 #: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/admin_user_assets.html:52 #: assets/templates/assets/asset_detail.html:61 -#: assets/templates/assets/asset_list.html:87 +#: assets/templates/assets/asset_list.html:92 #: assets/templates/assets/domain_gateway_list.html:57 #: assets/templates/assets/system_user_asset.html:50 -#: assets/templates/assets/user_asset_list.html:46 common/forms.py:144 +#: assets/templates/assets/user_asset_list.html:46 +#: assets/templates/assets/user_asset_list.html:182 common/forms.py:144 #: perms/templates/perms/asset_permission_asset.html:55 #: users/templates/users/login_log_list.html:52 #: users/templates/users/user_granted_asset.html:45 @@ -227,130 +226,135 @@ msgstr "如果选择手动登录模式,用户名和密码则不需要填写" msgid "IP" msgstr "IP" -#: assets/models/asset.py:77 assets/templates/assets/_asset_list_modal.html:45 +#: assets/models/asset.py:71 assets/templates/assets/_asset_list_modal.html:45 #: assets/templates/assets/admin_user_assets.html:51 #: assets/templates/assets/asset_detail.html:57 -#: assets/templates/assets/asset_list.html:86 +#: assets/templates/assets/asset_list.html:91 #: assets/templates/assets/system_user_asset.html:49 -#: assets/templates/assets/user_asset_list.html:45 common/forms.py:143 +#: assets/templates/assets/user_asset_list.html:45 +#: assets/templates/assets/user_asset_list.html:181 common/forms.py:143 #: perms/templates/perms/asset_permission_asset.html:54 #: users/templates/users/user_granted_asset.html:44 #: users/templates/users/user_group_granted_asset.html:44 msgid "Hostname" msgstr "主机名" -#: assets/models/asset.py:80 assets/models/domain.py:45 -#: assets/models/user.py:115 +#: assets/models/asset.py:72 assets/models/domain.py:46 +#: assets/models/user.py:116 #: assets/templates/assets/domain_gateway_list.html:59 #: assets/templates/assets/system_user_detail.html:70 #: assets/templates/assets/system_user_list.html:28 +#: assets/templates/assets/user_asset_list.html:184 #: terminal/templates/terminal/session_list.html:75 msgid "Protocol" msgstr "协议" -#: assets/models/asset.py:83 assets/templates/assets/asset_detail.html:97 +#: assets/models/asset.py:74 assets/templates/assets/asset_detail.html:97 +#: assets/templates/assets/user_asset_list.html:185 msgid "Platform" msgstr "系统平台" -#: assets/models/asset.py:90 assets/models/domain.py:48 +#: assets/models/asset.py:81 assets/models/domain.py:49 #: assets/models/label.py:20 assets/templates/assets/asset_detail.html:105 +#: assets/templates/assets/user_asset_list.html:189 msgid "Is active" msgstr "激活" -#: assets/models/asset.py:99 assets/templates/assets/asset_detail.html:65 +#: assets/models/asset.py:88 assets/templates/assets/asset_detail.html:65 msgid "Public IP" msgstr "公网IP" -#: assets/models/asset.py:101 assets/templates/assets/asset_detail.html:113 +#: assets/models/asset.py:89 assets/templates/assets/asset_detail.html:113 msgid "Asset number" msgstr "资产编号" -#: assets/models/asset.py:105 assets/templates/assets/asset_detail.html:77 +#: assets/models/asset.py:93 assets/templates/assets/asset_detail.html:77 msgid "Vendor" msgstr "制造商" -#: assets/models/asset.py:107 assets/templates/assets/asset_detail.html:81 +#: assets/models/asset.py:95 assets/templates/assets/asset_detail.html:81 msgid "Model" msgstr "型号" -#: assets/models/asset.py:109 assets/templates/assets/asset_detail.html:109 +#: assets/models/asset.py:97 assets/templates/assets/asset_detail.html:109 msgid "Serial number" msgstr "序列号" -#: assets/models/asset.py:112 +#: assets/models/asset.py:100 msgid "CPU model" msgstr "CPU型号" -#: assets/models/asset.py:113 +#: assets/models/asset.py:101 msgid "CPU count" msgstr "CPU数量" -#: assets/models/asset.py:114 +#: assets/models/asset.py:102 msgid "CPU cores" msgstr "CPU核数" -#: assets/models/asset.py:116 assets/templates/assets/asset_detail.html:89 +#: assets/models/asset.py:104 assets/templates/assets/asset_detail.html:89 msgid "Memory" msgstr "内存" -#: assets/models/asset.py:118 +#: assets/models/asset.py:106 msgid "Disk total" msgstr "硬盘大小" -#: assets/models/asset.py:120 +#: assets/models/asset.py:108 msgid "Disk info" msgstr "硬盘信息" -#: assets/models/asset.py:123 assets/templates/assets/asset_detail.html:101 +#: assets/models/asset.py:111 assets/templates/assets/asset_detail.html:101 +#: assets/templates/assets/user_asset_list.html:186 msgid "OS" msgstr "操作系统" -#: assets/models/asset.py:125 +#: assets/models/asset.py:113 msgid "OS version" msgstr "系统版本" -#: assets/models/asset.py:127 +#: assets/models/asset.py:115 msgid "OS arch" msgstr "系统架构" -#: assets/models/asset.py:129 +#: assets/models/asset.py:117 msgid "Hostname raw" msgstr "主机名原始" -#: assets/models/asset.py:133 assets/templates/assets/asset_create.html:34 +#: assets/models/asset.py:121 assets/templates/assets/asset_create.html:34 #: assets/templates/assets/asset_detail.html:220 #: assets/templates/assets/asset_update.html:39 templates/_nav.html:27 msgid "Labels" msgstr "标签管理" -#: assets/models/asset.py:135 assets/models/base.py:29 +#: assets/models/asset.py:123 assets/models/base.py:30 #: assets/models/cluster.py:28 assets/models/group.py:21 #: assets/templates/assets/admin_user_detail.html:68 #: assets/templates/assets/asset_detail.html:117 #: assets/templates/assets/domain_detail.html:72 #: assets/templates/assets/system_user_detail.html:100 -#: ops/templates/ops/adhoc_detail.html:86 perms/models.py:38 perms/models.py:81 -#: perms/templates/perms/asset_permission_detail.html:98 -#: users/models/user.py:90 users/templates/users/user_detail.html:111 +#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:13 perms/models.py:37 +#: perms/models.py:83 perms/templates/perms/asset_permission_detail.html:98 +#: users/models/user.py:92 users/templates/users/user_detail.html:111 msgid "Created by" msgstr "创建者" -#: assets/models/asset.py:138 assets/models/cluster.py:26 -#: assets/models/domain.py:20 assets/models/group.py:22 +#: assets/models/asset.py:126 assets/models/cluster.py:26 +#: assets/models/domain.py:21 assets/models/group.py:22 #: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:64 #: assets/templates/assets/domain_detail.html:68 #: assets/templates/assets/system_user_detail.html:96 #: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:63 -#: perms/models.py:39 perms/models.py:82 +#: orgs/models.py:14 perms/models.py:38 perms/models.py:84 #: perms/templates/perms/asset_permission_detail.html:94 -#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:15 +#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17 #: users/templates/users/user_group_detail.html:63 msgid "Date created" msgstr "创建日期" -#: assets/models/asset.py:140 assets/models/base.py:26 -#: assets/models/cluster.py:29 assets/models/domain.py:18 -#: assets/models/domain.py:47 assets/models/group.py:23 +#: assets/models/asset.py:128 assets/models/base.py:27 +#: assets/models/cluster.py:29 assets/models/domain.py:19 +#: assets/models/domain.py:48 assets/models/group.py:23 #: assets/models/label.py:21 assets/templates/assets/admin_user_detail.html:72 #: assets/templates/assets/admin_user_list.html:29 #: assets/templates/assets/asset_detail.html:125 @@ -358,22 +362,24 @@ msgstr "创建日期" #: assets/templates/assets/domain_gateway_list.html:61 #: assets/templates/assets/domain_list.html:25 #: assets/templates/assets/system_user_detail.html:104 -#: assets/templates/assets/system_user_list.html:34 common/models.py:30 -#: ops/models/adhoc.py:42 perms/models.py:40 perms/models.py:83 -#: perms/templates/perms/asset_permission_detail.html:102 terminal/models.py:26 -#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:13 -#: users/models/user.py:82 users/templates/users/user_detail.html:123 +#: assets/templates/assets/system_user_list.html:34 +#: assets/templates/assets/user_asset_list.html:190 common/models.py:30 +#: ops/models/adhoc.py:42 orgs/models.py:15 perms/models.py:39 +#: perms/models.py:85 perms/templates/perms/asset_permission_detail.html:102 +#: terminal/models.py:27 terminal/templates/terminal/terminal_detail.html:63 +#: users/models/group.py:15 users/models/user.py:84 +#: users/templates/users/user_detail.html:123 #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:14 #: users/templates/users/user_profile.html:130 msgid "Comment" msgstr "备注" -#: assets/models/base.py:24 +#: assets/models/base.py:25 msgid "SSH private key" msgstr "ssh密钥" -#: assets/models/base.py:25 +#: assets/models/base.py:26 msgid "SSH public key" msgstr "ssh公钥" @@ -385,7 +391,7 @@ msgstr "带宽" msgid "Contact" msgstr "联系人" -#: assets/models/cluster.py:22 users/models/user.py:68 +#: assets/models/cluster.py:22 users/models/user.py:70 #: users/templates/users/user_detail.html:76 msgid "Phone" msgstr "手机" @@ -411,7 +417,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:13 -#: users/models/user.py:345 +#: users/models/user.py:360 msgid "System" msgstr "系统" @@ -431,25 +437,25 @@ msgstr "资产组" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:14 audits/models.py:9 +#: assets/models/label.py:14 audits/models.py:11 #: audits/templates/audits/ftp_log_list.html:33 -#: audits/templates/audits/ftp_log_list.html:71 perms/forms.py:14 -#: perms/forms.py:31 perms/models.py:30 +#: audits/templates/audits/ftp_log_list.html:71 perms/forms.py:16 +#: perms/forms.py:41 perms/models.py:29 #: perms/templates/perms/asset_permission_create_update.html:36 #: perms/templates/perms/asset_permission_list.html:54 #: perms/templates/perms/asset_permission_list.html:133 -#: terminal/backends/command/models.py:10 terminal/models.py:126 +#: terminal/backends/command/models.py:12 terminal/models.py:127 #: terminal/templates/terminal/command_list.html:32 #: terminal/templates/terminal/command_list.html:72 #: terminal/templates/terminal/session_list.html:33 -#: terminal/templates/terminal/session_list.html:71 users/forms.py:282 -#: users/models/user.py:31 users/models/user.py:333 +#: terminal/templates/terminal/session_list.html:71 users/forms.py:312 +#: users/models/user.py:33 users/models/user.py:348 #: users/templates/users/user_group_detail.html:78 -#: users/templates/users/user_group_list.html:13 users/views/user.py:367 +#: users/templates/users/user_group_list.html:13 users/views/user.py:380 msgid "User" msgstr "用户" -#: assets/models/label.py:18 assets/models/node.py:16 +#: assets/models/label.py:18 assets/models/node.py:19 #: assets/templates/assets/label_list.html:15 common/models.py:27 msgid "Value" msgstr "值" @@ -458,19 +464,19 @@ msgstr "值" msgid "Category" msgstr "分类" -#: assets/models/node.py:15 +#: assets/models/node.py:18 msgid "Key" msgstr "" -#: assets/models/user.py:108 +#: assets/models/user.py:109 msgid "Automatic login" msgstr "自动登录" -#: assets/models/user.py:109 +#: assets/models/user.py:110 msgid "Manually login" msgstr "手动登录" -#: assets/models/user.py:113 +#: assets/models/user.py:114 #: assets/templates/assets/_asset_group_bulk_update_modal.html:11 #: assets/templates/assets/system_user_asset.html:21 #: assets/views/admin_user.py:29 assets/views/admin_user.py:47 @@ -488,37 +494,37 @@ msgstr "手动登录" msgid "Assets" msgstr "资产管理" -#: assets/models/user.py:114 +#: assets/models/user.py:115 msgid "Priority" msgstr "优先级" -#: assets/models/user.py:116 assets/templates/assets/_system_user.html:59 +#: assets/models/user.py:117 assets/templates/assets/_system_user.html:59 #: assets/templates/assets/system_user_detail.html:122 #: assets/templates/assets/system_user_update.html:10 msgid "Auto push" msgstr "自动推送" -#: assets/models/user.py:117 assets/templates/assets/system_user_detail.html:74 +#: assets/models/user.py:118 assets/templates/assets/system_user_detail.html:74 msgid "Sudo" msgstr "Sudo" -#: assets/models/user.py:118 assets/templates/assets/system_user_detail.html:79 +#: assets/models/user.py:119 assets/templates/assets/system_user_detail.html:79 msgid "Shell" msgstr "Shell" -#: assets/models/user.py:119 assets/templates/assets/system_user_detail.html:66 +#: assets/models/user.py:120 assets/templates/assets/system_user_detail.html:66 #: assets/templates/assets/system_user_list.html:29 msgid "Login mode" msgstr "登录模式" -#: assets/models/user.py:159 audits/models.py:12 -#: audits/templates/audits/ftp_log_list.html:49 -#: audits/templates/audits/ftp_log_list.html:73 perms/forms.py:43 -#: perms/models.py:34 perms/models.py:78 +#: assets/models/user.py:181 assets/templates/assets/user_asset_list.html:187 +#: audits/models.py:14 audits/templates/audits/ftp_log_list.html:49 +#: audits/templates/audits/ftp_log_list.html:73 perms/forms.py:53 +#: perms/models.py:33 perms/models.py:80 #: perms/templates/perms/asset_permission_detail.html:140 #: perms/templates/perms/asset_permission_list.html:58 #: perms/templates/perms/asset_permission_list.html:145 templates/_nav.html:26 -#: terminal/backends/command/models.py:12 terminal/models.py:128 +#: terminal/backends/command/models.py:14 terminal/models.py:129 #: terminal/templates/terminal/command_list.html:48 #: terminal/templates/terminal/command_list.html:74 #: terminal/templates/terminal/session_list.html:49 @@ -674,7 +680,7 @@ msgstr "重置" #: assets/templates/assets/admin_user_create_update.html:46 #: assets/templates/assets/asset_bulk_update.html:24 #: assets/templates/assets/asset_create.html:68 -#: assets/templates/assets/asset_list.html:108 +#: assets/templates/assets/asset_list.html:113 #: assets/templates/assets/asset_update.html:72 #: assets/templates/assets/domain_create_update.html:17 #: assets/templates/assets/gateway_create_update.html:59 @@ -698,6 +704,16 @@ msgstr "重置" msgid "Submit" msgstr "提交" +#: assets/templates/assets/_user_asset_detail_modal.html:7 +#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:198 +msgid "Asset detail" +msgstr "资产详情" + +#: assets/templates/assets/_user_asset_detail_modal.html:20 +#: templates/_modal.html:21 +msgid "Close" +msgstr "关闭" + #: assets/templates/assets/admin_user_assets.html:18 #: assets/templates/assets/admin_user_detail.html:18 #: assets/templates/assets/domain_detail.html:18 @@ -753,7 +769,7 @@ msgstr "测试" #: assets/templates/assets/admin_user_detail.html:24 #: assets/templates/assets/admin_user_list.html:85 #: assets/templates/assets/asset_detail.html:24 -#: assets/templates/assets/asset_list.html:175 +#: assets/templates/assets/asset_list.html:180 #: assets/templates/assets/domain_detail.html:24 #: assets/templates/assets/domain_detail.html:103 #: assets/templates/assets/domain_gateway_list.html:85 @@ -777,7 +793,7 @@ msgstr "更新" #: assets/templates/assets/admin_user_detail.html:28 #: assets/templates/assets/admin_user_list.html:86 #: assets/templates/assets/asset_detail.html:28 -#: assets/templates/assets/asset_list.html:176 +#: assets/templates/assets/asset_list.html:181 #: assets/templates/assets/domain_detail.html:28 #: assets/templates/assets/domain_detail.html:104 #: assets/templates/assets/domain_gateway_list.html:86 @@ -808,7 +824,7 @@ msgstr "选择节点" #: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/asset_detail.html:200 -#: assets/templates/assets/asset_list.html:638 +#: assets/templates/assets/asset_list.html:646 #: assets/templates/assets/system_user_detail.html:195 #: assets/templates/assets/system_user_list.html:139 templates/_modal.html:22 #: terminal/templates/terminal/session_detail.html:108 @@ -841,7 +857,7 @@ msgid "Ratio" msgstr "比例" #: assets/templates/assets/admin_user_list.html:30 -#: assets/templates/assets/asset_list.html:91 +#: assets/templates/assets/asset_list.html:96 #: assets/templates/assets/domain_gateway_list.html:62 #: assets/templates/assets/domain_list.html:26 #: assets/templates/assets/label_list.html:17 @@ -856,10 +872,6 @@ msgstr "比例" msgid "Action" msgstr "动作" -#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:198 -msgid "Asset detail" -msgstr "资产详情" - #: assets/templates/assets/asset_detail.html:85 msgid "CPU" msgstr "CPU" @@ -882,9 +894,9 @@ msgid "Quick modify" msgstr "快速修改" #: assets/templates/assets/asset_detail.html:143 -#: assets/templates/assets/asset_list.html:89 -#: assets/templates/assets/user_asset_list.html:47 perms/models.py:35 -#: perms/models.py:79 +#: assets/templates/assets/asset_list.html:94 +#: assets/templates/assets/user_asset_list.html:47 perms/models.py:34 +#: perms/models.py:81 #: perms/templates/perms/asset_permission_create_update.html:47 #: perms/templates/perms/asset_permission_detail.html:120 #: perms/templates/perms/asset_permission_list.html:59 @@ -912,97 +924,97 @@ msgstr "刷新" msgid "Update successfully!" msgstr "更新成功" -#: assets/templates/assets/asset_list.html:63 assets/views/asset.py:97 +#: assets/templates/assets/asset_list.html:68 assets/views/asset.py:97 msgid "Create asset" msgstr "创建资产" -#: assets/templates/assets/asset_list.html:67 +#: assets/templates/assets/asset_list.html:72 #: users/templates/users/user_list.html:7 msgid "Import" msgstr "导入" -#: assets/templates/assets/asset_list.html:70 +#: assets/templates/assets/asset_list.html:75 #: users/templates/users/user_list.html:10 msgid "Export" msgstr "导出" -#: assets/templates/assets/asset_list.html:88 +#: assets/templates/assets/asset_list.html:93 msgid "Hardware" msgstr "硬件" -#: assets/templates/assets/asset_list.html:100 +#: assets/templates/assets/asset_list.html:105 #: users/templates/users/user_list.html:38 msgid "Delete selected" msgstr "批量删除" -#: assets/templates/assets/asset_list.html:101 +#: assets/templates/assets/asset_list.html:106 #: users/templates/users/user_list.html:39 msgid "Update selected" msgstr "批量更新" -#: assets/templates/assets/asset_list.html:102 +#: assets/templates/assets/asset_list.html:107 msgid "Remove from this node" msgstr "从节点移除" -#: assets/templates/assets/asset_list.html:103 +#: assets/templates/assets/asset_list.html:108 #: users/templates/users/user_list.html:40 msgid "Deactive selected" msgstr "禁用所选" -#: assets/templates/assets/asset_list.html:104 +#: assets/templates/assets/asset_list.html:109 #: users/templates/users/user_list.html:41 msgid "Active selected" msgstr "激活所选" -#: assets/templates/assets/asset_list.html:121 +#: assets/templates/assets/asset_list.html:126 msgid "Add node" msgstr "新建节点" -#: assets/templates/assets/asset_list.html:122 +#: assets/templates/assets/asset_list.html:127 msgid "Rename node" msgstr "重命名节点" -#: assets/templates/assets/asset_list.html:123 +#: assets/templates/assets/asset_list.html:128 msgid "Delete node" msgstr "删除节点" -#: assets/templates/assets/asset_list.html:125 +#: assets/templates/assets/asset_list.html:130 msgid "Add assets to node" msgstr "添加资产到节点" -#: assets/templates/assets/asset_list.html:126 +#: assets/templates/assets/asset_list.html:131 msgid "Move assets to node" msgstr "移动资产到节点" -#: assets/templates/assets/asset_list.html:128 +#: assets/templates/assets/asset_list.html:133 msgid "Refresh node hardware info" msgstr "更新节点资产硬件信息" -#: assets/templates/assets/asset_list.html:129 +#: assets/templates/assets/asset_list.html:134 msgid "Test node connective" msgstr "测试节点资产可连接性" -#: assets/templates/assets/asset_list.html:131 +#: assets/templates/assets/asset_list.html:136 msgid "Display only current node assets" msgstr "仅显示当前节点资产" -#: assets/templates/assets/asset_list.html:132 +#: assets/templates/assets/asset_list.html:137 msgid "Displays all child node assets" msgstr "显示所有子节点资产" -#: assets/templates/assets/asset_list.html:218 +#: assets/templates/assets/asset_list.html:223 msgid "Create node failed" msgstr "创建节点失败" -#: assets/templates/assets/asset_list.html:230 +#: assets/templates/assets/asset_list.html:235 msgid "Have child node, cancel" msgstr "存在子节点,不能删除" -#: assets/templates/assets/asset_list.html:232 +#: assets/templates/assets/asset_list.html:237 msgid "Have assets, cancel" msgstr "存在资产,不能删除" -#: assets/templates/assets/asset_list.html:633 +#: assets/templates/assets/asset_list.html:641 #: assets/templates/assets/system_user_list.html:134 #: users/templates/users/user_detail.html:369 #: users/templates/users/user_detail.html:394 @@ -1012,20 +1024,20 @@ msgstr "存在资产,不能删除" msgid "Are you sure?" msgstr "你确认吗?" -#: assets/templates/assets/asset_list.html:634 +#: assets/templates/assets/asset_list.html:642 msgid "This will delete the selected assets !!!" msgstr "删除选择资产" -#: assets/templates/assets/asset_list.html:642 +#: assets/templates/assets/asset_list.html:650 msgid "Asset Deleted." msgstr "已被删除" -#: assets/templates/assets/asset_list.html:643 -#: assets/templates/assets/asset_list.html:648 +#: assets/templates/assets/asset_list.html:651 +#: assets/templates/assets/asset_list.html:656 msgid "Asset Delete" msgstr "删除" -#: assets/templates/assets/asset_list.html:647 +#: assets/templates/assets/asset_list.html:655 msgid "Asset Deleting failed." msgstr "删除失败" @@ -1219,22 +1231,22 @@ msgstr "资产管理" msgid "System user asset" msgstr "系统用户集群资产" -#: audits/models.py:10 audits/templates/audits/ftp_log_list.html:74 -#: terminal/models.py:130 terminal/templates/terminal/session_list.html:74 +#: audits/models.py:12 audits/templates/audits/ftp_log_list.html:74 +#: terminal/models.py:131 terminal/templates/terminal/session_list.html:74 #: terminal/templates/terminal/terminal_detail.html:47 msgid "Remote addr" msgstr "远端地址" -#: audits/models.py:13 audits/templates/audits/ftp_log_list.html:75 +#: audits/models.py:15 audits/templates/audits/ftp_log_list.html:75 msgid "Operate" msgstr "操作" -#: audits/models.py:14 audits/templates/audits/ftp_log_list.html:56 +#: audits/models.py:16 audits/templates/audits/ftp_log_list.html:56 #: audits/templates/audits/ftp_log_list.html:76 msgid "Filename" msgstr "文件名" -#: audits/models.py:15 audits/templates/audits/ftp_log_list.html:77 +#: audits/models.py:17 audits/templates/audits/ftp_log_list.html:77 #: ops/templates/ops/task_list.html:39 users/models/authentication.py:66 #: users/templates/users/user_detail.html:443 msgid "Success" @@ -1243,17 +1255,17 @@ msgstr "成功" #: audits/templates/audits/ftp_log_list.html:78 #: ops/templates/ops/adhoc_history.html:52 #: ops/templates/ops/adhoc_history_detail.html:61 -#: ops/templates/ops/task_history.html:58 perms/models.py:36 -#: perms/templates/perms/asset_permission_detail.html:86 terminal/models.py:137 +#: ops/templates/ops/task_history.html:58 perms/models.py:35 +#: perms/templates/perms/asset_permission_detail.html:86 terminal/models.py:138 #: terminal/templates/terminal/session_list.html:78 msgid "Date start" msgstr "开始日期" -#: audits/views.py:50 templates/_nav.html:64 +#: audits/views.py:51 templates/_nav.html:68 msgid "Audits" msgstr "日志审计" -#: audits/views.py:51 templates/_nav.html:67 +#: audits/views.py:52 templates/_nav.html:71 msgid "FTP log" msgstr "FTP日志" @@ -1401,7 +1413,7 @@ msgid "Public key auth" msgstr "密钥认证" #: common/forms.py:159 common/templates/common/terminal_setting.html:68 -#: terminal/forms.py:30 terminal/models.py:20 +#: terminal/forms.py:30 terminal/models.py:21 msgid "Command storage" msgstr "命令存储" @@ -1412,7 +1424,7 @@ msgid "" msgstr "设置终端命令存储,default是默认用的存储方式" #: common/forms.py:165 common/templates/common/terminal_setting.html:86 -#: terminal/forms.py:35 terminal/models.py:21 +#: terminal/forms.py:35 terminal/models.py:22 msgid "Replay storage" msgstr "录像存储" @@ -1491,11 +1503,11 @@ msgid "" "characters" msgstr "开启后,用户密码修改、重置必须包含特殊字符" -#: common/mixins.py:29 +#: common/mixins.py:28 msgid "is discard" msgstr "" -#: common/mixins.py:30 +#: common/mixins.py:29 msgid "discard time" msgstr "" @@ -1509,7 +1521,7 @@ msgstr "启用" #: common/templates/common/ldap_setting.html:15 #: common/templates/common/security_setting.html:15 #: common/templates/common/terminal_setting.html:16 -#: common/templates/common/terminal_setting.html:46 common/views.py:22 +#: common/templates/common/terminal_setting.html:46 common/views.py:20 msgid "Basic setting" msgstr "基本设置" @@ -1517,7 +1529,7 @@ msgstr "基本设置" #: common/templates/common/email_setting.html:18 #: common/templates/common/ldap_setting.html:18 #: common/templates/common/security_setting.html:18 -#: common/templates/common/terminal_setting.html:20 common/views.py:48 +#: common/templates/common/terminal_setting.html:20 common/views.py:46 msgid "Email setting" msgstr "邮件设置" @@ -1525,7 +1537,7 @@ msgstr "邮件设置" #: common/templates/common/email_setting.html:21 #: common/templates/common/ldap_setting.html:21 #: common/templates/common/security_setting.html:21 -#: common/templates/common/terminal_setting.html:24 common/views.py:74 +#: common/templates/common/terminal_setting.html:24 common/views.py:72 msgid "LDAP setting" msgstr "LDAP设置" @@ -1533,7 +1545,7 @@ msgstr "LDAP设置" #: common/templates/common/email_setting.html:24 #: common/templates/common/ldap_setting.html:24 #: common/templates/common/security_setting.html:24 -#: common/templates/common/terminal_setting.html:28 common/views.py:104 +#: common/templates/common/terminal_setting.html:28 common/views.py:102 msgid "Terminal setting" msgstr "终端设置" @@ -1541,7 +1553,7 @@ msgstr "终端设置" #: common/templates/common/email_setting.html:27 #: common/templates/common/ldap_setting.html:27 #: common/templates/common/security_setting.html:27 -#: common/templates/common/terminal_setting.html:31 common/views.py:132 +#: common/templates/common/terminal_setting.html:31 common/views.py:130 msgid "Security setting" msgstr "安全设置" @@ -1563,17 +1575,17 @@ msgstr "类型" msgid "Special char not allowed" msgstr "不能包含特殊字符" -#: common/views.py:21 common/views.py:47 common/views.py:73 common/views.py:103 -#: common/views.py:131 templates/_nav.html:81 +#: common/views.py:19 common/views.py:45 common/views.py:71 common/views.py:101 +#: common/views.py:129 templates/_nav.html:86 msgid "Settings" msgstr "系统设置" -#: common/views.py:32 common/views.py:58 common/views.py:86 common/views.py:116 -#: common/views.py:142 +#: common/views.py:30 common/views.py:56 common/views.py:84 common/views.py:114 +#: common/views.py:140 msgid "Update setting successfully, please restart program" msgstr "更新设置成功, 请手动重启程序" -#: ops/api.py:79 +#: ops/api.py:81 msgid "Waiting ..." msgstr "" @@ -1629,36 +1641,36 @@ msgstr "Become" msgid "Create by" msgstr "创建者" -#: ops/models/adhoc.py:323 +#: ops/models/adhoc.py:324 msgid "Start time" msgstr "开始时间" -#: ops/models/adhoc.py:324 +#: ops/models/adhoc.py:325 msgid "End time" msgstr "完成时间" -#: ops/models/adhoc.py:325 ops/templates/ops/adhoc_history.html:57 +#: ops/models/adhoc.py:326 ops/templates/ops/adhoc_history.html:57 #: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:41 msgid "Time" msgstr "时间" -#: ops/models/adhoc.py:326 ops/templates/ops/adhoc_detail.html:106 +#: ops/models/adhoc.py:327 ops/templates/ops/adhoc_detail.html:106 #: ops/templates/ops/adhoc_history.html:55 #: ops/templates/ops/adhoc_history_detail.html:69 #: ops/templates/ops/task_detail.html:83 ops/templates/ops/task_history.html:61 msgid "Is finished" msgstr "是否完成" -#: ops/models/adhoc.py:327 ops/templates/ops/adhoc_history.html:56 +#: ops/models/adhoc.py:328 ops/templates/ops/adhoc_history.html:56 #: ops/templates/ops/task_history.html:62 msgid "Is success" msgstr "是否成功" -#: ops/models/adhoc.py:328 +#: ops/models/adhoc.py:329 msgid "Adhoc raw result" msgstr "结果" -#: ops/models/adhoc.py:329 +#: ops/models/adhoc.py:330 msgid "Adhoc result summary" msgstr "汇总" @@ -1738,7 +1750,7 @@ msgid "Run history detail" msgstr "执行历史详情" #: ops/templates/ops/adhoc_history_detail.html:22 -#: terminal/backends/command/models.py:14 +#: terminal/backends/command/models.py:16 msgid "Output" msgstr "输出" @@ -1808,6 +1820,7 @@ msgstr "内容" #: ops/templates/ops/task_list.html:21 ops/templates/ops/task_list.html:26 #: templates/_base_list.html:43 templates/_header_bar.html:8 #: terminal/templates/terminal/command_list.html:60 +#: terminal/templates/terminal/session_list.html:61 #: users/templates/users/login_log_list.html:35 #: users/templates/users/login_log_list.html:40 msgid "Search" @@ -1835,7 +1848,7 @@ msgstr "任务开始: " msgid "Ops" msgstr "作业中心" -#: ops/views.py:37 templates/_nav.html:59 +#: ops/views.py:37 templates/_nav.html:62 msgid "Task list" msgstr "任务列表" @@ -1843,37 +1856,37 @@ msgstr "任务列表" msgid "Task run history" msgstr "执行历史" -#: perms/forms.py:18 users/forms.py:239 users/forms.py:244 users/forms.py:256 -#: users/forms.py:286 +#: perms/forms.py:20 users/forms.py:265 users/forms.py:270 users/forms.py:282 +#: users/forms.py:316 msgid "Select users" msgstr "选择用户" -#: perms/forms.py:34 perms/models.py:31 perms/models.py:77 +#: perms/forms.py:44 perms/models.py:30 perms/models.py:79 #: perms/templates/perms/asset_permission_list.html:55 #: perms/templates/perms/asset_permission_list.html:136 templates/_nav.html:14 -#: users/models/group.py:23 users/models/user.py:55 +#: users/models/group.py:26 users/models/user.py:57 #: users/templates/users/_select_user_modal.html:16 #: users/templates/users/user_detail.html:200 #: users/templates/users/user_list.html:26 msgid "User group" msgstr "用户组" -#: perms/forms.py:56 +#: perms/forms.py:66 msgid "User or group at least one required" msgstr "" -#: perms/forms.py:65 +#: perms/forms.py:75 msgid "Asset or group at least one required" msgstr "" -#: perms/models.py:37 perms/models.py:80 +#: perms/models.py:36 perms/models.py:82 #: perms/templates/perms/asset_permission_detail.html:90 -#: users/models/user.py:87 users/templates/users/user_detail.html:107 +#: users/models/user.py:89 users/templates/users/user_detail.html:107 #: users/templates/users/user_profile.html:112 msgid "Date expired" msgstr "失效日期" -#: perms/models.py:90 templates/_nav.html:34 +#: perms/models.py:92 templates/_nav.html:34 msgid "Asset permission" msgstr "资产授权" @@ -1963,28 +1976,28 @@ msgstr "添加用户组" msgid "Select user groups" msgstr "选择用户组" -#: perms/views.py:25 perms/views.py:55 perms/views.py:70 perms/views.py:85 -#: perms/views.py:120 perms/views.py:151 templates/_nav.html:31 +#: perms/views.py:23 perms/views.py:53 perms/views.py:68 perms/views.py:83 +#: perms/views.py:118 perms/views.py:150 templates/_nav.html:31 msgid "Perms" msgstr "权限管理" -#: perms/views.py:26 +#: perms/views.py:24 msgid "Asset permission list" msgstr "资产授权列表" -#: perms/views.py:56 +#: perms/views.py:54 msgid "Create asset permission" msgstr "创建权限规则" -#: perms/views.py:71 perms/views.py:86 +#: perms/views.py:69 perms/views.py:84 msgid "Update asset permission" msgstr "更新资产授权" -#: perms/views.py:121 +#: perms/views.py:119 msgid "Asset permission user list" msgstr "资产授权用户列表" -#: perms/views.py:152 +#: perms/views.py:151 msgid "Asset permission asset list" msgstr "资产授权资产列表" @@ -1996,14 +2009,14 @@ msgstr "商业支持" msgid "Docs" msgstr "文档" -#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:122 +#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:148 #: users/templates/users/_user.html:39 #: users/templates/users/first_login.html:39 #: users/templates/users/user_password_update.html:39 #: users/templates/users/user_profile.html:17 #: users/templates/users/user_profile_update.html:37 #: users/templates/users/user_profile_update.html:57 -#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:349 +#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:362 msgid "Profile" msgstr "个人信息" @@ -2054,19 +2067,15 @@ msgstr "" "\"%(user_pubkey_update)s\"> 链接 更新\n" " " -#: templates/_modal.html:21 -msgid "Close" -msgstr "关闭" - -#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44 -#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95 -#: users/views/login.py:332 users/views/login.py:390 users/views/user.py:67 -#: users/views/user.py:82 users/views/user.py:104 users/views/user.py:180 -#: users/views/user.py:336 users/views/user.py:386 users/views/user.py:421 +#: templates/_nav.html:10 users/views/group.py:27 users/views/group.py:43 +#: users/views/group.py:61 users/views/group.py:78 users/views/group.py:94 +#: users/views/login.py:333 users/views/login.py:397 users/views/user.py:68 +#: users/views/user.py:83 users/views/user.py:111 users/views/user.py:193 +#: users/views/user.py:349 users/views/user.py:399 users/views/user.py:434 msgid "Users" msgstr "用户管理" -#: templates/_nav.html:13 users/views/user.py:68 +#: templates/_nav.html:13 users/views/user.py:69 msgid "User list" msgstr "用户列表" @@ -2094,14 +2103,14 @@ msgstr "命令记录" msgid "Web terminal" msgstr "Web终端" -#: templates/_nav.html:51 terminal/views/command.py:49 +#: templates/_nav.html:52 terminal/views/command.py:50 #: terminal/views/session.py:75 terminal/views/session.py:93 #: terminal/views/session.py:115 terminal/views/terminal.py:31 #: terminal/views/terminal.py:46 terminal/views/terminal.py:58 msgid "Terminal" msgstr "终端管理" -#: templates/_nav.html:56 +#: templates/_nav.html:59 msgid "Job Center" msgstr "作业中心" @@ -2117,11 +2126,11 @@ msgstr "验证码" msgid "Filters" msgstr "过滤" -#: terminal/backends/command/models.py:13 +#: terminal/backends/command/models.py:15 msgid "Input" msgstr "输入" -#: terminal/backends/command/models.py:15 +#: terminal/backends/command/models.py:17 #: terminal/templates/terminal/command_list.html:75 #: terminal/templates/terminal/terminal_list.html:33 msgid "Session" @@ -2140,62 +2149,62 @@ msgstr "" "录像文件支持存储到服务器端硬盘、AWS S3、 阿里云 OSS 中,默认存储到服务器端硬" "盘, 更多查看文档" -#: terminal/models.py:17 +#: terminal/models.py:18 msgid "Remote Address" msgstr "远端地址" -#: terminal/models.py:18 +#: terminal/models.py:19 msgid "SSH Port" msgstr "SSH端口" -#: terminal/models.py:19 +#: terminal/models.py:20 msgid "HTTP Port" msgstr "HTTP端口" -#: terminal/models.py:98 +#: terminal/models.py:99 msgid "Session Online" msgstr "在线会话" -#: terminal/models.py:99 +#: terminal/models.py:100 msgid "CPU Usage" msgstr "CPU使用" -#: terminal/models.py:100 +#: terminal/models.py:101 msgid "Memory Used" msgstr "内存使用" -#: terminal/models.py:101 +#: terminal/models.py:102 msgid "Connections" msgstr "连接数" -#: terminal/models.py:102 +#: terminal/models.py:103 msgid "Threads" msgstr "线程数" -#: terminal/models.py:103 +#: terminal/models.py:104 msgid "Boot Time" msgstr "运行时间" -#: terminal/models.py:132 terminal/templates/terminal/session_list.html:104 +#: terminal/models.py:133 terminal/templates/terminal/session_list.html:104 msgid "Replay" msgstr "回放" -#: terminal/models.py:133 terminal/templates/terminal/command_list.html:55 +#: terminal/models.py:134 terminal/templates/terminal/command_list.html:55 #: terminal/templates/terminal/command_list.html:71 #: terminal/templates/terminal/session_detail.html:48 #: terminal/templates/terminal/session_list.html:77 msgid "Command" msgstr "命令" -#: terminal/models.py:136 +#: terminal/models.py:137 msgid "Date last active" msgstr "最后活跃日期" -#: terminal/models.py:138 +#: terminal/models.py:139 msgid "Date end" msgstr "结束日期" -#: terminal/models.py:155 +#: terminal/models.py:156 msgid "Args" msgstr "参数" @@ -2213,7 +2222,7 @@ msgid "Session detail" msgstr "会话详情" #: terminal/templates/terminal/session_detail.html:28 -#: terminal/views/command.py:50 +#: terminal/views/command.py:51 msgid "Command list" msgstr "命令记录列表" @@ -2329,7 +2338,7 @@ msgid "" "You should use your ssh client tools connect terminal: {}

    {}" msgstr "你可以使用ssh客户端工具连接终端" -#: users/api.py:226 users/templates/users/login.html:50 +#: users/api.py:232 users/templates/users/login.html:50 msgid "Log in frequently and try again later" msgstr "登录频繁, 稍后重试" @@ -2384,11 +2393,11 @@ msgstr "" msgid "Invalid token or cache refreshed." msgstr "" -#: users/forms.py:39 +#: users/forms.py:41 msgid "MFA code" msgstr "MFA 验证码" -#: users/forms.py:50 users/models/user.py:59 +#: users/forms.py:52 users/models/user.py:61 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:87 #: users/templates/users/user_list.html:25 @@ -2396,31 +2405,31 @@ msgstr "MFA 验证码" msgid "Role" msgstr "角色" -#: users/forms.py:53 users/forms.py:202 +#: users/forms.py:55 users/forms.py:228 msgid "ssh public key" msgstr "ssh公钥" -#: users/forms.py:54 users/forms.py:203 +#: users/forms.py:56 users/forms.py:229 msgid "ssh-rsa AAAA..." msgstr "" -#: users/forms.py:55 +#: users/forms.py:57 msgid "Paste user id_rsa.pub here." msgstr "复制用户公钥到这里" -#: users/forms.py:73 users/templates/users/user_detail.html:208 +#: users/forms.py:76 users/templates/users/user_detail.html:208 msgid "Join user groups" msgstr "添加到用户组" -#: users/forms.py:84 users/forms.py:217 +#: users/forms.py:110 users/forms.py:243 msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms.py:88 users/forms.py:221 users/serializers.py:48 +#: users/forms.py:114 users/forms.py:247 users/serializers.py:48 msgid "Not a valid ssh public key" msgstr "ssh密钥不合法" -#: users/forms.py:128 +#: users/forms.py:154 msgid "" "Tip: when enabled, you will enter the MFA binding process the next time you " "log in. you can also directly bind in \"personal information -> quick " @@ -2429,17 +2438,17 @@ msgstr "" "提示:启用之后您将会在下次登录时进入MFA绑定流程;您也可以在(个人信息->快速修" "改->更改MFA设置)中直接绑定!" -#: users/forms.py:138 +#: users/forms.py:164 msgid "* Enable MFA authentication to make the account more secure." msgstr "* 启用MFA认证,使账号更加安全." -#: users/forms.py:143 users/models/authentication.py:75 users/models/user.py:71 +#: users/forms.py:169 users/models/authentication.py:75 users/models/user.py:73 #: users/templates/users/first_login.html:45 #: users/templates/users/login_log_list.html:54 msgid "MFA" msgstr "MFA" -#: users/forms.py:148 +#: users/forms.py:174 msgid "" "In order to protect you and your company, please keep your account, password " "and key sensitive information properly. (for example: setting complex " @@ -2448,41 +2457,41 @@ msgstr "" "为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:" "设置复杂密码,启用MFA认证)" -#: users/forms.py:155 users/templates/users/first_login.html:48 +#: users/forms.py:181 users/templates/users/first_login.html:48 #: users/templates/users/first_login.html:107 #: users/templates/users/first_login.html:130 msgid "Finish" msgstr "完成" -#: users/forms.py:161 +#: users/forms.py:187 msgid "Old password" msgstr "原来密码" -#: users/forms.py:166 +#: users/forms.py:192 msgid "New password" msgstr "新密码" -#: users/forms.py:171 +#: users/forms.py:197 msgid "Confirm password" msgstr "确认密码" -#: users/forms.py:181 +#: users/forms.py:207 msgid "Old password error" msgstr "原来密码错误" -#: users/forms.py:189 +#: users/forms.py:215 msgid "Password does not match" msgstr "密码不一致" -#: users/forms.py:200 +#: users/forms.py:226 msgid "Automatically configure and download the SSH key" msgstr "自动配置并下载SSH密钥" -#: users/forms.py:204 +#: users/forms.py:230 msgid "Paste your id_rsa.pub here." msgstr "复制你的公钥到这里" -#: users/forms.py:232 users/models/user.py:79 +#: users/forms.py:258 users/models/user.py:81 #: users/templates/users/first_login.html:42 #: users/templates/users/user_password_update.html:45 #: users/templates/users/user_profile.html:68 @@ -2545,49 +2554,49 @@ msgstr "状态" msgid "Date login" msgstr "登录日期" -#: users/models/user.py:30 users/models/user.py:341 +#: users/models/user.py:32 users/models/user.py:356 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:32 +#: users/models/user.py:34 msgid "Application" msgstr "应用程序" -#: users/models/user.py:35 users/templates/users/user_profile.html:92 +#: users/models/user.py:37 users/templates/users/user_profile.html:92 #: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:166 msgid "Disable" msgstr "禁用" -#: users/models/user.py:36 users/templates/users/user_profile.html:90 +#: users/models/user.py:38 users/templates/users/user_profile.html:90 #: users/templates/users/user_profile.html:170 msgid "Enable" msgstr "启用" -#: users/models/user.py:37 users/templates/users/user_profile.html:88 +#: users/models/user.py:39 users/templates/users/user_profile.html:88 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:51 users/templates/users/user_detail.html:71 +#: users/models/user.py:53 users/templates/users/user_detail.html:71 #: users/templates/users/user_profile.html:59 msgid "Email" msgstr "邮件" -#: users/models/user.py:62 +#: users/models/user.py:64 msgid "Avatar" msgstr "头像" -#: users/models/user.py:65 users/templates/users/user_detail.html:82 +#: users/models/user.py:67 users/templates/users/user_detail.html:82 msgid "Wechat" msgstr "微信" -#: users/models/user.py:94 users/templates/users/user_detail.html:103 +#: users/models/user.py:96 users/templates/users/user_detail.html:103 #: users/templates/users/user_list.html:27 #: users/templates/users/user_profile.html:100 msgid "Source" msgstr "用户来源" -#: users/models/user.py:344 +#: users/models/user.py:359 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -2730,7 +2739,7 @@ msgid "Setting" msgstr "设置" #: users/templates/users/user_create.html:4 -#: users/templates/users/user_list.html:16 users/views/user.py:82 +#: users/templates/users/user_list.html:16 users/views/user.py:83 msgid "Create user" msgstr "创建用户" @@ -2739,7 +2748,7 @@ msgid "Reset link will be generated and sent to the user. " msgstr "生成重置密码连接,通过邮件发送给用户" #: users/templates/users/user_detail.html:19 -#: users/templates/users/user_granted_asset.html:18 users/views/user.py:181 +#: users/templates/users/user_granted_asset.html:18 users/views/user.py:194 msgid "User detail" msgstr "用户详情" @@ -2833,7 +2842,7 @@ msgstr "取消" #: users/templates/users/user_group_detail.html:22 #: users/templates/users/user_group_granted_asset.html:18 -#: users/views/group.py:80 +#: users/views/group.py:79 msgid "User group detail" msgstr "用户组详情" @@ -2841,7 +2850,7 @@ msgstr "用户组详情" msgid "Add user" msgstr "添加用户" -#: users/templates/users/user_group_list.html:5 users/views/group.py:45 +#: users/templates/users/user_group_list.html:5 users/views/group.py:44 msgid "Create user group" msgstr "创建用户组" @@ -2883,8 +2892,8 @@ msgstr "用户删除失败" msgid "Administrator Settings force MFA login" msgstr "管理员设置强制使用MFA登录" -#: users/templates/users/user_profile.html:116 users/views/user.py:211 -#: users/views/user.py:265 +#: users/templates/users/user_profile.html:116 users/views/user.py:224 +#: users/views/user.py:278 msgid "User groups" msgstr "用户组" @@ -2930,10 +2939,34 @@ msgid "" "corresponding private key." msgstr "新的公钥已设置成功,请下载对应的私钥" -#: users/templates/users/user_update.html:4 users/views/user.py:105 +#: users/templates/users/user_update.html:4 users/views/user.py:112 msgid "Update user" msgstr "更新用户" +#: users/templates/users/user_update.html:34 +msgid "Very weak" +msgstr "很弱" + +#: users/templates/users/user_update.html:35 +msgid "Weak" +msgstr "弱" + +#: users/templates/users/user_update.html:36 +msgid "Normal" +msgstr "正常" + +#: users/templates/users/user_update.html:37 +msgid "Medium" +msgstr "一般" + +#: users/templates/users/user_update.html:38 +msgid "Strong" +msgstr "强" + +#: users/templates/users/user_update.html:39 +msgid "Very strong" +msgstr "很强" + #: users/utils.py:41 msgid "Create account successfully" msgstr "创建账户成功" @@ -3072,119 +3105,122 @@ msgstr "密码或密钥不合法" msgid "Bit" msgstr " 位" -#: users/views/group.py:29 +#: users/views/group.py:28 msgid "User group list" msgstr "用户组列表" -#: users/views/group.py:63 +#: users/views/group.py:62 msgid "Update user group" msgstr "更新用户组" -#: users/views/group.py:96 +#: users/views/group.py:95 msgid "User group granted asset" msgstr "用户组授权资产" -#: users/views/login.py:76 +#: users/views/login.py:77 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: users/views/login.py:180 users/views/user.py:506 users/views/user.py:531 +#: users/views/login.py:181 users/views/user.py:519 users/views/user.py:544 msgid "MFA code invalid" msgstr "MFA码认证失败" -#: users/views/login.py:209 +#: users/views/login.py:210 msgid "Logout success" msgstr "退出登录成功" -#: users/views/login.py:210 +#: users/views/login.py:211 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: users/views/login.py:226 +#: users/views/login.py:227 msgid "Email address invalid, please input again" msgstr "邮箱地址错误,重新输入" -#: users/views/login.py:239 +#: users/views/login.py:240 msgid "Send reset password message" msgstr "发送重置密码邮件" -#: users/views/login.py:240 +#: users/views/login.py:241 msgid "Send reset password mail success, login your mail box and follow it " msgstr "" "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)" -#: users/views/login.py:253 +#: users/views/login.py:254 msgid "Reset password success" msgstr "重置密码成功" -#: users/views/login.py:254 +#: users/views/login.py:255 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: users/views/login.py:275 users/views/login.py:288 +#: users/views/login.py:276 users/views/login.py:289 msgid "Token invalid or expired" msgstr "Token错误或失效" -#: users/views/login.py:284 +#: users/views/login.py:285 msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:294 users/views/user.py:120 users/views/user.py:404 +#: users/views/login.py:295 users/views/user.py:127 users/views/user.py:417 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/views/login.py:332 +#: users/views/login.py:333 msgid "First login" msgstr "首次登陆" -#: users/views/login.py:391 +#: users/views/login.py:398 msgid "Login log list" msgstr "登录日志" -#: users/views/user.py:131 +#: users/views/user.py:144 msgid "Bulk update user success" msgstr "批量更新用户成功" -#: users/views/user.py:240 +#: users/views/user.py:253 msgid "Invalid file." msgstr "文件不合法" -#: users/views/user.py:337 +#: users/views/user.py:350 msgid "User granted assets" msgstr "用户授权资产" -#: users/views/user.py:368 +#: users/views/user.py:381 msgid "Profile setting" msgstr "个人信息设置" -#: users/views/user.py:387 +#: users/views/user.py:400 msgid "Password update" msgstr "密码更新" -#: users/views/user.py:422 +#: users/views/user.py:435 msgid "Public key update" msgstr "密钥更新" -#: users/views/user.py:463 +#: users/views/user.py:476 msgid "Password invalid" msgstr "用户名或密码无效" -#: users/views/user.py:557 +#: users/views/user.py:570 msgid "MFA enable success" msgstr "MFA 绑定成功" -#: users/views/user.py:558 +#: users/views/user.py:571 msgid "MFA enable success, return login page" msgstr "MFA 绑定成功,返回到登录页面" -#: users/views/user.py:560 +#: users/views/user.py:573 msgid "MFA disable success" msgstr "MFA 解绑成功" -#: users/views/user.py:561 +#: users/views/user.py:574 msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" +#~ msgid "* required Must set exact system platform, Windows, Linux ..." +#~ msgstr "* required 必须准确设置操作系统平台,如Windows, Linux ..." + #~ msgid "Unblock user successfully. " #~ msgstr "解除登录限制成功" diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index dfe696f36..72128fa2b 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -685,7 +685,7 @@ function popoverPasswordRules(password_check_rules, $el) { } // 初始化弹窗popover -function initPopover($container, $progress, $idPassword, $el, password_check_rules){ +function initPopover($container, $progress, $idPassword, $el, password_check_rules, i18n_fallback){ options = {}; // User Interface options.ui = { @@ -697,6 +697,14 @@ function initPopover($container, $progress, $idPassword, $el, password_check_rul showProgressbar: true, showVerdictsInsideProgressBar: true }; + options.i18n = { + fallback: i18n_fallback, + t: function (key) { + var result = ''; + result = options.i18n.fallback[key]; + return result === key ? '' : result; + } + }; $idPassword.pwstrength(options); popoverPasswordRules(password_check_rules, $el); } diff --git a/apps/users/forms.py b/apps/users/forms.py index 2ffc11b68..d1bcf44b5 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -78,6 +78,30 @@ class UserCreateUpdateForm(OrgModelForm): ) } + def __init__(self, *args, **kwargs): + self.request = kwargs.pop("request", None) + super(UserCreateUpdateForm, self).__init__(*args, **kwargs) + + roles = [] + # Super admin user + if self.request.user.is_superuser: + roles.append((User.ROLE_ADMIN, dict(User.ROLE_CHOICES).get(User.ROLE_ADMIN))) + roles.append((User.ROLE_USER, dict(User.ROLE_CHOICES).get(User.ROLE_USER))) + + # Org admin user + else: + user = kwargs.get('instance') + # Update + if user: + role = kwargs.get('instance').role + roles.append((role, dict(User.ROLE_CHOICES).get(role))) + # Create + else: + roles.append((User.ROLE_USER, dict(User.ROLE_CHOICES).get(User.ROLE_USER))) + + field = self.fields['role'] + field.choices = set(roles) + def clean_public_key(self): public_key = self.cleaned_data['public_key'] if not public_key: diff --git a/apps/users/templates/users/reset_password.html b/apps/users/templates/users/reset_password.html index 3d669a1e5..7071f4cac 100644 --- a/apps/users/templates/users/reset_password.html +++ b/apps/users/templates/users/reset_password.html @@ -100,10 +100,18 @@ progress = $('#id_progress'), password_check_rules = {{ password_check_rules|safe }}, minLength = {{ min_length }}, - top = 146, left = 170; + top = 146, left = 170, + i18n_fallback = { + "veryWeak": "{% trans 'Very weak' %}", + "weak": "{% trans 'Weak' %}", + "normal": "{% trans 'Normal' %}", + "medium": "{% trans 'Medium' %}", + "strong": "{% trans 'Strong' %}", + "veryStrong": "{% trans 'Very strong' %}" + }; // 初始化popover - initPopover(container, progress, idPassword, el, password_check_rules); + initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback); // 监听事件 idPassword.on('focus', function () { diff --git a/apps/users/templates/users/user_password_update.html b/apps/users/templates/users/user_password_update.html index 50c428ee6..2943665f2 100644 --- a/apps/users/templates/users/user_password_update.html +++ b/apps/users/templates/users/user_password_update.html @@ -93,10 +93,18 @@ password_check_rules = {{ password_check_rules|safe }}, minLength = {{ min_length }}, top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) - 10 + 34, - left = 377; + left = 377, + i18n_fallback = { + "veryWeak": "{% trans 'Very weak' %}", + "weak": "{% trans 'Weak' %}", + "normal": "{% trans 'Normal' %}", + "medium": "{% trans 'Medium' %}", + "strong": "{% trans 'Strong' %}", + "veryStrong": "{% trans 'Very strong' %}" + }; // 初始化popover - initPopover(container, progress, idPassword, el, password_check_rules); + initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback); // 监听事件 idPassword.on('focus', function () { diff --git a/apps/users/templates/users/user_update.html b/apps/users/templates/users/user_update.html index b51610587..5026b090d 100644 --- a/apps/users/templates/users/user_update.html +++ b/apps/users/templates/users/user_update.html @@ -29,10 +29,18 @@ password_check_rules = {{ password_check_rules|safe }}, minLength = {{ min_length }}, top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) - 10 + 34, - left = 377; + left = 377, + i18n_fallback = { + "veryWeak": "{% trans 'Very weak' %}", + "weak": "{% trans 'Weak' %}", + "normal": "{% trans 'Normal' %}", + "medium": "{% trans 'Medium' %}", + "strong": "{% trans 'Strong' %}", + "veryStrong": "{% trans 'Very strong' %}" + }; // 初始化popover - initPopover(container, progress, idPassword, el, password_check_rules); + initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback); // 监听事件 idPassword.on('focus', function () { diff --git a/apps/users/views/user.py b/apps/users/views/user.py index 624ba3786..cb0b45bad 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -90,6 +90,12 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): post_user_create.send(self.__class__, user=user) return super().form_valid(form) + def get_form_kwargs(self): + kwargs = super(UserCreateView, self).get_form_kwargs() + data = {'request': self.request} + kwargs.update(data) + return kwargs + class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView): model = User @@ -123,6 +129,12 @@ class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView): return self.form_invalid(form) return super().form_valid(form) + def get_form_kwargs(self): + kwargs = super(UserUpdateView, self).get_form_kwargs() + data = {'request': self.request} + kwargs.update(data) + return kwargs + class UserBulkUpdateView(AdminUserRequiredMixin, TemplateView): model = User From 183ff09530fe4f86895139e9dc2f795ceb1a6a47 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 1 Aug 2018 12:35:48 +0800 Subject: [PATCH 47/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9token?= =?UTF-8?q?=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/api.py | 10 +++++++++- apps/users/hands.py | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/users/api.py b/apps/users/api.py index 840b8c913..2f41de361 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -3,6 +3,7 @@ import uuid from django.core.cache import cache from django.urls import reverse +from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext as _ from rest_framework import generics @@ -20,6 +21,7 @@ from .permissions import IsSuperUser, IsValidUser, IsCurrentUserOrReadOnly, \ IsSuperUserOrAppUser from .utils import check_user_valid, generate_token, get_login_ip, \ check_otp_code, set_user_login_failed_count_to_cache, is_block_login +from .hands import Asset, SystemUser from common.mixins import IDInFilterMixin from common.utils import get_logger @@ -305,10 +307,16 @@ class UserConnectionTokenApi(APIView): asset_id = request.data.get('asset', '') system_user_id = request.data.get('system_user', '') token = str(uuid.uuid4()) + user = get_object_or_404(User, id=user_id) + asset = get_object_or_404(Asset, id=asset_id) + system_user = get_object_or_404(SystemUser, id=system_user_id) value = { 'user': user_id, + 'username': user.username, 'asset': asset_id, - 'system_user': system_user_id + 'hostname': asset.hostname, + 'system_user': system_user_id, + 'system_user_name': system_user.name } cache.set(token, value, timeout=20) return Response({"token": token}, status=201) diff --git a/apps/users/hands.py b/apps/users/hands.py index 56435a5a4..0792fa099 100644 --- a/apps/users/hands.py +++ b/apps/users/hands.py @@ -15,3 +15,4 @@ # from users.models import User # from perms.models import AssetPermission # from perms.utils import get_user_granted_assets, get_user_granted_asset_groups +from assets.models import Asset, SystemUser From 87e0e1f2c4ddb8ba052e3dc32e3c6096d9949002 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 1 Aug 2018 13:06:50 +0800 Subject: [PATCH 48/63] =?UTF-8?q?[Update]=20Asset=E8=BF=94=E5=9B=9Eorg=20n?= =?UTF-8?q?ame?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/asset.py | 6 ++++++ apps/assets/serializers/asset.py | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 3219ca75b..97bcd4b47 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -161,6 +161,12 @@ class Asset(OrgModelMixin): nodes = list(reduce(lambda x, y: set(x) | set(y), nodes)) return nodes + @property + def org_name(self): + from orgs.models import Organization + org = Organization.get_instance(self.org_id) + return org.name + @property def hardware_info(self): if self.cpu_count: diff --git a/apps/assets/serializers/asset.py b/apps/assets/serializers/asset.py index 4c648c0ab..36639a17e 100644 --- a/apps/assets/serializers/asset.py +++ b/apps/assets/serializers/asset.py @@ -25,7 +25,7 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer): def get_field_names(self, declared_fields, info): fields = super().get_field_names(declared_fields, info) fields.extend([ - 'hardware_info', 'is_connective', + 'hardware_info', 'is_connective', 'org_name' ]) return fields @@ -43,7 +43,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer): fields = ( "id", "hostname", "ip", "port", "system_users_granted", "is_active", "system_users_join", "os", 'domain', - "platform", "comment", "protocol", "org_id", + "platform", "comment", "protocol", "org_id", "org_name", ) @staticmethod @@ -61,6 +61,6 @@ class MyAssetGrantedSerializer(AssetGrantedSerializer): model = Asset fields = ( "id", "hostname", "system_users_granted", - "is_active", "system_users_join", - "os", "platform", "comment", + "is_active", "system_users_join", "org_name", + "os", "platform", "comment", "org_id", "protocol" ) From 9c4ebf9c75fb7e786e7bdb835ea16bfc548feae9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 2 Aug 2018 12:36:31 +0800 Subject: [PATCH 49/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E6=94=AF?= =?UTF-8?q?=E6=8C=81xpack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + apps/jumpserver/settings.py | 29 ++++++++++++++++++++++++++++- apps/jumpserver/urls.py | 5 +++++ apps/templates/_nav.html | 12 ++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 488f8a776..b75f0c9ff 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ django.db celerybeat-schedule.db data/static docs/_build/ +xpack diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index ff69c9621..49b2f7870 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -78,6 +78,12 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', ] + +XPACK_DIR = os.path.join(BASE_DIR, 'xpack') +XPACK_ENABLED = os.path.isdir(XPACK_DIR) +if XPACK_ENABLED: + INSTALLED_APPS.append('xpack.apps.XpackConfig') + MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -94,10 +100,30 @@ MIDDLEWARE = [ ROOT_URLCONF = 'jumpserver.urls' + +def get_xpack_context_processor(): + if XPACK_ENABLED: + return ['xpack.context_processor.xpack_processor'] + return [] + + +def get_xpack_templates_dir(): + if XPACK_ENABLED: + dirs = [] + from xpack.utils import find_enabled_plugins + for i in find_enabled_plugins(): + template_dir = os.path.join(BASE_DIR, 'xpack', 'plugins', i, 'templates') + if os.path.isdir(template_dir): + dirs.append(template_dir) + return dirs + else: + return [] + + TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates'), ], + 'DIRS': [os.path.join(BASE_DIR, 'templates'), *get_xpack_templates_dir()], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -111,6 +137,7 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.template.context_processors.media', 'orgs.context_processor.org_processor', + *get_xpack_context_processor(), ], }, }, diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index 06034865c..b0c30b575 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -1,6 +1,7 @@ # ~*~ coding: utf-8 ~*~ from __future__ import unicode_literals import re +import os from django.urls import path, include, re_path from django.conf import settings @@ -74,6 +75,10 @@ app_view_patterns = [ path('orgs/', include('orgs.urls.views_urls', namespace='orgs')), ] +XPACK_DIR = os.path.join(settings.BASE_DIR, 'xpack') +if os.path.isdir(XPACK_DIR): + app_view_patterns.append(path('xpack/', include('xpack.urls', namespace='xpack'))) + urlpatterns = [ path('', IndexView.as_view(), name='index'), diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html index 772c9db78..be9633054 100644 --- a/apps/templates/_nav.html +++ b/apps/templates/_nav.html @@ -80,6 +80,18 @@ {#
  • {% trans 'File download' %}
  • #} {# #} {##} +{% if XPACK_ENABLED %} +
  • + + {% trans 'XPack' %} + + +
  • +{% endif %} {% if request.user.is_superuser %}
  • From 90c629c837da4e490335f1311ec23eec5705596e Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 3 Aug 2018 10:25:41 +0800 Subject: [PATCH 50/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/urls.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index b0c30b575..636bcb938 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -30,7 +30,6 @@ schema_view = get_schema_view( ) api_url_pattern = re.compile(r'^/api/(?P\w+)/(?P\w+)/(?P.*)$') - class HttpResponseTemporaryRedirect(HttpResponse): status_code = 307 @@ -75,9 +74,8 @@ app_view_patterns = [ path('orgs/', include('orgs.urls.views_urls', namespace='orgs')), ] -XPACK_DIR = os.path.join(settings.BASE_DIR, 'xpack') -if os.path.isdir(XPACK_DIR): - app_view_patterns.append(path('xpack/', include('xpack.urls', namespace='xpack'))) +if settings.XPACK_ENABLED: + app_view_patterns.append(path('xpack/', include('xpack.urls.views_urls', namespace='xpack'))) urlpatterns = [ From c01936facc1390f76ed01397d73910d714d321b3 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 3 Aug 2018 10:32:02 +0800 Subject: [PATCH 51/63] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E4=B8=8D?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E5=B0=B1=E8=83=BD=E6=9F=A5=E7=9C=8B=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/views/asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py index 273323193..2a50a4879 100644 --- a/apps/assets/views/asset.py +++ b/apps/assets/views/asset.py @@ -186,7 +186,7 @@ class AssetDeleteView(AdminUserRequiredMixin, DeleteView): success_url = reverse_lazy('assets:asset-list') -class AssetDetailView(DetailView): +class AssetDetailView(LoginRequiredMixin, DetailView): model = Asset context_object_name = 'asset' template_name = 'assets/asset_detail.html' @@ -203,7 +203,7 @@ class AssetDetailView(DetailView): @method_decorator(csrf_exempt, name='dispatch') -class AssetExportView(View): +class AssetExportView(LoginRequiredMixin, View): def get(self, request): spm = request.GET.get('spm', '') assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else [] From b8ecb6f81d4d08d8a34789df53c005055434a6b1 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 6 Aug 2018 10:24:51 +0800 Subject: [PATCH 52/63] =?UTF-8?q?[Update]=20=E7=94=A8=E6=88=B7=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/views/asset.py | 4 ++-- apps/users/api.py | 10 +++++++++- apps/users/hands.py | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/assets/views/asset.py b/apps/assets/views/asset.py index ba490818d..5b136aa79 100644 --- a/apps/assets/views/asset.py +++ b/apps/assets/views/asset.py @@ -186,7 +186,7 @@ class AssetDeleteView(AdminUserRequiredMixin, DeleteView): success_url = reverse_lazy('assets:asset-list') -class AssetDetailView(DetailView): +class AssetDetailView(LoginRequiredMixin, DetailView): model = Asset context_object_name = 'asset' template_name = 'assets/asset_detail.html' @@ -203,7 +203,7 @@ class AssetDetailView(DetailView): @method_decorator(csrf_exempt, name='dispatch') -class AssetExportView(View): +class AssetExportView(LoginRequiredMixin, View): def get(self, request): spm = request.GET.get('spm', '') assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else [] diff --git a/apps/users/api.py b/apps/users/api.py index 89ab0d129..5afc1b4c0 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -3,6 +3,7 @@ import uuid from django.core.cache import cache from django.urls import reverse +from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext as _ from rest_framework import generics @@ -20,6 +21,7 @@ from .utils import check_user_valid, generate_token, get_login_ip, \ check_otp_code, set_user_login_failed_count_to_cache, is_block_login from orgs.utils import current_org from common.permissions import IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser +from .hands import Asset, SystemUser from common.mixins import IDInFilterMixin from common.utils import get_logger @@ -311,10 +313,16 @@ class UserConnectionTokenApi(APIView): asset_id = request.data.get('asset', '') system_user_id = request.data.get('system_user', '') token = str(uuid.uuid4()) + user = get_object_or_404(User, id=user_id) + asset = get_object_or_404(Asset, id=asset_id) + system_user = get_object_or_404(SystemUser, id=system_user_id) value = { 'user': user_id, + 'username': user.username, 'asset': asset_id, - 'system_user': system_user_id + 'hostname': asset.hostname, + 'system_user': system_user_id, + 'system_user_name': system_user.name } cache.set(token, value, timeout=20) return Response({"token": token}, status=201) diff --git a/apps/users/hands.py b/apps/users/hands.py index 56435a5a4..0792fa099 100644 --- a/apps/users/hands.py +++ b/apps/users/hands.py @@ -15,3 +15,4 @@ # from users.models import User # from perms.models import AssetPermission # from perms.utils import get_user_granted_assets, get_user_granted_asset_groups +from assets.models import Asset, SystemUser From fc156e23f3a702e168eb6ae1cbba9efeec1b826e Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 6 Aug 2018 10:39:22 +0800 Subject: [PATCH 53/63] [Bugfix] ... --- apps/jumpserver/urls.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index 636bcb938..f48041ef1 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -30,6 +30,7 @@ schema_view = get_schema_view( ) api_url_pattern = re.compile(r'^/api/(?P\w+)/(?P\w+)/(?P.*)$') + class HttpResponseTemporaryRedirect(HttpResponse): status_code = 307 @@ -75,7 +76,7 @@ app_view_patterns = [ ] if settings.XPACK_ENABLED: - app_view_patterns.append(path('xpack/', include('xpack.urls.views_urls', namespace='xpack'))) + app_view_patterns.append(path('xpack/', include('xpack.urls', namespace='xpack'))) urlpatterns = [ From b0aa9f197ae7341e27e450288d2357ed9cae5138 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 6 Aug 2018 12:20:27 +0800 Subject: [PATCH 54/63] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E5=A4=8D=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/assets/_user_asset_detail_modal.html | 9 ++++++--- apps/assets/templates/assets/domain_list.html | 4 ++-- apps/assets/templates/assets/user_asset_list.html | 2 +- apps/jumpserver/views.py | 8 +++++--- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/assets/templates/assets/_user_asset_detail_modal.html b/apps/assets/templates/assets/_user_asset_detail_modal.html index b78854d2e..ca2b8f252 100644 --- a/apps/assets/templates/assets/_user_asset_detail_modal.html +++ b/apps/assets/templates/assets/_user_asset_detail_modal.html @@ -1,19 +1,22 @@ {% extends '_modal.html' %} {% load i18n %} {% load static %} - + {% block modal_id %}user_asset_detail_modal{% endblock %} {% block modal_title %}{% trans "Asset detail" %}{% endblock %} {% block modal_body %} -
    +
    - {% endblock %} {% block modal_button %} diff --git a/apps/assets/templates/assets/domain_list.html b/apps/assets/templates/assets/domain_list.html index a4042d57e..f16bc7f7f 100644 --- a/apps/assets/templates/assets/domain_list.html +++ b/apps/assets/templates/assets/domain_list.html @@ -4,8 +4,8 @@ {% block help_message %}
    - 网域功能是为了解决部分环境(如:混合云)无法直接连接而新增的功能,原理是通过网关服务器进行跳转登 -录。 + 网域功能是为了解决部分环境(如:混合云)无法直接连接而新增的功能,原理是通过网关服务器进行跳转登录。
    + JMS => 网域网关 => 目标资产
    {% endblock %} diff --git a/apps/assets/templates/assets/user_asset_list.html b/apps/assets/templates/assets/user_asset_list.html index a40cc9e52..e1284b7a7 100644 --- a/apps/assets/templates/assets/user_asset_list.html +++ b/apps/assets/templates/assets/user_asset_list.html @@ -193,7 +193,7 @@ $(document).ready(function () { $.each(data, function(index, value){ if(value.id === asset_id){ for(var i in desc){ - trs += "\n" + + trs += "\n" + ""+ desc[i] + ":"+ ""+ (value[i] === null?'':value[i]) + "\n" + ""; diff --git a/apps/jumpserver/views.py b/apps/jumpserver/views.py index a924c5ef5..7ec6c7774 100644 --- a/apps/jumpserver/views.py +++ b/apps/jumpserver/views.py @@ -5,15 +5,15 @@ from django.views.generic import TemplateView, View from django.utils import timezone from django.db.models import Count from django.shortcuts import redirect +from django.contrib.auth.mixins import LoginRequiredMixin from users.models import User from assets.models import Asset from terminal.models import Session -from common.permissions import AdminUserRequiredMixin from orgs.utils import current_org -class IndexView(AdminUserRequiredMixin, TemplateView): +class IndexView(LoginRequiredMixin, TemplateView): template_name = 'index.html' session_week = None @@ -22,7 +22,9 @@ class IndexView(AdminUserRequiredMixin, TemplateView): session_month_dates_archive = [] def dispatch(self, request, *args, **kwargs): - if not request.user.is_authenticated or not request.user.is_org_admin: + if not request.user.is_authenticated: + return self.handle_no_permission() + if not request.user.is_org_admin: return redirect('assets:user-asset-list') return super(IndexView, self).dispatch(request, *args, **kwargs) From f5531b6065662431ee8e5ddea2a988cd9b36b493 Mon Sep 17 00:00:00 2001 From: BaiJiangJie <32935519+BaiJiangJie@users.noreply.github.com> Date: Mon, 6 Aug 2018 16:08:05 +0800 Subject: [PATCH 55/63] =?UTF-8?q?=20[Update]=20xpack/orgs=E7=BB=84?= =?UTF-8?q?=E7=BB=87=E6=B7=BB=E5=8A=A0=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD?= =?UTF-8?q?-js;=20=E4=BF=AE=E5=A4=8DLabel=E7=BB=A7=E6=89=BFOrg=E5=90=8Ebug?= =?UTF-8?q?;=20(#1644)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Update] 更新xpack下orgs的翻译信息 * [Update] 更新model Label,继承OrgModelMixin; * [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug; * [Bugfix] 修复小bug --- apps/assets/api/label.py | 5 +- apps/assets/models/label.py | 3 +- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 37050 -> 37547 bytes apps/i18n/zh/LC_MESSAGES/django.po | 152 ++++++++++++++---- apps/static/js/jumpserver.js | 36 +++++ .../users/user_otp_authentication.html | 2 +- 6 files changed, 167 insertions(+), 31 deletions(-) diff --git a/apps/assets/api/label.py b/apps/assets/api/label.py index 3466c3ec3..e5391c76a 100644 --- a/apps/assets/api/label.py +++ b/apps/assets/api/label.py @@ -27,7 +27,6 @@ __all__ = ['LabelViewSet'] class LabelViewSet(BulkModelViewSet): - queryset = Label.objects.annotate(asset_count=Count("assets")) permission_classes = (IsOrgAdmin,) serializer_class = serializers.LabelSerializer @@ -36,3 +35,7 @@ class LabelViewSet(BulkModelViewSet): self.serializer_class = serializers.LabelDistinctSerializer self.queryset = self.queryset.values("name").distinct() return super().list(request, *args, **kwargs) + + def get_queryset(self): + self.queryset = Label.objects.annotate(asset_count=Count("assets")) + return self.queryset diff --git a/apps/assets/models/label.py b/apps/assets/models/label.py index 41f0f8724..7f1d08fa1 100644 --- a/apps/assets/models/label.py +++ b/apps/assets/models/label.py @@ -4,9 +4,10 @@ import uuid from django.db import models from django.utils.translation import ugettext_lazy as _ +from orgs.mixins import OrgModelMixin -class Label(models.Model): +class Label(OrgModelMixin): SYSTEM_CATEGORY = "S" USER_CATEGORY = "U" CATEGORY_CHOICES = ( diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index 903763d9bba7f3375f3f335e30acdc234491726a..da76112fc347e87489519ac854b2c5ef457817c3 100644 GIT binary patch delta 12952 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 Ct*O8O diff --git a/apps/i18n/zh/LC_MESSAGES/django.po b/apps/i18n/zh/LC_MESSAGES/django.po index 12310b65f..46cf77ef9 100644 --- a/apps/i18n/zh/LC_MESSAGES/django.po +++ b/apps/i18n/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Jumpserver 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-07-31 19:18+0800\n" +"POT-Creation-Date: 2018-08-03 15:14+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -96,7 +96,7 @@ msgid "Port" msgstr "端口" #: assets/forms/domain.py:15 assets/forms/label.py:13 -#: assets/models/asset.py:225 assets/templates/assets/admin_user_list.html:25 +#: assets/models/asset.py:231 assets/templates/assets/admin_user_list.html:25 #: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_list.html:23 #: assets/templates/assets/label_list.html:16 @@ -142,6 +142,9 @@ msgstr "资产" #: users/templates/users/user_list.html:23 #: users/templates/users/user_profile.html:51 #: users/templates/users/user_pubkey_update.html:53 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:52 +#: xpack/plugins/orgs/templates/orgs/org_list.html:12 +#: xpack/templates/orgs/org_list.html:12 msgid "Name" msgstr "名称" @@ -349,6 +352,7 @@ msgstr "创建者" #: perms/templates/perms/asset_permission_detail.html:94 #: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17 #: users/templates/users/user_group_detail.html:63 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:60 msgid "Date created" msgstr "创建日期" @@ -372,6 +376,9 @@ msgstr "创建日期" #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:14 #: users/templates/users/user_profile.html:130 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:64 +#: xpack/plugins/orgs/templates/orgs/org_list.html:15 +#: xpack/templates/orgs/org_list.html:14 msgid "Comment" msgstr "备注" @@ -452,6 +459,10 @@ msgstr "默认资产组" #: users/models/user.py:33 users/models/user.py:348 #: users/templates/users/user_group_detail.html:78 #: users/templates/users/user_group_list.html:13 users/views/user.py:380 +#: xpack/plugins/orgs/forms.py:26 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:113 +#: xpack/plugins/orgs/templates/orgs/org_list.html:14 +#: xpack/templates/orgs/org_list.html:13 msgid "User" msgstr "用户" @@ -787,6 +798,9 @@ msgstr "测试" #: users/templates/users/user_list.html:77 #: users/templates/users/user_profile.html:151 #: users/templates/users/user_profile.html:180 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:25 +#: xpack/plugins/orgs/templates/orgs/org_list.html:48 +#: xpack/templates/orgs/org_list.html:43 msgid "Update" msgstr "更新" @@ -810,6 +824,9 @@ msgstr "更新" #: users/templates/users/user_group_list.html:45 #: users/templates/users/user_list.html:81 #: users/templates/users/user_list.html:85 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:29 +#: xpack/plugins/orgs/templates/orgs/org_list.html:50 +#: xpack/templates/orgs/org_list.html:45 msgid "Delete" msgstr "删除" @@ -836,6 +853,8 @@ msgstr "选择节点" #: users/templates/users/user_group_list.html:86 #: users/templates/users/user_list.html:200 #: users/templates/users/user_profile.html:222 +#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33 +#: xpack/templates/orgs/org_list.html:86 msgid "Confirm" msgstr "确认" @@ -869,6 +888,8 @@ msgstr "比例" #: terminal/templates/terminal/terminal_list.html:36 #: users/templates/users/user_group_list.html:15 #: users/templates/users/user_list.html:29 +#: xpack/plugins/orgs/templates/orgs/org_list.html:16 +#: xpack/templates/orgs/org_list.html:15 msgid "Action" msgstr "动作" @@ -1021,6 +1042,7 @@ msgstr "存在资产,不能删除" #: users/templates/users/user_detail.html:461 #: users/templates/users/user_group_list.html:81 #: users/templates/users/user_list.html:195 +#: xpack/templates/orgs/org_list.html:81 msgid "Are you sure?" msgstr "你确认吗?" @@ -1576,7 +1598,7 @@ msgid "Special char not allowed" msgstr "不能包含特殊字符" #: common/views.py:19 common/views.py:45 common/views.py:71 common/views.py:101 -#: common/views.py:129 templates/_nav.html:86 +#: common/views.py:129 templates/_nav.html:98 msgid "Settings" msgstr "系统设置" @@ -1638,6 +1660,7 @@ msgid "Become" msgstr "Become" #: ops/models/adhoc.py:161 users/templates/users/user_group_detail.html:59 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:56 msgid "Create by" msgstr "创建者" @@ -1857,7 +1880,7 @@ msgid "Task run history" msgstr "执行历史" #: perms/forms.py:20 users/forms.py:265 users/forms.py:270 users/forms.py:282 -#: users/forms.py:316 +#: users/forms.py:316 xpack/plugins/orgs/forms.py:30 msgid "Select users" msgstr "选择用户" @@ -1911,6 +1934,8 @@ msgstr "添加资产" #: perms/templates/perms/asset_permission_user.html:97 #: perms/templates/perms/asset_permission_user.html:125 #: users/templates/users/user_group_detail.html:95 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:93 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:130 msgid "Add" msgstr "添加" @@ -2114,6 +2139,10 @@ msgstr "终端管理" msgid "Job Center" msgstr "作业中心" +#: templates/_nav.html:86 +msgid "XPack" +msgstr "" + #: templates/captcha/image.html:3 msgid "Play CAPTCHA as audio file" msgstr "语言播放验证码" @@ -2738,6 +2767,42 @@ msgstr "再次输入密码" msgid "Setting" msgstr "设置" +#: users/templates/users/reset_password.html:105 +#: users/templates/users/user_password_update.html:98 +#: users/templates/users/user_update.html:34 +msgid "Very weak" +msgstr "很弱" + +#: users/templates/users/reset_password.html:106 +#: users/templates/users/user_password_update.html:99 +#: users/templates/users/user_update.html:35 +msgid "Weak" +msgstr "弱" + +#: users/templates/users/reset_password.html:107 +#: users/templates/users/user_password_update.html:100 +#: users/templates/users/user_update.html:36 +msgid "Normal" +msgstr "正常" + +#: users/templates/users/reset_password.html:108 +#: users/templates/users/user_password_update.html:101 +#: users/templates/users/user_update.html:37 +msgid "Medium" +msgstr "一般" + +#: users/templates/users/reset_password.html:109 +#: users/templates/users/user_password_update.html:102 +#: users/templates/users/user_update.html:38 +msgid "Strong" +msgstr "强" + +#: users/templates/users/reset_password.html:110 +#: users/templates/users/user_password_update.html:103 +#: users/templates/users/user_update.html:39 +msgid "Very strong" +msgstr "很强" + #: users/templates/users/user_create.html:4 #: users/templates/users/user_list.html:16 users/views/user.py:83 msgid "Create user" @@ -2837,6 +2902,7 @@ msgid "After unlocking the user, the user can log in normally." msgstr "解除用户登录限制后,此用户即可正常登录" #: users/templates/users/user_group_create_update.html:31 +#: xpack/plugins/orgs/templates/orgs/org_create_update.html:32 msgid "Cancel" msgstr "取消" @@ -2847,27 +2913,33 @@ msgid "User group detail" msgstr "用户组详情" #: users/templates/users/user_group_detail.html:86 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:121 msgid "Add user" msgstr "添加用户" #: users/templates/users/user_group_list.html:5 users/views/group.py:44 +#: xpack/templates/orgs/org_list.html:5 msgid "Create user group" msgstr "创建用户组" #: users/templates/users/user_group_list.html:82 +#: xpack/templates/orgs/org_list.html:82 msgid "This will delete the selected groups !!!" msgstr "删除选择组" #: users/templates/users/user_group_list.html:90 +#: xpack/templates/orgs/org_list.html:90 msgid "UserGroups Deleted." msgstr "用户组删除" #: users/templates/users/user_group_list.html:91 #: users/templates/users/user_group_list.html:96 +#: xpack/templates/orgs/org_list.html:91 xpack/templates/orgs/org_list.html:96 msgid "UserGroups Delete" msgstr "用户组删除" #: users/templates/users/user_group_list.html:95 +#: xpack/templates/orgs/org_list.html:95 msgid "UserGroup Deleting failed." msgstr "用户组删除失败" @@ -2943,30 +3015,6 @@ msgstr "新的公钥已设置成功,请下载对应的私钥" msgid "Update user" msgstr "更新用户" -#: users/templates/users/user_update.html:34 -msgid "Very weak" -msgstr "很弱" - -#: users/templates/users/user_update.html:35 -msgid "Weak" -msgstr "弱" - -#: users/templates/users/user_update.html:36 -msgid "Normal" -msgstr "正常" - -#: users/templates/users/user_update.html:37 -msgid "Medium" -msgstr "一般" - -#: users/templates/users/user_update.html:38 -msgid "Strong" -msgstr "强" - -#: users/templates/users/user_update.html:39 -msgid "Very strong" -msgstr "很强" - #: users/utils.py:41 msgid "Create account successfully" msgstr "创建账户成功" @@ -3218,6 +3266,54 @@ msgstr "MFA 解绑成功" msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" +#: xpack/plugins/orgs/forms.py:14 +#: xpack/plugins/orgs/templates/orgs/org_detail.html:76 +#: xpack/plugins/orgs/templates/orgs/org_list.html:13 +msgid "Admin" +msgstr "管理员" + +#: xpack/plugins/orgs/forms.py:18 +msgid "Select admins" +msgstr "选择管理员" + +#: xpack/plugins/orgs/meta.py:7 +msgid "Organization" +msgstr "组织管理" + +#: xpack/plugins/orgs/templates/orgs/org_detail.html:22 +#: xpack/plugins/orgs/views.py:73 +msgid "Org detail" +msgstr "组织详情" + +#: xpack/plugins/orgs/templates/orgs/org_detail.html:84 +msgid "Add admin" +msgstr "添加管理员" + +#: xpack/plugins/orgs/templates/orgs/org_list.html:5 +msgid "Create organization " +msgstr "创建组织" + +#: xpack/plugins/orgs/views.py:24 +msgid "Org" +msgstr "组织" + +#: xpack/plugins/orgs/views.py:25 +msgid "Org list" +msgstr "组织列表" + +#: xpack/plugins/orgs/views.py:40 xpack/plugins/orgs/views.py:56 +#: xpack/plugins/orgs/views.py:72 +msgid "Orgs" +msgstr "组织" + +#: xpack/plugins/orgs/views.py:41 +msgid "Create org" +msgstr "创建组织" + +#: xpack/plugins/orgs/views.py:57 +msgid "Update org" +msgstr "更新组织" + #~ msgid "* required Must set exact system platform, Windows, Linux ..." #~ msgstr "* required 必须准确设置操作系统平台,如Windows, Linux ..." diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 72128fa2b..859d399e8 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -224,6 +224,42 @@ function objectDelete(obj, name, url, redirectTo) { }); } +function orgDelete(obj, name, url, redirectTo){ + function doDelete() { + var body = {}; + var success = function() { + if (!redirectTo) { + $(obj).parent().parent().remove(); + } else { + window.location.href=redirectTo; + } + }; + var fail = function() { + swal("错误", "[ " + name + " ] 组织中存在未删除信息,请删除后重试", "error"); + }; + APIUpdateAttr({ + url: url, + body: JSON.stringify(body), + method: 'DELETE', + success_message: "删除成功", + success: success, + error: fail + }); + } + swal({ + title: "请先删除组织内的以下信息:", + text: "用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权规则", + type: "warning", + showCancelButton: true, + cancelButtonText: '取消', + confirmButtonColor: "#ed5565", + confirmButtonText: '确认', + closeOnConfirm: true + }, function () { + doDelete(); + }); +} + $.fn.serializeObject = function() { var o = {}; diff --git a/apps/users/templates/users/user_otp_authentication.html b/apps/users/templates/users/user_otp_authentication.html index 502707874..8d7101475 100644 --- a/apps/users/templates/users/user_otp_authentication.html +++ b/apps/users/templates/users/user_otp_authentication.html @@ -6,7 +6,7 @@

    账号保护已开启,请根据提示完成以下操作

    -

    请在手机中打开Google Authenticator应用,输入6为动态码

    +

    请在手机中打开Google Authenticator应用,输入6位动态码

    From 7c814080b264e73e1c2cc07e1f71d8039a2cd50d Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 6 Aug 2018 17:16:52 +0800 Subject: [PATCH 56/63] =?UTF-8?q?[Update]=20=E4=BC=98=E5=8C=96=E4=B8=80?= =?UTF-8?q?=E4=BA=9Bapi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/node.py | 47 ++++--------- apps/assets/models/node.py | 23 +++++-- apps/assets/serializers/node.py | 19 ++++-- .../templates/assets/_asset_list_modal.html | 9 ++- apps/assets/templates/assets/asset_list.html | 67 +++++++------------ apps/jumpserver/settings.py | 8 +-- apps/orgs/mixins.py | 3 +- .../perms/asset_permission_list.html | 39 ++++++----- apps/perms/utils.py | 4 +- .../templates/users/user_granted_asset.html | 40 ++++------- 10 files changed, 116 insertions(+), 143 deletions(-) diff --git a/apps/assets/api/node.py b/apps/assets/api/node.py index 471a096b6..515f1f13c 100644 --- a/apps/assets/api/node.py +++ b/apps/assets/api/node.py @@ -20,6 +20,7 @@ from rest_framework.response import Response from rest_framework_bulk import BulkModelViewSet from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 +from django.db.models import Count from common.utils import get_logger, get_object_or_none from ..hands import IsOrgAdmin @@ -42,40 +43,16 @@ class NodeViewSet(viewsets.ModelViewSet): permission_classes = (IsOrgAdmin,) serializer_class = serializers.NodeSerializer + def get_queryset(self): + queryset = super().get_queryset().annotate(Count('assets')) + return queryset + def perform_create(self, serializer): child_key = Node.root().get_next_child_key() serializer.validated_data["key"] = child_key serializer.save() -# class NodeWithAssetsApi(generics.ListAPIView): -# permission_classes = (IsOrgAdmin,) -# serializers = serializers.NodeSerializer -# -# def get_node(self): -# pk = self.kwargs.get('pk') or self.request.query_params.get('node') -# if not pk: -# node = Node.root() -# else: -# node = get_object_or_404(Node, pk) -# return node -# -# def get_queryset(self): -# queryset = [] -# node = self.get_node() -# children = node.get_children() -# assets = node.get_assets() -# queryset.extend(list(children)) -# -# for asset in assets: -# node = Node() -# node.id = asset.id -# node.parent = node.id -# node.value = asset.hostname -# queryset.append(node) -# return queryset - - class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView): queryset = Node.objects.all() permission_classes = (IsOrgAdmin,) @@ -124,22 +101,26 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView): query_all = self.request.query_params.get("all") query_assets = self.request.query_params.get('assets') node = self.get_object() + if node is None: node = Node.root() + node.assets__count = node.get_all_assets().count() queryset.append(node) - if query_all: - children = node.get_all_children() - else: - children = node.get_children() + if query_all: + children = node.get_all_children().annotate(Count("assets")) + else: + children = node.get_children().annotate(Count("assets")) queryset.extend(list(children)) + if query_assets: assets = node.get_assets() for asset in assets: node_fake = Node() + node_fake.assets__count = 0 node_fake.id = asset.id node_fake.is_node = False - node_fake.parent_id = node.id + node_fake.key = node.key + ':0' node_fake.value = asset.hostname queryset.append(node_fake) queryset = sorted(queryset, key=lambda x: x.is_node, reverse=True) diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 27a8e4519..8d006a40e 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -97,12 +97,10 @@ class Node(OrgModelMixin): def get_assets(self): from .asset import Asset - if self.is_root(): - assets = Asset.objects.filter( - Q(nodes__id=self.id) | Q(nodes__isnull=True) - ) + if self.is_default_node(): + assets = Asset.objects.filter(nodes__isnull=True) else: - assets = self.assets.all() + assets = Asset.objects.filter(nodes__id=self.id) return assets def get_valid_assets(self): @@ -132,13 +130,17 @@ class Node(OrgModelMixin): else: return False + @property + def parent_key(self): + parent_key = ":".join(self.key.split(":")[:-1]) + return parent_key + @property def parent(self): if self.is_root(): return self - parent_key = ":".join(self.key.split(":")[:-1]) try: - parent = self.__class__.objects.get(key=parent_key) + parent = self.__class__.objects.get(key=self.parent_key) return parent except Node.DoesNotExist: return self.__class__.root() @@ -197,5 +199,12 @@ class Node(OrgModelMixin): else: return cls.create_root_node() + @classmethod + def generate_fake(cls, count=100): + import random + for i in range(count): + node = random.choice(cls.objects.all()) + node.create_child('Node {}'.format(i)) + diff --git a/apps/assets/serializers/node.py b/apps/assets/serializers/node.py index 3eca3276f..4f7031065 100644 --- a/apps/assets/serializers/node.py +++ b/apps/assets/serializers/node.py @@ -43,14 +43,15 @@ class NodeGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer): class NodeSerializer(serializers.ModelSerializer): - parent = serializers.SerializerMethodField() assets_amount = serializers.SerializerMethodField() + tree_id = serializers.SerializerMethodField() + tree_parent = serializers.SerializerMethodField() class Meta: model = Node fields = [ - 'id', 'key', 'value', 'parent', 'assets_amount', - 'is_node', 'org_id', + 'id', 'key', 'value', 'assets_amount', + 'is_node', 'org_id', 'tree_id', 'tree_parent', ] list_serializer_class = BulkListSerializer @@ -66,12 +67,16 @@ class NodeSerializer(serializers.ModelSerializer): return data @staticmethod - def get_parent(obj): - return obj.parent.id if obj.is_node else obj.parent_id + def get_assets_amount(obj): + return obj.assets__count if hasattr(obj, 'assets__count') else 0 @staticmethod - def get_assets_amount(obj): - return obj.get_all_assets().count() if obj.is_node else 0 + def get_tree_id(obj): + return obj.key + + @staticmethod + def get_tree_parent(obj): + return obj.parent_key def get_fields(self): fields = super().get_fields() diff --git a/apps/assets/templates/assets/_asset_list_modal.html b/apps/assets/templates/assets/_asset_list_modal.html index faf569137..ea8d59e49 100644 --- a/apps/assets/templates/assets/_asset_list_modal.html +++ b/apps/assets/templates/assets/_asset_list_modal.html @@ -71,7 +71,7 @@ function initTable2() { function onSelected2(event, treeNode) { var url = asset_table2.ajax.url(); - url = setUrlParam(url, "node_id", treeNode.id); + url = setUrlParam(url, "node_id", treeNode.node_id); setCookie('node_selected', treeNode.id); asset_table2.ajax.url(url); asset_table2.ajax.reload(); @@ -97,17 +97,20 @@ function initTree2() { var zNodes = []; $.get("{% url 'api-assets:node-list' %}", function(data, status){ $.each(data, function (index, value) { - value["pId"] = value["parent"]; + value["node_id"] = value["id"]; + value["id"] = value["tree_id"]; + value["pId"] = value["tree_parent"]; {#value["open"] = true;#} if (value["key"] === "0") { value["open"] = true; } value["name"] = value["value"] + ' (' + value['assets_amount'] + ')'; - value['value'] = value['value']; }); zNodes = data; $.fn.zTree.init($("#assetTree2"), setting, zNodes); zTree2 = $.fn.zTree.getZTreeObj("assetTree2"); + var root = zTree2.getNodes()[0]; + zTree2.expandNode(root); }); } diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index 270cc4c4f..4986bb959 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -166,16 +166,6 @@ function initTable() { } }}, - {#{targets: 5, createdCell: function (td, cellData) {#} - {# if (cellData === 'Unknown'){#} - {# $(td).html('')#} - {# } else if (!cellData) {#} - {# $(td).html('')#} - {# } else {#} - {# $(td).html('')#} - {# }#} - {# }},#} - {targets: 5, createdCell: function (td, cellData, rowData) { var update_btn = '{% trans "Update" %}'.replace("{{ DEFAULT_PK }}", cellData); var del_btn = '{% trans "Delete" %}'.replace('{{ DEFAULT_PK }}', cellData); @@ -183,13 +173,6 @@ function initTable() { }} ], ajax_url: '{% url "api-assets:asset-list" %}', - - {#columns: [#} - {# {data: "id"}, {data: "hostname" }, {data: "ip" },#} - {# {data: "cpu_cores"}, {data: "is_active", orderable: false },#} - {# {data: "is_connective", orderable: false}, {data: "id", orderable: false }#} - {#],#} - columns: [ {data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "cpu_cores"}, {data: "is_active", orderable: false }, @@ -207,17 +190,17 @@ function addTreeNode() { if (!parentNode){ return } - var url = "{% url 'api-assets:node-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", parentNode.id ); + var url = "{% url 'api-assets:node-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", parentNode.node_id ); $.post(url, {}, function (data, status){ if (status === "success") { var newNode = { name: data["value"], id: data["id"], - pId: parentNode.id + pId: parentNode.node_id }; newNode.checked = zTree.getSelectedNodes()[0].checked; zTree.addNodes(parentNode, 0, newNode); - var node = zTree.getNodeByParam('id', newNode.id, parentNode) + var node = zTree.getNodeByParam('id', newNode.node_id, parentNode); zTree.editName(node); } else { alert("{% trans 'Create node failed' %}") @@ -236,7 +219,7 @@ function removeTreeNode() { } else if (current_node.assets_amount !== 0) { toastr.error("{% trans 'Have assets, cancel' %}"); } else { - var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id ); + var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.node_id ); $.ajax({ url: url, method: "DELETE", @@ -296,7 +279,7 @@ function onBodyMouseDown(event){ function onRename(event, treeId, treeNode, isCancel){ - var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", treeNode.id); + var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", treeNode.node_id); var data = {"value": treeNode.name}; if (isCancel){ return @@ -310,9 +293,9 @@ function onRename(event, treeId, treeNode, isCancel){ function onSelected(event, treeNode) { var url = asset_table.ajax.url(); - url = setUrlParam(url, "node_id", treeNode.id); + url = setUrlParam(url, "node_id", treeNode.node_id); url = setUrlParam(url, "show_current_asset", getCookie('show_current_asset')); - setCookie('node_selected', treeNode.id); + setCookie('node_selected', treeNode.node_id); asset_table.ajax.url(url); asset_table.ajax.reload(); } @@ -329,7 +312,7 @@ function selectQueryNode() { node_id = cookie_node_id; } - node = zTree.getNodesByParam("id", node_id, null); + node = zTree.getNodesByParam("node_id", node_id, null); if (node){ zTree.selectNode(node[0]); } @@ -346,11 +329,7 @@ function beforeDrop(treeId, treeNodes, targetNode, moveType) { }); var msg = "你想移动节点: `" + treeNodesNames.join(",") + "` 到 `" + targetNode.value + "` 下吗?"; - if (confirm(msg)){ - return true - } else { - return false - } + return confirm(msg); } function onDrag(event, treeId, treeNodes) { @@ -359,10 +338,10 @@ function onDrag(event, treeId, treeNodes) { function onDrop(event, treeId, treeNodes, targetNode, moveType) { var treeNodesIds = []; $.each(treeNodes, function (index, value) { - treeNodesIds.push(value.id); + treeNodesIds.push(value.node_id); }); - var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.id); + var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.node_id); var body = {nodes: treeNodesIds}; APIUpdateAttr({ url: the_url, @@ -406,8 +385,10 @@ function initTree() { var zNodes = []; $.get("{% url 'api-assets:node-list' %}", function(data, status){ $.each(data, function (index, value) { - if (value["parent"] !== value["id"]){ - value["pId"] = value["parent"]; + value["node_id"] = value["id"]; + value["id"] = value["tree_id"]; + if (value["tree_id"] !== value["tree_parent"]){ + value["pId"] = value["tree_parent"]; } else { value["isParent"] = true; } @@ -470,7 +451,7 @@ $(document).ready(function(){ $.ajax({ url: "{% url "assets:asset-export" %}", method: 'POST', - data: JSON.stringify({assets_id: assets, node_id: current_node.id}), + data: JSON.stringify({assets_id: assets, node_id: current_node.node_id}), dataType: "json", success: function (data, textStatus) { window.open(data.redirect) @@ -487,8 +468,8 @@ $(document).ready(function(){ var current_node; if (nodes && nodes.length ===1 ){ current_node = nodes[0]; - action = setUrlParam(action, 'node_id', current_node.id); - {#action += "?node_id=" + current_node.id;#} + action = setUrlParam(action, 'node_id', current_node.node_id); + {#action += "?node_id=" + current_node.node_id;#} $form.attr("action", action) } $form.find('.help-block').remove(); @@ -514,7 +495,7 @@ $(document).ready(function(){ var current_node; if (nodes && nodes.length ===1 ){ current_node = nodes[0]; - url += "?node_id=" + current_node.id; + url += "?node_id=" + current_node.node_id; } window.open(url, '_self'); }) @@ -528,7 +509,7 @@ $(document).ready(function(){ return null; } - var the_url = url.replace("{{ DEFAULT_PK }}", current_node.id); + var the_url = url.replace("{{ DEFAULT_PK }}", current_node.node_id); function success(data) { rMenu.css({"visibility" : "hidden"}); var task_id = data.task; @@ -553,7 +534,7 @@ $(document).ready(function(){ return null; } - var the_url = url.replace("{{ DEFAULT_PK }}", current_node.id); + var the_url = url.replace("{{ DEFAULT_PK }}", current_node.node_id); function success(data) { rMenu.css({"visibility" : "hidden"}); var task_id = data.task; @@ -690,7 +671,7 @@ $(document).ready(function(){ }; APIUpdateAttr({ - 'url': '/api/assets/v1/nodes/' + current_node.id + '/assets/remove/', + 'url': '/api/assets/v1/nodes/' + current_node.node_id + '/assets/remove/', 'method': 'PUT', 'body': JSON.stringify(data), 'success': success @@ -735,9 +716,9 @@ $(document).ready(function(){ var url = ''; if (update_node_action === "move") { - url = "{% url 'api-assets:node-replace-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id); + url = "{% url 'api-assets:node-replace-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.node_id); } else { - url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id); + url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.node_id); } APIUpdateAttr({ diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index 49b2f7870..b1c4fb3bf 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -261,10 +261,10 @@ LOGGING = { 'handlers': ['console', 'file'], 'level': "INFO", }, - # 'django.db': { - # 'handlers': ['console', 'file'], - # 'level': 'DEBUG' - # } + 'django.db': { + 'handlers': ['console', 'file'], + 'level': 'DEBUG' + } } } diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index 4a47f2ee3..7557600cb 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -29,8 +29,7 @@ class OrgManager(models.Manager): kwargs = {} if not hasattr(tl, 'times'): tl.times = 0 - print("[{}]>>>>>>>>>> Get query set".format(tl.times)) - print(current_org) + # logger.debug("[{}]>>>>>>>>>> Get query set".format(tl.times)) if not current_org: kwargs['id'] = None elif current_org.is_real(): diff --git a/apps/perms/templates/perms/asset_permission_list.html b/apps/perms/templates/perms/asset_permission_list.html index 36504ef59..31ccce112 100644 --- a/apps/perms/templates/perms/asset_permission_list.html +++ b/apps/perms/templates/perms/asset_permission_list.html @@ -80,12 +80,12 @@ function onSelected(event, treeNode) { var url = table.ajax.url(); if (treeNode.is_node) { url = setUrlParam(url, 'asset', ""); - url = setUrlParam(url, 'node', treeNode.id) + url = setUrlParam(url, 'node', treeNode.node_id) } else { url = setUrlParam(url, 'node', ""); - url = setUrlParam(url, 'asset', treeNode.id) + url = setUrlParam(url, 'asset', treeNode.node_id) } - setCookie('node_selected', treeNode.id); + setCookie('node_selected', treeNode.node_id); table.ajax.url(url); table.ajax.reload(); } @@ -111,10 +111,19 @@ function selectQueryNode() { function filter(treeId, parentNode, childNodes) { $.each(childNodes, function (index, value) { - value["pId"] = value["parent"]; - value["name"] = value["value"]; - value["isParent"] = value["is_node"]; + value["node_id"] = value["id"]; + value["id"] = value["tree_id"]; + if (value["tree_id"] !== value["tree_parent"]) { + value["pId"] = value["tree_parent"]; + } else { + value["isParent"] = true; + } + value['name'] = value['value']; value["iconSkin"] = value["is_node"] ? null : 'file'; + + {#value["pId"] = value["parent"];#} + {#value["name"] = value["value"];#} + value["isParent"] = value["is_node"]; }); return childNodes; } @@ -227,7 +236,7 @@ function initTree() { async: { enable: true, url: "{% url 'api-assets:node-children-2' %}?assets=1&all=", - autoParam:["id", "name=n", "level=lv"], + autoParam:["node_id=id", "name=n", "level=lv"], dataFilter: filter, type: 'get' }, @@ -238,15 +247,15 @@ function initTree() { }; var zNodes = []; - $.get("{% url 'api-assets:node-children-2' %}?assets=1&all=", function(data, status){ + $.get("{% url 'api-assets:node-children-2' %}?assets=1", function(data, status){ $.each(data, function (index, value) { - if (value["parent"] !== value["id"]){ - value["pId"] = value["parent"]; - } else { - value["isParent"] = true; + value["node_id"] = value["id"]; + value["id"] = value["tree_id"]; + if (value["tree_id"] !== value["tree_parent"]) { + value["pId"] = value["tree_parent"]; } - value["name"] = value["value"]; value["isParent"] = value["is_node"]; + value['name'] = value['value']; value["iconSkin"] = value["is_node"] ? null : 'file'; }); zNodes = data; @@ -291,9 +300,9 @@ $(document).ready(function(){ var _assets = []; $.each(nodes, function (id, node) { if (node.is_node) { - _nodes.push(node.id) + _nodes.push(node.node_id) } else { - _assets.push(node.id) + _assets.push(node.node_id) } }); url += "?assets=" + _assets.join(",") + "&nodes=" + _nodes.join(","); diff --git a/apps/perms/utils.py b/apps/perms/utils.py index a76953aac..326a8cfcf 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -13,7 +13,7 @@ logger = get_logger(__file__) class Tree: def __init__(self): - self.__all_nodes = list(Node.objects.all().prefetch_related('assets')) + self.__all_nodes = Node.objects.all().prefetch_related('assets') self.__node_asset_map = defaultdict(set) self.nodes = defaultdict(dict) self.root = Node.root() @@ -21,7 +21,7 @@ class Tree: def init_node_asset_map(self): for node in self.__all_nodes: - assets = node.get_assets().values_list('id', flat=True) + assets = [a.id for a in node.assets.all()] for asset in assets: self.__node_asset_map[str(asset)].add(node) diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html index 299c33e07..5b30c71b6 100644 --- a/apps/users/templates/users/user_granted_asset.html +++ b/apps/users/templates/users/user_granted_asset.html @@ -107,31 +107,14 @@ function initTable() { function onSelected(event, treeNode) { url = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}'; - url = url.replace("{{ DEFAULT_PK }}", treeNode.id); - setCookie('node_selected', treeNode.id); + console.log(">>>>>>>>>>>>>>>>>>>>>>>", treeNode.node_id, treeNode.id, treeNode); + url = url.replace("{{ DEFAULT_PK }}", treeNode.node_id); + setCookie('node_selected', treeNode.node_id); asset_table = initTable(); asset_table.ajax.url(url); asset_table.ajax.reload(); } -function selectQueryNode() { - var query_node_id = $.getUrlParam("node"); - var cookie_node_id = getCookie('node_selected'); - var node; - var node_id; - - if (query_node_id !== null) { - node_id = query_node_id - } else if (cookie_node_id !== null) { - node_id = cookie_node_id; - } - - node = zTree.getNodesByParam("id", node_id, null); - if (node){ - zTree.selectNode(node[0]); - } -} - function initTree() { var setting = { view: { @@ -149,19 +132,22 @@ function initTree() { }; var zNodes = []; - $.get("{% url 'api-perms:user-nodes' pk=object.id %}", function(data, status){ + $.get("{% url 'api-perms:user-nodes' pk=object.id %}", function(data, status) { $.each(data, function (index, value) { - value["pId"] = value["parent"]; - if (value["key"] === "0") { - value["open"] = true; + value["node_id"] = value["id"]; + value["id"] = value["tree_id"]; + if (value["tree_id"] !== value["tree_parent"]) { + value["pId"] = value["tree_parent"]; } - value["name"] = value["value"] + value["isParent"] = value["is_node"]; + value['name'] = value['value']; + value["iconSkin"] = value["is_node"] ? null : 'file'; }); zNodes = data; $.fn.zTree.init($("#assetTree"), setting, zNodes); zTree = $.fn.zTree.getZTreeObj("assetTree"); - rMenu = $("#rMenu"); - selectQueryNode(); + var root = zTree.getNodes()[0]; + zTree.expandNode(root); }); } From b7fcf80fc55e25623db17097f2882fe2a63126d3 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 6 Aug 2018 18:16:25 +0800 Subject: [PATCH 57/63] =?UTF-8?q?[Update]=20=E4=BC=98=E5=8C=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E8=B5=84=E4=BA=A7=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/assets/user_asset_list.html | 46 ++++++------------- apps/perms/api.py | 4 +- .../templates/users/user_granted_asset.html | 7 ++- apps/users/views/user.py | 1 - 4 files changed, 17 insertions(+), 41 deletions(-) diff --git a/apps/assets/templates/assets/user_asset_list.html b/apps/assets/templates/assets/user_asset_list.html index e1284b7a7..dc799e734 100644 --- a/apps/assets/templates/assets/user_asset_list.html +++ b/apps/assets/templates/assets/user_asset_list.html @@ -62,7 +62,7 @@ {% block custom_foot_js %} {% endblock %} diff --git a/apps/users/views/user.py b/apps/users/views/user.py index cb0b45bad..133298c43 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -342,7 +342,6 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView): class UserGrantedAssetView(AdminUserRequiredMixin, DetailView): model = User template_name = 'users/user_granted_asset.html' - object = None def get_context_data(self, **kwargs): context = { From 96ec5fac99f3fc870101333ca43f1f106d9cb3f9 Mon Sep 17 00:00:00 2001 From: BaiJiangJie <32935519+BaiJiangJie@users.noreply.github.com> Date: Mon, 6 Aug 2018 18:17:17 +0800 Subject: [PATCH 58/63] =?UTF-8?q?[Update]=20=E6=9B=B4=E6=96=B0=20xpack/org?= =?UTF-8?q?s=20=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD=EF=BC=9A=E9=99=90?= =?UTF-8?q?=E5=88=B6=E5=9C=A8=E5=BD=93=E5=89=8Dorg=E4=B8=8B=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E5=BD=93=E5=89=8Dorg=20(#1645)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/static/js/jumpserver.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 859d399e8..e0e7a2d6e 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -179,7 +179,7 @@ function APIUpdateAttr(props) { toastr.error(fail_message); } if (typeof props.error === 'function') { - return props.error(jqXHR.responseText); + return props.error(jqXHR.responseText, jqXHR.status); } }); // return true; @@ -234,8 +234,13 @@ function orgDelete(obj, name, url, redirectTo){ window.location.href=redirectTo; } }; - var fail = function() { - swal("错误", "[ " + name + " ] 组织中存在未删除信息,请删除后重试", "error"); + var fail = function(responseText, status) { + if (status === 400){ + swal("错误", "[ " + name + " ] 组织中包含未删除信息,请删除后重试", "error"); + } + else if (status === 405){ + swal("错误", "请勿在组织 [ "+ name + " ] 下执行此操作,切换到其他组织后重试", "error"); + } }; APIUpdateAttr({ url: url, @@ -247,7 +252,7 @@ function orgDelete(obj, name, url, redirectTo){ }); } swal({ - title: "请先删除组织内的以下信息:", + title: "请确保组织内的以下信息已删除", text: "用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权规则", type: "warning", showCancelButton: true, From 534734881c96e5f3b4609f2c28fea103718cb513 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 7 Aug 2018 11:32:11 +0800 Subject: [PATCH 59/63] =?UTF-8?q?[Update]=20=E4=BF=AE=E6=94=B9=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/__init__.py | 2 +- apps/templates/_footer.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/__init__.py b/apps/__init__.py index be40e1dd2..c84997cac 100644 --- a/apps/__init__.py +++ b/apps/__init__.py @@ -2,4 +2,4 @@ # -*- coding: utf-8 -*- # -__version__ = "1.3.3" +__version__ = "1.4.0" diff --git a/apps/templates/_footer.html b/apps/templates/_footer.html index 70e0c8dcd..7fbc74055 100644 --- a/apps/templates/_footer.html +++ b/apps/templates/_footer.html @@ -1,6 +1,6 @@
  • #} -{% if XPACK_ENABLED %} +{% if XPACK_PLUGINS %}
  • {% trans 'XPack' %} From 337338ebf3872d3ec52b5f3466c0c33b124fa87f Mon Sep 17 00:00:00 2001 From: pufer Date: Tue, 7 Aug 2018 18:11:46 +0800 Subject: [PATCH 61/63] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=9AOU=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20(#1262)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加多OU支持 * Update models.py need to import LDAPSearchUnion --- apps/common/api.py | 25 +++++++++++++------------ apps/common/forms.py | 3 ++- apps/common/models.py | 11 ++++++----- apps/jumpserver/settings.py | 10 ++++++---- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/apps/common/api.py b/apps/common/api.py index b6658b665..e09cf9726 100644 --- a/apps/common/api.py +++ b/apps/common/api.py @@ -48,7 +48,7 @@ class LDAPTestingAPI(APIView): bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"] password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"] use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False) - search_ou = serializer.validated_data["AUTH_LDAP_SEARCH_OU"] + search_ougroup = serializer.validated_data["AUTH_LDAP_SEARCH_OU"] search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"] attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"] @@ -64,18 +64,19 @@ class LDAPTestingAPI(APIView): except Exception as e: return Response({"error": str(e)}, status=401) - ok = conn.search(search_ou, search_filter % ({"user": "*"}), - attributes=list(attr_map.values())) - if not ok: - return Response({"error": "Search no entry matched"}, status=401) - users = [] - for entry in conn.entries: - user = {} - for attr, mapping in attr_map.items(): - if hasattr(entry, mapping): - user[attr] = getattr(entry, mapping) - users.append(user) + for search_ou in str(search_ougroup).split("|"): + ok = conn.search(search_ou, search_filter % ({"user": "*"}), + attributes=list(attr_map.values())) + if not ok: + return Response({"error": _("Search no entry matched in ou {}").format(search_ou)}, status=401) + + for entry in conn.entries: + user = {} + for attr, mapping in attr_map.items(): + if hasattr(entry, mapping): + user[attr] = getattr(entry, mapping) + users.append(user) if len(users) > 0: return Response({"msg": _("Match {} s users").format(len(users))}) else: diff --git a/apps/common/forms.py b/apps/common/forms.py index 8667aa128..3ee553016 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -114,7 +114,8 @@ class LDAPSettingForm(BaseForm): widget=forms.PasswordInput, required=False ) AUTH_LDAP_SEARCH_OU = forms.CharField( - label=_("User OU"), initial='ou=tech,dc=jumpserver,dc=org' + label=_("User OU"), initial='ou=tech,dc=jumpserver,dc=org', + help_text=_("Use | split User OUs") ) AUTH_LDAP_SEARCH_FILTER = forms.CharField( label=_("User search filter"), initial='(cn=%(user)s)', diff --git a/apps/common/models.py b/apps/common/models.py index c90458985..007683917 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -5,7 +5,7 @@ from django.db import models from django.db.utils import ProgrammingError, OperationalError from django.utils.translation import ugettext_lazy as _ from django.conf import settings -from django_auth_ldap.config import LDAPSearch +from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion class SettingQuerySet(models.QuerySet): @@ -72,10 +72,11 @@ class Setting(models.Model): settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND) if self.name == "AUTH_LDAP_SEARCH_FILTER": - settings.AUTH_LDAP_USER_SEARCH = LDAPSearch( - settings.AUTH_LDAP_SEARCH_OU, ldap.SCOPE_SUBTREE, - settings.AUTH_LDAP_SEARCH_FILTER, - ) + settings.AUTH_LDAP_USER_SEARCH_UNION = [ + LDAPSearch(USER_SEARCH, ldap.SCOPE_SUBTREE, settings.AUTH_LDAP_SEARCH_FILTER) + for USER_SEARCH in str(settings.AUTH_LDAP_SEARCH_OU).split("|") + ] + settings.AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(*settings.AUTH_LDAP_USER_SEARCH_UNION) class Meta: db_table = "settings" diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index e4a932775..37c14dbf1 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -14,7 +14,7 @@ import os import sys import ldap -from django_auth_ldap.config import LDAPSearch +from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion from django.urls import reverse_lazy # Build paths inside the project like this: os.path.join(BASE_DIR, ...) @@ -356,9 +356,11 @@ AUTH_LDAP_SEARCH_OU = CONFIG.AUTH_LDAP_SEARCH_OU AUTH_LDAP_SEARCH_FILTER = CONFIG.AUTH_LDAP_SEARCH_FILTER AUTH_LDAP_START_TLS = CONFIG.AUTH_LDAP_START_TLS AUTH_LDAP_USER_ATTR_MAP = CONFIG.AUTH_LDAP_USER_ATTR_MAP -AUTH_LDAP_USER_SEARCH = LDAPSearch( - AUTH_LDAP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_SEARCH_FILTER, -) +AUTH_LDAP_USER_SEARCH_UNION = [ + LDAPSearch(USER_SEARCH, ldap.SCOPE_SUBTREE, AUTH_LDAP_SEARCH_FILTER) + for USER_SEARCH in str(AUTH_LDAP_SEARCH_OU).split("|") +] +AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(*AUTH_LDAP_USER_SEARCH_UNION) AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER AUTH_LDAP_GROUP_SEARCH = LDAPSearch( From ccc292d9a98252bdec112e2efdaf87c9efaae4a5 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 7 Aug 2018 18:38:46 +0800 Subject: [PATCH 62/63] =?UTF-8?q?[Update]=20=E5=8E=BB=E6=8E=89debug?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/permissions.py | 3 --- apps/orgs/mixins.py | 1 - 2 files changed, 4 deletions(-) diff --git a/apps/common/permissions.py b/apps/common/permissions.py index 9d01031bc..4693748eb 100644 --- a/apps/common/permissions.py +++ b/apps/common/permissions.py @@ -79,7 +79,6 @@ class AdminUserRequiredMixin(UserPassesTestMixin): return True def dispatch(self, request, *args, **kwargs): - print("Current org: {}".format(current_org)) if not request.user.is_authenticated: return super().dispatch(request, *args, **kwargs) @@ -92,6 +91,4 @@ class AdminUserRequiredMixin(UserPassesTestMixin): print("Is org admin") return redirect('orgs:switch-a-org') return HttpResponseForbidden() - else: - print(current_org.can_admin_by(request.user)) return super().dispatch(request, *args, **kwargs) diff --git a/apps/orgs/mixins.py b/apps/orgs/mixins.py index 7557600cb..99140f40b 100644 --- a/apps/orgs/mixins.py +++ b/apps/orgs/mixins.py @@ -43,7 +43,6 @@ class OrgManager(models.Manager): def all(self): if not current_org: msg = 'You can `objects.set_current_org(org).all()` then run it' - warnings.warn(msg) return self else: return super(OrgManager, self).all() From 3a4c7846bf3eab34bf32c482e79657ebe4e032c6 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 8 Aug 2018 11:44:06 +0800 Subject: [PATCH 63/63] =?UTF-8?q?[Bugfix]=20=E4=BF=AE=E6=94=B9=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E7=AC=AC=E4=B8=80=E4=B8=AA=E8=8A=82=E7=82=B9=E7=9A=84?= =?UTF-8?q?Bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/models/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/models/node.py b/apps/assets/models/node.py index 8d006a40e..c919edff6 100644 --- a/apps/assets/models/node.py +++ b/apps/assets/models/node.py @@ -185,7 +185,7 @@ class Node(OrgModelMixin): else: set_current_org(Organization.root()) org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$') - org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) + org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) or [0] key = max([int(k) for k in org_nodes_roots_keys]) + 1 set_current_org(_current_org) root = cls.objects.create(key=key, value=_current_org.name)