mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-06-30 00:32:06 +00:00
commit
84070a558e
@ -1,3 +1,4 @@
|
|||||||
from .application import *
|
from .application import *
|
||||||
|
from .application_user import *
|
||||||
from .mixin import *
|
from .mixin import *
|
||||||
from .remote_app import *
|
from .remote_app import *
|
||||||
|
@ -2,18 +2,13 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from rest_framework import generics
|
|
||||||
|
|
||||||
from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin
|
from ..hands import IsOrgAdminOrAppUser
|
||||||
from .. import models, serializers
|
from .. import serializers
|
||||||
from ..models import Application
|
from ..models import Application
|
||||||
from assets.models import SystemUser
|
|
||||||
from assets.serializers import SystemUserListSerializer
|
|
||||||
from perms.models import ApplicationPermission
|
|
||||||
from ..const import ApplicationCategoryChoices
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['ApplicationViewSet', 'ApplicationUserListApi']
|
__all__ = ['ApplicationViewSet']
|
||||||
|
|
||||||
|
|
||||||
class ApplicationViewSet(OrgBulkModelViewSet):
|
class ApplicationViewSet(OrgBulkModelViewSet):
|
||||||
@ -22,29 +17,3 @@ class ApplicationViewSet(OrgBulkModelViewSet):
|
|||||||
search_fields = filterset_fields
|
search_fields = filterset_fields
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.ApplicationSerializer
|
serializer_class = serializers.ApplicationSerializer
|
||||||
|
|
||||||
|
|
||||||
class ApplicationUserListApi(generics.ListAPIView):
|
|
||||||
permission_classes = (IsOrgAdmin, )
|
|
||||||
filterset_fields = ('name', 'username')
|
|
||||||
search_fields = filterset_fields
|
|
||||||
serializer_class = SystemUserListSerializer
|
|
||||||
|
|
||||||
def get_application(self):
|
|
||||||
application = None
|
|
||||||
app_id = self.request.query_params.get('application_id')
|
|
||||||
if app_id:
|
|
||||||
application = Application.objects.get(id=app_id)
|
|
||||||
return application
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = SystemUser.objects.none()
|
|
||||||
application = self.get_application()
|
|
||||||
if not application:
|
|
||||||
return queryset
|
|
||||||
system_user_ids = ApplicationPermission.objects.filter(applications=application)\
|
|
||||||
.values_list('system_users', flat=True)
|
|
||||||
if not system_user_ids:
|
|
||||||
return queryset
|
|
||||||
queryset = SystemUser.objects.filter(id__in=system_user_ids)
|
|
||||||
return queryset
|
|
||||||
|
55
apps/applications/api/application_user.py
Normal file
55
apps/applications/api/application_user.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
#
|
||||||
|
|
||||||
|
from rest_framework import generics
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from ..hands import IsOrgAdminOrAppUser, IsOrgAdmin, NeedMFAVerify
|
||||||
|
from .. import serializers
|
||||||
|
from ..models import Application, ApplicationUser
|
||||||
|
from perms.models import ApplicationPermission
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationUserListApi(generics.ListAPIView):
|
||||||
|
permission_classes = (IsOrgAdmin, )
|
||||||
|
filterset_fields = ('name', 'username')
|
||||||
|
search_fields = filterset_fields
|
||||||
|
serializer_class = serializers.ApplicationUserSerializer
|
||||||
|
_application = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def application(self):
|
||||||
|
if self._application is None:
|
||||||
|
app_id = self.request.query_params.get('application_id')
|
||||||
|
if app_id:
|
||||||
|
self._application = Application.objects.get(id=app_id)
|
||||||
|
return self._application
|
||||||
|
|
||||||
|
def get_serializer_context(self):
|
||||||
|
context = super().get_serializer_context()
|
||||||
|
context.update({
|
||||||
|
'application': self.application
|
||||||
|
})
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = ApplicationUser.objects.none()
|
||||||
|
if not self.application:
|
||||||
|
return queryset
|
||||||
|
system_user_ids = ApplicationPermission.objects.filter(applications=self.application)\
|
||||||
|
.values_list('system_users', flat=True)
|
||||||
|
if not system_user_ids:
|
||||||
|
return queryset
|
||||||
|
queryset = ApplicationUser.objects.filter(id__in=system_user_ids)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationUserAuthInfoListApi(ApplicationUserListApi):
|
||||||
|
serializer_class = serializers.ApplicationUserWithAuthInfoSerializer
|
||||||
|
http_method_names = ['get']
|
||||||
|
permission_classes = [IsOrgAdminOrAppUser]
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
if settings.SECURITY_VIEW_AUTH_NEED_MFA:
|
||||||
|
self.permission_classes = [IsOrgAdminOrAppUser, NeedMFAVerify]
|
||||||
|
return super().get_permissions()
|
@ -11,5 +11,5 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
|
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser, NeedMFAVerify
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
|
@ -3,7 +3,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
from common.mixins import CommonModelMixin
|
from common.mixins import CommonModelMixin
|
||||||
from assets.models import Asset
|
from assets.models import Asset, SystemUser
|
||||||
from .. import const
|
from .. import const
|
||||||
|
|
||||||
|
|
||||||
@ -68,3 +68,8 @@ class Application(CommonModelMixin, OrgModelMixin):
|
|||||||
raise ValueError("Remote App not has asset attr")
|
raise ValueError("Remote App not has asset attr")
|
||||||
asset = Asset.objects.filter(id=asset_id).first()
|
asset = Asset.objects.filter(id=asset_id).first()
|
||||||
return asset
|
return asset
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationUser(SystemUser):
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
||||||
|
@ -6,11 +6,12 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from common.drf.serializers import MethodSerializer
|
from common.drf.serializers import MethodSerializer
|
||||||
from .attrs import category_serializer_classes_mapping, type_serializer_classes_mapping
|
from .attrs import category_serializer_classes_mapping, type_serializer_classes_mapping
|
||||||
|
from assets.serializers import SystemUserSerializer
|
||||||
from .. import models
|
from .. import models
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'ApplicationSerializer', 'ApplicationSerializerMixin',
|
'ApplicationSerializer', 'ApplicationSerializerMixin',
|
||||||
|
'ApplicationUserSerializer', 'ApplicationUserWithAuthInfoSerializer'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -66,3 +67,42 @@ class ApplicationSerializer(ApplicationSerializerMixin, BulkOrgResourceModelSeri
|
|||||||
_attrs.update(attrs)
|
_attrs.update(attrs)
|
||||||
return _attrs
|
return _attrs
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationUserSerializer(SystemUserSerializer):
|
||||||
|
application_name = serializers.SerializerMethodField(label=_('Application name'))
|
||||||
|
application_category = serializers.SerializerMethodField(label=_('Application category'))
|
||||||
|
application_type = serializers.SerializerMethodField(label=_('Application type'))
|
||||||
|
|
||||||
|
class Meta(SystemUserSerializer.Meta):
|
||||||
|
model = models.ApplicationUser
|
||||||
|
fields_mini = [
|
||||||
|
'id', 'application_name', 'application_category', 'application_type', 'name', 'username'
|
||||||
|
]
|
||||||
|
fields_small = fields_mini + [
|
||||||
|
'protocol', 'login_mode', 'login_mode_display', 'priority',
|
||||||
|
"username_same_with_user", 'comment',
|
||||||
|
]
|
||||||
|
fields = fields_small
|
||||||
|
extra_kwargs = {
|
||||||
|
'login_mode_display': {'label': _('Login mode display')},
|
||||||
|
'created_by': {'read_only': True},
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def application(self):
|
||||||
|
return self.context['application']
|
||||||
|
|
||||||
|
def get_application_name(self, obj):
|
||||||
|
return self.application.name
|
||||||
|
|
||||||
|
def get_application_category(self, obj):
|
||||||
|
return self.application.get_category_display()
|
||||||
|
|
||||||
|
def get_application_type(self, obj):
|
||||||
|
return self.application.get_type_display()
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationUserWithAuthInfoSerializer(ApplicationUserSerializer):
|
||||||
|
|
||||||
|
class Meta(ApplicationUserSerializer.Meta):
|
||||||
|
fields = ApplicationUserSerializer.Meta.fields + ['password']
|
||||||
|
@ -14,7 +14,8 @@ router.register(r'applications', api.ApplicationViewSet, 'application')
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
|
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
|
||||||
path('application-users/', api.ApplicationUserListApi.as_view(), name='application-user')
|
path('application-users/', api.ApplicationUserListApi.as_view(), name='application-user'),
|
||||||
|
path('application-user-auth-infos/', api.ApplicationUserAuthInfoListApi.as_view(), name='application-user-auth-info')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,8 +94,14 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet):
|
|||||||
date_range_filter_fields = [
|
date_range_filter_fields = [
|
||||||
('date_start', ('date_from', 'date_to'))
|
('date_start', ('date_from', 'date_to'))
|
||||||
]
|
]
|
||||||
filterset_fields = ['user__name', 'command', 'run_as__name', 'is_finished']
|
filterset_fields = [
|
||||||
search_fields = ['command', 'user__name', 'run_as__name']
|
'user__name', 'user__username', 'command',
|
||||||
|
'run_as__name', 'run_as__username', 'is_finished'
|
||||||
|
]
|
||||||
|
search_fields = [
|
||||||
|
'command', 'user__name', 'user__username',
|
||||||
|
'run_as__name', 'run_as__username',
|
||||||
|
]
|
||||||
ordering = ['-date_created']
|
ordering = ['-date_created']
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -82,7 +82,7 @@ class CommandExecutionSerializer(serializers.ModelSerializer):
|
|||||||
model = CommandExecution
|
model = CommandExecution
|
||||||
fields_mini = ['id']
|
fields_mini = ['id']
|
||||||
fields_small = fields_mini + [
|
fields_small = fields_mini + [
|
||||||
'run_as', 'command', 'user', 'is_finished',
|
'run_as', 'command', 'is_finished', 'user',
|
||||||
'date_start', 'result', 'is_success', 'org_id'
|
'date_start', 'result', 'is_success', 'org_id'
|
||||||
]
|
]
|
||||||
fields = fields_small + ['hosts', 'hosts_display', 'run_as_display', 'user_display']
|
fields = fields_small + ['hosts', 'hosts_display', 'run_as_display', 'user_display']
|
||||||
|
@ -57,6 +57,7 @@ class AuthBackendLabelMapping(LazyObject):
|
|||||||
backend_label_mapping[settings.AUTH_BACKEND_PUBKEY] = _('SSH Key')
|
backend_label_mapping[settings.AUTH_BACKEND_PUBKEY] = _('SSH Key')
|
||||||
backend_label_mapping[settings.AUTH_BACKEND_MODEL] = _('Password')
|
backend_label_mapping[settings.AUTH_BACKEND_MODEL] = _('Password')
|
||||||
backend_label_mapping[settings.AUTH_BACKEND_SSO] = _('SSO')
|
backend_label_mapping[settings.AUTH_BACKEND_SSO] = _('SSO')
|
||||||
|
backend_label_mapping[settings.AUTH_BACKEND_AUTH_TOKEN] = _('Auth Token')
|
||||||
backend_label_mapping[settings.AUTH_BACKEND_WECOM] = _('WeCom')
|
backend_label_mapping[settings.AUTH_BACKEND_WECOM] = _('WeCom')
|
||||||
backend_label_mapping[settings.AUTH_BACKEND_DINGTALK] = _('DingTalk')
|
backend_label_mapping[settings.AUTH_BACKEND_DINGTALK] = _('DingTalk')
|
||||||
return backend_label_mapping
|
return backend_label_mapping
|
||||||
@ -156,12 +157,13 @@ def get_login_backend(request):
|
|||||||
return backend_label
|
return backend_label
|
||||||
|
|
||||||
|
|
||||||
def generate_data(username, request):
|
def generate_data(username, request, login_type=None):
|
||||||
user_agent = request.META.get('HTTP_USER_AGENT', '')
|
user_agent = request.META.get('HTTP_USER_AGENT', '')
|
||||||
login_ip = get_request_ip(request) or '0.0.0.0'
|
login_ip = get_request_ip(request) or '0.0.0.0'
|
||||||
if isinstance(request, Request):
|
|
||||||
|
if login_type is None and isinstance(request, Request):
|
||||||
login_type = request.META.get('HTTP_X_JMS_LOGIN_TYPE', 'U')
|
login_type = request.META.get('HTTP_X_JMS_LOGIN_TYPE', 'U')
|
||||||
else:
|
if login_type is None:
|
||||||
login_type = 'W'
|
login_type = 'W'
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@ -176,9 +178,9 @@ def generate_data(username, request):
|
|||||||
|
|
||||||
|
|
||||||
@receiver(post_auth_success)
|
@receiver(post_auth_success)
|
||||||
def on_user_auth_success(sender, user, request, **kwargs):
|
def on_user_auth_success(sender, user, request, login_type=None, **kwargs):
|
||||||
logger.debug('User login success: {}'.format(user.username))
|
logger.debug('User login success: {}'.format(user.username))
|
||||||
data = generate_data(user.username, request)
|
data = generate_data(user.username, request, login_type=login_type)
|
||||||
data.update({'mfa': int(user.mfa_enabled), 'status': True})
|
data.update({'mfa': int(user.mfa_enabled), 'status': True})
|
||||||
write_login_log(**data)
|
write_login_log(**data)
|
||||||
|
|
||||||
|
@ -6,12 +6,14 @@ from django.conf import settings
|
|||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.viewsets import GenericViewSet
|
from rest_framework.viewsets import GenericViewSet
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from authentication.signals import post_auth_failed, post_auth_success
|
||||||
from common.utils import get_logger, random_string
|
from common.utils import get_logger, random_string
|
||||||
from common.drf.api import SerializerMixin2
|
from common.drf.api import SerializerMixin2
|
||||||
from common.permissions import IsSuperUserOrAppUser, IsValidUser, IsSuperUser
|
from common.permissions import IsSuperUserOrAppUser, IsValidUser, IsSuperUser
|
||||||
@ -51,10 +53,6 @@ class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericView
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def create_token(self, user, asset, application, system_user, ttl=5*60):
|
def create_token(self, user, asset, application, system_user, ttl=5*60):
|
||||||
if not settings.CONNECTION_TOKEN_ENABLED:
|
|
||||||
raise PermissionDenied('Connection token disabled')
|
|
||||||
if not user:
|
|
||||||
user = self.request.user
|
|
||||||
if not self.request.user.is_superuser and user != self.request.user:
|
if not self.request.user.is_superuser and user != self.request.user:
|
||||||
raise PermissionDenied('Only super user can create user token')
|
raise PermissionDenied('Only super user can create user token')
|
||||||
self.check_resource_permission(user, asset, application, system_user)
|
self.check_resource_permission(user, asset, application, system_user)
|
||||||
@ -233,12 +231,24 @@ class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericView
|
|||||||
|
|
||||||
if asset and not asset.is_active:
|
if asset and not asset.is_active:
|
||||||
raise serializers.ValidationError("Asset disabled")
|
raise serializers.ValidationError("Asset disabled")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.check_resource_permission(user, asset, app, system_user)
|
||||||
|
except PermissionDenied:
|
||||||
|
raise serializers.ValidationError('Permission expired or invalid')
|
||||||
return value, user, system_user, asset, app
|
return value, user, system_user, asset, app
|
||||||
|
|
||||||
@action(methods=['POST'], detail=False, permission_classes=[IsSuperUserOrAppUser], url_path='secret-info/detail')
|
@action(methods=['POST'], detail=False, permission_classes=[IsSuperUserOrAppUser], url_path='secret-info/detail')
|
||||||
def get_secret_detail(self, request, *args, **kwargs):
|
def get_secret_detail(self, request, *args, **kwargs):
|
||||||
token = request.data.get('token', '')
|
token = request.data.get('token', '')
|
||||||
value, user, system_user, asset, app = self.valid_token(token)
|
try:
|
||||||
|
value, user, system_user, asset, app = self.valid_token(token)
|
||||||
|
except serializers.ValidationError as e:
|
||||||
|
post_auth_failed.send(
|
||||||
|
sender=self.__class__, username='', request=self.request,
|
||||||
|
reason=_('Invalid token')
|
||||||
|
)
|
||||||
|
raise e
|
||||||
|
|
||||||
data = dict(user=user, system_user=system_user)
|
data = dict(user=user, system_user=system_user)
|
||||||
if asset:
|
if asset:
|
||||||
@ -252,6 +262,9 @@ class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericView
|
|||||||
data['type'] = 'application'
|
data['type'] = 'application'
|
||||||
data.update(app_detail)
|
data.update(app_detail)
|
||||||
|
|
||||||
|
self.request.session['auth_backend'] = settings.AUTH_BACKEND_AUTH_TOKEN
|
||||||
|
post_auth_success.send(sender=self.__class__, user=user, request=self.request, login_type='T')
|
||||||
|
|
||||||
serializer = self.get_serializer(data)
|
serializer = self.get_serializer(data)
|
||||||
return Response(data=serializer.data, status=200)
|
return Response(data=serializer.data, status=200)
|
||||||
|
|
||||||
|
@ -228,3 +228,11 @@ class DingTalkAuthentication(ModelBackend):
|
|||||||
|
|
||||||
def authenticate(self, request, **kwargs):
|
def authenticate(self, request, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorizationTokenAuthentication(ModelBackend):
|
||||||
|
"""
|
||||||
|
什么也不做呀😺
|
||||||
|
"""
|
||||||
|
def authenticate(self, request, **kwargs):
|
||||||
|
pass
|
||||||
|
@ -2,9 +2,11 @@ from channels.auth import AuthMiddlewareStack
|
|||||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||||
|
|
||||||
from ops.urls.ws_urls import urlpatterns as ops_urlpatterns
|
from ops.urls.ws_urls import urlpatterns as ops_urlpatterns
|
||||||
|
from notifications.urls.ws_urls import urlpatterns as notifications_urlpatterns
|
||||||
|
|
||||||
urlpatterns = []
|
urlpatterns = []
|
||||||
urlpatterns += ops_urlpatterns
|
urlpatterns += ops_urlpatterns \
|
||||||
|
+ notifications_urlpatterns
|
||||||
|
|
||||||
application = ProtocolTypeRouter({
|
application = ProtocolTypeRouter({
|
||||||
'websocket': AuthMiddlewareStack(
|
'websocket': AuthMiddlewareStack(
|
||||||
|
@ -130,10 +130,12 @@ AUTH_BACKEND_CAS = 'authentication.backends.cas.CASBackend'
|
|||||||
AUTH_BACKEND_SSO = 'authentication.backends.api.SSOAuthentication'
|
AUTH_BACKEND_SSO = 'authentication.backends.api.SSOAuthentication'
|
||||||
AUTH_BACKEND_WECOM = 'authentication.backends.api.WeComAuthentication'
|
AUTH_BACKEND_WECOM = 'authentication.backends.api.WeComAuthentication'
|
||||||
AUTH_BACKEND_DINGTALK = 'authentication.backends.api.DingTalkAuthentication'
|
AUTH_BACKEND_DINGTALK = 'authentication.backends.api.DingTalkAuthentication'
|
||||||
|
AUTH_BACKEND_AUTH_TOKEN = 'authentication.backends.api.AuthorizationTokenAuthentication'
|
||||||
|
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = [
|
AUTHENTICATION_BACKENDS = [
|
||||||
AUTH_BACKEND_MODEL, AUTH_BACKEND_PUBKEY, AUTH_BACKEND_WECOM, AUTH_BACKEND_DINGTALK
|
AUTH_BACKEND_MODEL, AUTH_BACKEND_PUBKEY, AUTH_BACKEND_WECOM,
|
||||||
|
AUTH_BACKEND_DINGTALK, AUTH_BACKEND_AUTH_TOKEN
|
||||||
]
|
]
|
||||||
|
|
||||||
if AUTH_CAS:
|
if AUTH_CAS:
|
||||||
|
@ -48,7 +48,7 @@ INSTALLED_APPS = [
|
|||||||
'applications.apps.ApplicationsConfig',
|
'applications.apps.ApplicationsConfig',
|
||||||
'tickets.apps.TicketsConfig',
|
'tickets.apps.TicketsConfig',
|
||||||
'acls.apps.AclsConfig',
|
'acls.apps.AclsConfig',
|
||||||
'notifications',
|
'notifications.apps.NotificationsConfig',
|
||||||
'common.apps.CommonConfig',
|
'common.apps.CommonConfig',
|
||||||
'jms_oidc_rp',
|
'jms_oidc_rp',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
@ -23,7 +23,7 @@ api_v1 = [
|
|||||||
path('applications/', include('applications.urls.api_urls', namespace='api-applications')),
|
path('applications/', include('applications.urls.api_urls', namespace='api-applications')),
|
||||||
path('tickets/', include('tickets.urls.api_urls', namespace='api-tickets')),
|
path('tickets/', include('tickets.urls.api_urls', namespace='api-tickets')),
|
||||||
path('acls/', include('acls.urls.api_urls', namespace='api-acls')),
|
path('acls/', include('acls.urls.api_urls', namespace='api-acls')),
|
||||||
path('notifications/', include('notifications.urls.notifications', namespace='api-notifications')),
|
path('notifications/', include('notifications.urls.api_urls', namespace='api-notifications')),
|
||||||
path('prometheus/metrics/', api.PrometheusMetricsApi.as_view()),
|
path('prometheus/metrics/', api.PrometheusMetricsApi.as_view()),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Binary file not shown.
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-06-04 11:29+0800\n"
|
"POT-Creation-Date: 2021-06-11 11:06+0800\n"
|
||||||
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
|
||||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||||
@ -121,7 +121,7 @@ msgstr "系统用户"
|
|||||||
#: applications/serializers/attrs/application_category/remote_app.py:33
|
#: applications/serializers/attrs/application_category/remote_app.py:33
|
||||||
#: assets/models/asset.py:355 assets/models/authbook.py:26
|
#: assets/models/asset.py:355 assets/models/authbook.py:26
|
||||||
#: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:34
|
#: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:34
|
||||||
#: assets/serializers/asset_user.py:48 assets/serializers/asset_user.py:90
|
#: assets/serializers/asset_user.py:48 assets/serializers/asset_user.py:91
|
||||||
#: assets/serializers/system_user.py:202 audits/models.py:38
|
#: assets/serializers/system_user.py:202 audits/models.py:38
|
||||||
#: perms/models/asset_permission.py:99 templates/index.html:82
|
#: perms/models/asset_permission.py:99 templates/index.html:82
|
||||||
#: terminal/backends/command/models.py:19
|
#: terminal/backends/command/models.py:19
|
||||||
@ -158,7 +158,7 @@ msgstr ""
|
|||||||
#: acls/serializers/login_acl.py:30 acls/serializers/login_asset_acl.py:31
|
#: acls/serializers/login_acl.py:30 acls/serializers/login_asset_acl.py:31
|
||||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:18
|
#: applications/serializers/attrs/application_type/mysql_workbench.py:18
|
||||||
#: assets/models/asset.py:183 assets/models/domain.py:52
|
#: assets/models/asset.py:183 assets/models/domain.py:52
|
||||||
#: assets/serializers/asset_user.py:47 settings/serializers/settings.py:117
|
#: assets/serializers/asset_user.py:47 settings/serializers/settings.py:113
|
||||||
#: users/templates/users/_granted_assets.html:26
|
#: users/templates/users/_granted_assets.html:26
|
||||||
#: users/templates/users/user_asset_permission.html:156
|
#: users/templates/users/user_asset_permission.html:156
|
||||||
msgid "IP"
|
msgid "IP"
|
||||||
@ -199,7 +199,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: acls/serializers/login_asset_acl.py:35 assets/models/asset.py:184
|
#: acls/serializers/login_asset_acl.py:35 assets/models/asset.py:184
|
||||||
#: assets/serializers/asset_user.py:46 assets/serializers/gathered_user.py:23
|
#: assets/serializers/asset_user.py:46 assets/serializers/gathered_user.py:23
|
||||||
#: settings/serializers/settings.py:116
|
#: settings/serializers/settings.py:112
|
||||||
#: users/templates/users/_granted_assets.html:25
|
#: users/templates/users/_granted_assets.html:25
|
||||||
#: users/templates/users/user_asset_permission.html:157
|
#: users/templates/users/user_asset_permission.html:157
|
||||||
msgid "Hostname"
|
msgid "Hostname"
|
||||||
@ -272,14 +272,32 @@ msgstr "网域"
|
|||||||
msgid "Attrs"
|
msgid "Attrs"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: applications/serializers/application.py:47
|
#: applications/serializers/application.py:48
|
||||||
msgid "Category(Display)"
|
msgid "Category(Display)"
|
||||||
msgstr "类别 (显示名称)"
|
msgstr "类别 (显示名称)"
|
||||||
|
|
||||||
#: applications/serializers/application.py:48
|
#: applications/serializers/application.py:49
|
||||||
msgid "Type(Dispaly)"
|
msgid "Type(Dispaly)"
|
||||||
msgstr "类型 (显示名称)"
|
msgstr "类型 (显示名称)"
|
||||||
|
|
||||||
|
#: applications/serializers/application.py:72
|
||||||
|
msgid "Application name"
|
||||||
|
msgstr "应用名称"
|
||||||
|
|
||||||
|
#: applications/serializers/application.py:73
|
||||||
|
msgid "Application category"
|
||||||
|
msgstr "应用类别"
|
||||||
|
|
||||||
|
#: applications/serializers/application.py:74
|
||||||
|
msgid "Application type"
|
||||||
|
msgstr "应用类型"
|
||||||
|
|
||||||
|
#: applications/serializers/application.py:87
|
||||||
|
#: assets/serializers/system_user.py:49 assets/serializers/system_user.py:177
|
||||||
|
#: assets/serializers/system_user.py:203
|
||||||
|
msgid "Login mode display"
|
||||||
|
msgstr "登录模式(显示名称)"
|
||||||
|
|
||||||
#: applications/serializers/attrs/application_category/cloud.py:9
|
#: applications/serializers/attrs/application_category/cloud.py:9
|
||||||
#: assets/models/cluster.py:40
|
#: assets/models/cluster.py:40
|
||||||
msgid "Cluster"
|
msgid "Cluster"
|
||||||
@ -316,10 +334,10 @@ msgstr "目标URL"
|
|||||||
#: applications/serializers/attrs/application_type/custom.py:25
|
#: applications/serializers/attrs/application_type/custom.py:25
|
||||||
#: applications/serializers/attrs/application_type/mysql_workbench.py:34
|
#: applications/serializers/attrs/application_type/mysql_workbench.py:34
|
||||||
#: applications/serializers/attrs/application_type/vmware_client.py:30
|
#: applications/serializers/attrs/application_type/vmware_client.py:30
|
||||||
#: assets/models/base.py:251 assets/serializers/asset_user.py:77
|
#: assets/models/base.py:251 assets/serializers/asset_user.py:78
|
||||||
#: audits/signals_handler.py:58 authentication/forms.py:22
|
#: audits/signals_handler.py:58 authentication/forms.py:22
|
||||||
#: authentication/templates/authentication/login.html:164
|
#: authentication/templates/authentication/login.html:164
|
||||||
#: settings/serializers/settings.py:98 users/forms/profile.py:21
|
#: settings/serializers/settings.py:94 users/forms/profile.py:21
|
||||||
#: users/templates/users/user_otp_check_password.html:13
|
#: users/templates/users/user_otp_check_password.html:13
|
||||||
#: users/templates/users/user_password_update.html:43
|
#: users/templates/users/user_password_update.html:43
|
||||||
#: users/templates/users/user_password_verify.html:18
|
#: users/templates/users/user_password_verify.html:18
|
||||||
@ -822,12 +840,12 @@ msgstr "后端"
|
|||||||
msgid "Source"
|
msgid "Source"
|
||||||
msgstr "来源"
|
msgstr "来源"
|
||||||
|
|
||||||
#: assets/serializers/asset_user.py:81 users/forms/profile.py:160
|
#: assets/serializers/asset_user.py:82 users/forms/profile.py:160
|
||||||
#: users/models/user.py:580 users/templates/users/user_password_update.html:48
|
#: users/models/user.py:580 users/templates/users/user_password_update.html:48
|
||||||
msgid "Public key"
|
msgid "Public key"
|
||||||
msgstr "SSH公钥"
|
msgstr "SSH公钥"
|
||||||
|
|
||||||
#: assets/serializers/asset_user.py:85 users/models/user.py:577
|
#: assets/serializers/asset_user.py:86 users/models/user.py:577
|
||||||
msgid "Private key"
|
msgid "Private key"
|
||||||
msgstr "ssh私钥"
|
msgstr "ssh私钥"
|
||||||
|
|
||||||
@ -875,11 +893,6 @@ msgstr "同级别节点名字不能重复"
|
|||||||
msgid "Nodes amount"
|
msgid "Nodes amount"
|
||||||
msgstr "节点数量"
|
msgstr "节点数量"
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:49 assets/serializers/system_user.py:177
|
|
||||||
#: assets/serializers/system_user.py:203
|
|
||||||
msgid "Login mode display"
|
|
||||||
msgstr "登录模式(显示名称)"
|
|
||||||
|
|
||||||
#: assets/serializers/system_user.py:51 assets/serializers/system_user.py:179
|
#: assets/serializers/system_user.py:51 assets/serializers/system_user.py:179
|
||||||
msgid "Ad domain"
|
msgid "Ad domain"
|
||||||
msgstr "Ad 网域"
|
msgstr "Ad 网域"
|
||||||
@ -2049,7 +2062,7 @@ msgstr "应用程序"
|
|||||||
msgid "Application permission"
|
msgid "Application permission"
|
||||||
msgstr "应用管理"
|
msgstr "应用管理"
|
||||||
|
|
||||||
#: perms/models/asset_permission.py:37 settings/serializers/settings.py:121
|
#: perms/models/asset_permission.py:37 settings/serializers/settings.py:117
|
||||||
msgid "All"
|
msgid "All"
|
||||||
msgstr "全部"
|
msgstr "全部"
|
||||||
|
|
||||||
@ -2194,27 +2207,19 @@ msgstr "当前站点URL"
|
|||||||
msgid "eg: http://dev.jumpserver.org:8080"
|
msgid "eg: http://dev.jumpserver.org:8080"
|
||||||
msgstr "如: http://dev.jumpserver.org:8080"
|
msgstr "如: http://dev.jumpserver.org:8080"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:19
|
#: settings/serializers/settings.py:20
|
||||||
msgid "RDP address"
|
|
||||||
msgstr "RDP 地址"
|
|
||||||
|
|
||||||
#: settings/serializers/settings.py:21
|
|
||||||
msgid "RDP visit address, eg: dev.jumpserver.org:3389"
|
|
||||||
msgstr "RDP 访问地址, 如: dev.jumpserver.org:3389"
|
|
||||||
|
|
||||||
#: settings/serializers/settings.py:24
|
|
||||||
msgid "User guide url"
|
msgid "User guide url"
|
||||||
msgstr "用户向导URL"
|
msgstr "用户向导URL"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:25
|
#: settings/serializers/settings.py:21
|
||||||
msgid "User first login update profile done redirect to it"
|
msgid "User first login update profile done redirect to it"
|
||||||
msgstr "用户第一次登录,修改profile后重定向到地址, 可以是 wiki 或 其他说明文档"
|
msgstr "用户第一次登录,修改profile后重定向到地址, 可以是 wiki 或 其他说明文档"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:28
|
#: settings/serializers/settings.py:24
|
||||||
msgid "Forgot password url"
|
msgid "Forgot password url"
|
||||||
msgstr "忘记密码URL"
|
msgstr "忘记密码URL"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:29
|
#: settings/serializers/settings.py:25
|
||||||
msgid ""
|
msgid ""
|
||||||
"The forgot password url on login page, If you use ldap or cas external "
|
"The forgot password url on login page, If you use ldap or cas external "
|
||||||
"authentication, you can set it"
|
"authentication, you can set it"
|
||||||
@ -2222,138 +2227,138 @@ msgstr ""
|
|||||||
"登录页面忘记密码URL, 如果使用了 LDAP, OPENID 等外部认证系统,可以自定义用户重"
|
"登录页面忘记密码URL, 如果使用了 LDAP, OPENID 等外部认证系统,可以自定义用户重"
|
||||||
"置密码访问的地址"
|
"置密码访问的地址"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:33
|
#: settings/serializers/settings.py:29
|
||||||
msgid "Global organization name"
|
msgid "Global organization name"
|
||||||
msgstr "全局组织名"
|
msgstr "全局组织名"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:34
|
#: settings/serializers/settings.py:30
|
||||||
msgid "The name of global organization to display"
|
msgid "The name of global organization to display"
|
||||||
msgstr "全局组织的显示名称,默认为 全局组织"
|
msgstr "全局组织的显示名称,默认为 全局组织"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:41
|
#: settings/serializers/settings.py:37
|
||||||
msgid "SMTP host"
|
msgid "SMTP host"
|
||||||
msgstr "SMTP 主机"
|
msgstr "SMTP 主机"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:42
|
#: settings/serializers/settings.py:38
|
||||||
msgid "SMTP port"
|
msgid "SMTP port"
|
||||||
msgstr "SMTP 端口"
|
msgstr "SMTP 端口"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:43
|
#: settings/serializers/settings.py:39
|
||||||
msgid "SMTP account"
|
msgid "SMTP account"
|
||||||
msgstr "SMTP 账号"
|
msgstr "SMTP 账号"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:45
|
#: settings/serializers/settings.py:41
|
||||||
msgid "SMTP password"
|
msgid "SMTP password"
|
||||||
msgstr "SMTP 密码"
|
msgstr "SMTP 密码"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:46
|
#: settings/serializers/settings.py:42
|
||||||
msgid "Tips: Some provider use token except password"
|
msgid "Tips: Some provider use token except password"
|
||||||
msgstr "提示:一些邮件提供商需要输入的是授权码"
|
msgstr "提示:一些邮件提供商需要输入的是授权码"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:49
|
#: settings/serializers/settings.py:45
|
||||||
msgid "Send user"
|
msgid "Send user"
|
||||||
msgstr "发件人"
|
msgstr "发件人"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:50
|
#: settings/serializers/settings.py:46
|
||||||
msgid "Tips: Send mail account, default SMTP account as the send account"
|
msgid "Tips: Send mail account, default SMTP account as the send account"
|
||||||
msgstr "提示:发送邮件账号,默认使用 SMTP 账号作为发送账号"
|
msgstr "提示:发送邮件账号,默认使用 SMTP 账号作为发送账号"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:53
|
#: settings/serializers/settings.py:49
|
||||||
msgid "Test recipient"
|
msgid "Test recipient"
|
||||||
msgstr "测试收件人"
|
msgstr "测试收件人"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:54
|
#: settings/serializers/settings.py:50
|
||||||
msgid "Tips: Used only as a test mail recipient"
|
msgid "Tips: Used only as a test mail recipient"
|
||||||
msgstr "提示:仅用来作为测试邮件收件人"
|
msgstr "提示:仅用来作为测试邮件收件人"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:57
|
#: settings/serializers/settings.py:53
|
||||||
msgid "Use SSL"
|
msgid "Use SSL"
|
||||||
msgstr "使用 SSL"
|
msgstr "使用 SSL"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:58
|
#: settings/serializers/settings.py:54
|
||||||
msgid "If SMTP port is 465, may be select"
|
msgid "If SMTP port is 465, may be select"
|
||||||
msgstr "如果SMTP端口是465,通常需要启用 SSL"
|
msgstr "如果SMTP端口是465,通常需要启用 SSL"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:61
|
#: settings/serializers/settings.py:57
|
||||||
msgid "Use TLS"
|
msgid "Use TLS"
|
||||||
msgstr "使用 TLS"
|
msgstr "使用 TLS"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:62
|
#: settings/serializers/settings.py:58
|
||||||
msgid "If SMTP port is 587, may be select"
|
msgid "If SMTP port is 587, may be select"
|
||||||
msgstr "如果SMTP端口是587,通常需要启用 TLS"
|
msgstr "如果SMTP端口是587,通常需要启用 TLS"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:65
|
#: settings/serializers/settings.py:61
|
||||||
msgid "Subject prefix"
|
msgid "Subject prefix"
|
||||||
msgstr "主题前缀"
|
msgstr "主题前缀"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:72
|
#: settings/serializers/settings.py:68
|
||||||
msgid "Create user email subject"
|
msgid "Create user email subject"
|
||||||
msgstr "邮件主题"
|
msgstr "邮件主题"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:73
|
#: settings/serializers/settings.py:69
|
||||||
msgid ""
|
msgid ""
|
||||||
"Tips: When creating a user, send the subject of the email (eg:Create account "
|
"Tips: When creating a user, send the subject of the email (eg:Create account "
|
||||||
"successfully)"
|
"successfully)"
|
||||||
msgstr "提示: 创建用户时,发送设置密码邮件的主题 (例如: 创建用户成功)"
|
msgstr "提示: 创建用户时,发送设置密码邮件的主题 (例如: 创建用户成功)"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:77
|
#: settings/serializers/settings.py:73
|
||||||
msgid "Create user honorific"
|
msgid "Create user honorific"
|
||||||
msgstr "邮件的敬语"
|
msgstr "邮件的敬语"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:78
|
#: settings/serializers/settings.py:74
|
||||||
msgid "Tips: When creating a user, send the honorific of the email (eg:Hello)"
|
msgid "Tips: When creating a user, send the honorific of the email (eg:Hello)"
|
||||||
msgstr "提示: 创建用户时,发送设置密码邮件的敬语 (例如: 您好)"
|
msgstr "提示: 创建用户时,发送设置密码邮件的敬语 (例如: 您好)"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:82
|
#: settings/serializers/settings.py:78
|
||||||
msgid "Create user email content"
|
msgid "Create user email content"
|
||||||
msgstr "邮件的内容"
|
msgstr "邮件的内容"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:83
|
#: settings/serializers/settings.py:79
|
||||||
msgid "Tips:When creating a user, send the content of the email"
|
msgid "Tips:When creating a user, send the content of the email"
|
||||||
msgstr "提示: 创建用户时,发送设置密码邮件的内容"
|
msgstr "提示: 创建用户时,发送设置密码邮件的内容"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:86
|
#: settings/serializers/settings.py:82
|
||||||
msgid "Signature"
|
msgid "Signature"
|
||||||
msgstr "署名"
|
msgstr "署名"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:87
|
#: settings/serializers/settings.py:83
|
||||||
msgid "Tips: Email signature (eg:jumpserver)"
|
msgid "Tips: Email signature (eg:jumpserver)"
|
||||||
msgstr "邮件署名 (如:jumpserver)"
|
msgstr "邮件署名 (如:jumpserver)"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:95
|
#: settings/serializers/settings.py:91
|
||||||
msgid "LDAP server"
|
msgid "LDAP server"
|
||||||
msgstr "LDAP 地址"
|
msgstr "LDAP 地址"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:95
|
#: settings/serializers/settings.py:91
|
||||||
msgid "eg: ldap://localhost:389"
|
msgid "eg: ldap://localhost:389"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: settings/serializers/settings.py:97
|
#: settings/serializers/settings.py:93
|
||||||
msgid "Bind DN"
|
msgid "Bind DN"
|
||||||
msgstr "绑定 DN"
|
msgstr "绑定 DN"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:100
|
#: settings/serializers/settings.py:96
|
||||||
msgid "User OU"
|
msgid "User OU"
|
||||||
msgstr "用户 OU"
|
msgstr "用户 OU"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:101
|
#: settings/serializers/settings.py:97
|
||||||
msgid "Use | split multi OUs"
|
msgid "Use | split multi OUs"
|
||||||
msgstr "多个 OU 使用 | 分割"
|
msgstr "多个 OU 使用 | 分割"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:104
|
#: settings/serializers/settings.py:100
|
||||||
msgid "User search filter"
|
msgid "User search filter"
|
||||||
msgstr "用户过滤器"
|
msgstr "用户过滤器"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:105
|
#: settings/serializers/settings.py:101
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Choice may be (cn|uid|sAMAccountName)=%(user)s)"
|
msgid "Choice may be (cn|uid|sAMAccountName)=%(user)s)"
|
||||||
msgstr "可能的选项是(cn或uid或sAMAccountName=%(user)s)"
|
msgstr "可能的选项是(cn或uid或sAMAccountName=%(user)s)"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:108
|
#: settings/serializers/settings.py:104
|
||||||
msgid "User attr map"
|
msgid "User attr map"
|
||||||
msgstr "用户属性映射"
|
msgstr "用户属性映射"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:109
|
#: settings/serializers/settings.py:105
|
||||||
msgid ""
|
msgid ""
|
||||||
"User attr map present how to map LDAP user attr to jumpserver, username,name,"
|
"User attr map present how to map LDAP user attr to jumpserver, username,name,"
|
||||||
"email is jumpserver attr"
|
"email is jumpserver attr"
|
||||||
@ -2361,23 +2366,23 @@ msgstr ""
|
|||||||
"用户属性映射代表怎样将LDAP中用户属性映射到jumpserver用户上,username, name,"
|
"用户属性映射代表怎样将LDAP中用户属性映射到jumpserver用户上,username, name,"
|
||||||
"email 是jumpserver的用户需要属性"
|
"email 是jumpserver的用户需要属性"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:111
|
#: settings/serializers/settings.py:107
|
||||||
msgid "Enable LDAP auth"
|
msgid "Enable LDAP auth"
|
||||||
msgstr "启用 LDAP 认证"
|
msgstr "启用 LDAP 认证"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:122
|
#: settings/serializers/settings.py:118
|
||||||
msgid "Auto"
|
msgid "Auto"
|
||||||
msgstr "自动"
|
msgstr "自动"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:128
|
#: settings/serializers/settings.py:124
|
||||||
msgid "Password auth"
|
msgid "Password auth"
|
||||||
msgstr "密码认证"
|
msgstr "密码认证"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:130
|
#: settings/serializers/settings.py:126
|
||||||
msgid "Public key auth"
|
msgid "Public key auth"
|
||||||
msgstr "密钥认证"
|
msgstr "密钥认证"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:131
|
#: settings/serializers/settings.py:127
|
||||||
msgid ""
|
msgid ""
|
||||||
"Tips: If use other auth method, like AD/LDAP, you should disable this to "
|
"Tips: If use other auth method, like AD/LDAP, you should disable this to "
|
||||||
"avoid being able to log in after deleting"
|
"avoid being able to log in after deleting"
|
||||||
@ -2385,19 +2390,19 @@ msgstr ""
|
|||||||
"提示:如果你使用其它认证方式,如 AD/LDAP,你应该禁用此项,以避免第三方系统删"
|
"提示:如果你使用其它认证方式,如 AD/LDAP,你应该禁用此项,以避免第三方系统删"
|
||||||
"除后,还可以登录"
|
"除后,还可以登录"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:134
|
#: settings/serializers/settings.py:130
|
||||||
msgid "List sort by"
|
msgid "List sort by"
|
||||||
msgstr "资产列表排序"
|
msgstr "资产列表排序"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:135
|
#: settings/serializers/settings.py:131
|
||||||
msgid "List page size"
|
msgid "List page size"
|
||||||
msgstr "资产列表每页数量"
|
msgstr "资产列表每页数量"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:137
|
#: settings/serializers/settings.py:133
|
||||||
msgid "Session keep duration"
|
msgid "Session keep duration"
|
||||||
msgstr "会话日志保存时间"
|
msgstr "会话日志保存时间"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:138
|
#: settings/serializers/settings.py:134
|
||||||
msgid ""
|
msgid ""
|
||||||
"Units: days, Session, record, command will be delete if more than duration, "
|
"Units: days, Session, record, command will be delete if more than duration, "
|
||||||
"only in database"
|
"only in database"
|
||||||
@ -2405,64 +2410,72 @@ msgstr ""
|
|||||||
"单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不"
|
"单位:天。 会话、录像、命令记录超过该时长将会被删除(仅影响数据库存储, oss等不"
|
||||||
"受影响)"
|
"受影响)"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:140
|
#: settings/serializers/settings.py:136
|
||||||
msgid "Telnet login regex"
|
msgid "Telnet login regex"
|
||||||
msgstr "Telnet 成功正则表达式"
|
msgstr "Telnet 成功正则表达式"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:145
|
#: settings/serializers/settings.py:138
|
||||||
|
msgid "RDP address"
|
||||||
|
msgstr "RDP 地址"
|
||||||
|
|
||||||
|
#: settings/serializers/settings.py:141
|
||||||
|
msgid "RDP visit address, eg: dev.jumpserver.org:3389"
|
||||||
|
msgstr "RDP 访问地址, 如: dev.jumpserver.org:3389"
|
||||||
|
|
||||||
|
#: settings/serializers/settings.py:147
|
||||||
msgid "Global MFA auth"
|
msgid "Global MFA auth"
|
||||||
msgstr "全局启用 MFA 认证"
|
msgstr "全局启用 MFA 认证"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:146
|
#: settings/serializers/settings.py:148
|
||||||
msgid "All user enable MFA"
|
msgid "All user enable MFA"
|
||||||
msgstr "强制所有用户启用多因子认证"
|
msgstr "强制所有用户启用多因子认证"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:149
|
#: settings/serializers/settings.py:151
|
||||||
msgid "Batch command execution"
|
msgid "Batch command execution"
|
||||||
msgstr "批量命令执行"
|
msgstr "批量命令执行"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:150
|
#: settings/serializers/settings.py:152
|
||||||
msgid "Allow user run batch command or not using ansible"
|
msgid "Allow user run batch command or not using ansible"
|
||||||
msgstr "是否允许用户使用 ansible 执行批量命令"
|
msgstr "是否允许用户使用 ansible 执行批量命令"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:153
|
#: settings/serializers/settings.py:155
|
||||||
msgid "Enable terminal register"
|
msgid "Enable terminal register"
|
||||||
msgstr "终端注册"
|
msgstr "终端注册"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:154
|
#: settings/serializers/settings.py:156
|
||||||
msgid ""
|
msgid ""
|
||||||
"Allow terminal register, after all terminal setup, you should disable this "
|
"Allow terminal register, after all terminal setup, you should disable this "
|
||||||
"for security"
|
"for security"
|
||||||
msgstr "是否允许终端注册,当所有终端启动后,为了安全应该关闭"
|
msgstr "是否允许终端注册,当所有终端启动后,为了安全应该关闭"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:158
|
#: settings/serializers/settings.py:160
|
||||||
msgid "Limit the number of login failures"
|
msgid "Limit the number of login failures"
|
||||||
msgstr "限制登录失败次数"
|
msgstr "限制登录失败次数"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:162
|
#: settings/serializers/settings.py:164
|
||||||
msgid "Block logon interval"
|
msgid "Block logon interval"
|
||||||
msgstr "禁止登录时间间隔"
|
msgstr "禁止登录时间间隔"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:163
|
#: settings/serializers/settings.py:165
|
||||||
msgid ""
|
msgid ""
|
||||||
"Tip: (unit/minute) if the user has failed to log in for a limited number of "
|
"Tip: (unit/minute) if the user has failed to log in for a limited number of "
|
||||||
"times, no login is allowed during this time interval."
|
"times, no login is allowed during this time interval."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"提示:(单位:分)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录"
|
"提示:(单位:分)当用户登录失败次数达到限制后,那么在此时间间隔内禁止登录"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:167
|
#: settings/serializers/settings.py:169
|
||||||
msgid "Connection max idle time"
|
msgid "Connection max idle time"
|
||||||
msgstr "连接最大空闲时间"
|
msgstr "连接最大空闲时间"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:168
|
#: settings/serializers/settings.py:170
|
||||||
msgid "If idle time more than it, disconnect connection Unit: minute"
|
msgid "If idle time more than it, disconnect connection Unit: minute"
|
||||||
msgstr "提示:如果超过该配置没有操作,连接会被断开 (单位:分)"
|
msgstr "提示:如果超过该配置没有操作,连接会被断开 (单位:分)"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:172
|
#: settings/serializers/settings.py:174
|
||||||
msgid "User password expiration"
|
msgid "User password expiration"
|
||||||
msgstr "用户密码过期时间"
|
msgstr "用户密码过期时间"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:173
|
#: settings/serializers/settings.py:175
|
||||||
msgid ""
|
msgid ""
|
||||||
"Tip: (unit: day) If the user does not update the password during the time, "
|
"Tip: (unit: day) If the user does not update the password during the time, "
|
||||||
"the user password will expire failure;The password expiration reminder mail "
|
"the user password will expire failure;The password expiration reminder mail "
|
||||||
@ -2472,53 +2485,53 @@ msgstr ""
|
|||||||
"提示:(单位:天)如果用户在此期间没有更新密码,用户密码将过期失效; 密码过期"
|
"提示:(单位:天)如果用户在此期间没有更新密码,用户密码将过期失效; 密码过期"
|
||||||
"提醒邮件将在密码过期前5天内由系统(每天)自动发送给用户"
|
"提醒邮件将在密码过期前5天内由系统(每天)自动发送给用户"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:177
|
#: settings/serializers/settings.py:179
|
||||||
msgid "Number of repeated historical passwords"
|
msgid "Number of repeated historical passwords"
|
||||||
msgstr "不能设置近几次密码"
|
msgstr "不能设置近几次密码"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:178
|
#: settings/serializers/settings.py:180
|
||||||
msgid ""
|
msgid ""
|
||||||
"Tip: When the user resets the password, it cannot be the previous n "
|
"Tip: When the user resets the password, it cannot be the previous n "
|
||||||
"historical passwords of the user"
|
"historical passwords of the user"
|
||||||
msgstr "提示:用户重置密码时,不能为该用户前几次使用过的密码"
|
msgstr "提示:用户重置密码时,不能为该用户前几次使用过的密码"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:182
|
#: settings/serializers/settings.py:184
|
||||||
msgid "Password minimum length"
|
msgid "Password minimum length"
|
||||||
msgstr "密码最小长度"
|
msgstr "密码最小长度"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:185
|
#: settings/serializers/settings.py:187
|
||||||
msgid "Must contain capital"
|
msgid "Must contain capital"
|
||||||
msgstr "必须包含大写字符"
|
msgstr "必须包含大写字符"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:187
|
#: settings/serializers/settings.py:189
|
||||||
msgid "Must contain lowercase"
|
msgid "Must contain lowercase"
|
||||||
msgstr "必须包含小写字符"
|
msgstr "必须包含小写字符"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:188
|
#: settings/serializers/settings.py:190
|
||||||
msgid "Must contain numeric"
|
msgid "Must contain numeric"
|
||||||
msgstr "必须包含数字"
|
msgstr "必须包含数字"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:189
|
#: settings/serializers/settings.py:191
|
||||||
msgid "Must contain special"
|
msgid "Must contain special"
|
||||||
msgstr "必须包含特殊字符"
|
msgstr "必须包含特殊字符"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:190
|
#: settings/serializers/settings.py:192
|
||||||
msgid "Insecure command alert"
|
msgid "Insecure command alert"
|
||||||
msgstr "危险命令告警"
|
msgstr "危险命令告警"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:192
|
#: settings/serializers/settings.py:194
|
||||||
msgid "Email recipient"
|
msgid "Email recipient"
|
||||||
msgstr "邮件收件人"
|
msgstr "邮件收件人"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:193
|
#: settings/serializers/settings.py:195
|
||||||
msgid "Multiple user using , split"
|
msgid "Multiple user using , split"
|
||||||
msgstr "多个用户,使用 , 分割"
|
msgstr "多个用户,使用 , 分割"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:201
|
#: settings/serializers/settings.py:203
|
||||||
msgid "Enable WeCom Auth"
|
msgid "Enable WeCom Auth"
|
||||||
msgstr "启用企业微信认证"
|
msgstr "启用企业微信认证"
|
||||||
|
|
||||||
#: settings/serializers/settings.py:208
|
#: settings/serializers/settings.py:210
|
||||||
msgid "Enable DingTalk Auth"
|
msgid "Enable DingTalk Auth"
|
||||||
msgstr "启用钉钉认证"
|
msgstr "启用钉钉认证"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ from ..serializers import (
|
|||||||
SiteMessageDetailSerializer, SiteMessageIdsSerializer,
|
SiteMessageDetailSerializer, SiteMessageIdsSerializer,
|
||||||
SiteMessageSendSerializer,
|
SiteMessageSendSerializer,
|
||||||
)
|
)
|
||||||
from ..site_msg import SiteMessage
|
from ..site_msg import SiteMessageUtil
|
||||||
from ..filters import SiteMsgFilter
|
from ..filters import SiteMsgFilter
|
||||||
|
|
||||||
__all__ = ('SiteMessageViewSet', )
|
__all__ = ('SiteMessageViewSet', )
|
||||||
@ -30,15 +30,15 @@ class SiteMessageViewSet(ListModelMixin, RetrieveModelMixin, JmsGenericViewSet):
|
|||||||
has_read = self.request.query_params.get('has_read')
|
has_read = self.request.query_params.get('has_read')
|
||||||
|
|
||||||
if has_read is None:
|
if has_read is None:
|
||||||
msgs = SiteMessage.get_user_all_msgs(user.id)
|
msgs = SiteMessageUtil.get_user_all_msgs(user.id)
|
||||||
else:
|
else:
|
||||||
msgs = SiteMessage.filter_user_msgs(user.id, has_read=is_true(has_read))
|
msgs = SiteMessageUtil.filter_user_msgs(user.id, has_read=is_true(has_read))
|
||||||
return msgs
|
return msgs
|
||||||
|
|
||||||
@action(methods=[GET], detail=False, url_path='unread-total')
|
@action(methods=[GET], detail=False, url_path='unread-total')
|
||||||
def unread_total(self, request, **kwargs):
|
def unread_total(self, request, **kwargs):
|
||||||
user = request.user
|
user = request.user
|
||||||
msgs = SiteMessage.filter_user_msgs(user.id, has_read=False)
|
msgs = SiteMessageUtil.filter_user_msgs(user.id, has_read=False)
|
||||||
return Response(data={'total': msgs.count()})
|
return Response(data={'total': msgs.count()})
|
||||||
|
|
||||||
@action(methods=[PATCH], detail=False, url_path='mark-as-read')
|
@action(methods=[PATCH], detail=False, url_path='mark-as-read')
|
||||||
@ -47,12 +47,12 @@ class SiteMessageViewSet(ListModelMixin, RetrieveModelMixin, JmsGenericViewSet):
|
|||||||
seri = self.get_serializer(data=request.data)
|
seri = self.get_serializer(data=request.data)
|
||||||
seri.is_valid(raise_exception=True)
|
seri.is_valid(raise_exception=True)
|
||||||
ids = seri.validated_data['ids']
|
ids = seri.validated_data['ids']
|
||||||
SiteMessage.mark_msgs_as_read(user.id, ids)
|
SiteMessageUtil.mark_msgs_as_read(user.id, ids)
|
||||||
return Response({'detail': 'ok'})
|
return Response({'detail': 'ok'})
|
||||||
|
|
||||||
@action(methods=[POST], detail=False)
|
@action(methods=[POST], detail=False)
|
||||||
def send(self, request, **kwargs):
|
def send(self, request, **kwargs):
|
||||||
seri = self.get_serializer(data=request.data)
|
seri = self.get_serializer(data=request.data)
|
||||||
seri.is_valid(raise_exception=True)
|
seri.is_valid(raise_exception=True)
|
||||||
SiteMessage.send_msg(**seri.validated_data, sender=request.user)
|
SiteMessageUtil.send_msg(**seri.validated_data, sender=request.user)
|
||||||
return Response({'detail': 'ok'})
|
return Response({'detail': 'ok'})
|
||||||
|
@ -3,3 +3,7 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class NotificationsConfig(AppConfig):
|
class NotificationsConfig(AppConfig):
|
||||||
name = 'notifications'
|
name = 'notifications'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
from . import signals_handler
|
||||||
|
super().ready()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from notifications.site_msg import SiteMessage as Client
|
from notifications.site_msg import SiteMessageUtil as Client
|
||||||
from .base import BackendBase
|
from .base import BackendBase
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ class Migration(migrations.Migration):
|
|||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='SiteMessage',
|
name='SiteMessageUtil',
|
||||||
fields=[
|
fields=[
|
||||||
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||||
('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
|
('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
|
||||||
|
43
apps/notifications/signals_handler.py
Normal file
43
apps/notifications/signals_handler.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.utils.functional import LazyObject
|
||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.dispatch import receiver
|
||||||
|
|
||||||
|
from common.utils.connection import RedisPubSub
|
||||||
|
from common.utils import get_logger
|
||||||
|
from common.decorator import on_transaction_commit
|
||||||
|
from .models import SiteMessage
|
||||||
|
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def new_site_msg_pub_sub():
|
||||||
|
return RedisPubSub('notifications.SiteMessageCome')
|
||||||
|
|
||||||
|
|
||||||
|
class NewSiteMsgSubPub(LazyObject):
|
||||||
|
def _setup(self):
|
||||||
|
self._wrapped = new_site_msg_pub_sub()
|
||||||
|
|
||||||
|
|
||||||
|
new_site_msg_chan = NewSiteMsgSubPub()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=SiteMessage)
|
||||||
|
@on_transaction_commit
|
||||||
|
def on_site_message_create(sender, instance, created, **kwargs):
|
||||||
|
if not created:
|
||||||
|
return
|
||||||
|
logger.debug('New site msg created, publish it')
|
||||||
|
user_ids = instance.users.all().values_list('id', flat=True)
|
||||||
|
user_ids = [str(i) for i in user_ids]
|
||||||
|
data = {
|
||||||
|
'id': str(instance.id),
|
||||||
|
'subject': instance.subject,
|
||||||
|
'message': instance.message,
|
||||||
|
'users': user_ids
|
||||||
|
}
|
||||||
|
data = json.dumps(data)
|
||||||
|
new_site_msg_chan.publish(data)
|
@ -1,11 +1,12 @@
|
|||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
from common.utils.timezone import now
|
from common.utils.timezone import now
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from .models import SiteMessage as SiteMessageModel, SiteMessageUsers
|
from .models import SiteMessage as SiteMessageModel, SiteMessageUsers
|
||||||
|
|
||||||
|
|
||||||
class SiteMessage:
|
class SiteMessageUtil:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def send_msg(cls, subject, message, user_ids=(), group_ids=(),
|
def send_msg(cls, subject, message, user_ids=(), group_ids=(),
|
||||||
@ -13,24 +14,24 @@ class SiteMessage:
|
|||||||
if not any((user_ids, group_ids, is_broadcast)):
|
if not any((user_ids, group_ids, is_broadcast)):
|
||||||
raise ValueError('No recipient is specified')
|
raise ValueError('No recipient is specified')
|
||||||
|
|
||||||
site_msg = SiteMessageModel.objects.create(
|
with transaction.atomic():
|
||||||
subject=subject, message=message,
|
site_msg = SiteMessageModel.objects.create(
|
||||||
is_broadcast=is_broadcast, sender=sender,
|
subject=subject, message=message,
|
||||||
)
|
is_broadcast=is_broadcast, sender=sender,
|
||||||
|
)
|
||||||
|
|
||||||
if is_broadcast:
|
if is_broadcast:
|
||||||
user_ids = User.objects.all().values_list('id', flat=True)
|
user_ids = User.objects.all().values_list('id', flat=True)
|
||||||
else:
|
else:
|
||||||
if group_ids:
|
if group_ids:
|
||||||
site_msg.groups.add(*group_ids)
|
site_msg.groups.add(*group_ids)
|
||||||
|
|
||||||
user_ids_from_group = User.groups.through.objects.filter(
|
user_ids_from_group = User.groups.through.objects.filter(
|
||||||
usergroup_id__in=group_ids
|
usergroup_id__in=group_ids
|
||||||
).values_list('user_id', flat=True)
|
).values_list('user_id', flat=True)
|
||||||
|
user_ids = [*user_ids, *user_ids_from_group]
|
||||||
|
|
||||||
user_ids = [*user_ids, *user_ids_from_group]
|
site_msg.users.add(*user_ids)
|
||||||
|
|
||||||
site_msg.users.add(*user_ids)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_user_all_msgs(cls, user_id):
|
def get_user_all_msgs(cls, user_id):
|
||||||
@ -72,14 +73,14 @@ class SiteMessage:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mark_msgs_as_read(cls, user_id, msg_ids):
|
def mark_msgs_as_read(cls, user_id, msg_ids):
|
||||||
sitemsg_users = SiteMessageUsers.objects.filter(
|
site_msg_users = SiteMessageUsers.objects.filter(
|
||||||
user_id=user_id, sitemessage_id__in=msg_ids,
|
user_id=user_id, sitemessage_id__in=msg_ids,
|
||||||
has_read=False
|
has_read=False
|
||||||
)
|
)
|
||||||
|
|
||||||
for sitemsg_user in sitemsg_users:
|
for site_msg_user in site_msg_users:
|
||||||
sitemsg_user.has_read = True
|
site_msg_user.has_read = True
|
||||||
sitemsg_user.read_at = now()
|
site_msg_user.read_at = now()
|
||||||
|
|
||||||
SiteMessageUsers.objects.bulk_update(
|
SiteMessageUsers.objects.bulk_update(
|
||||||
sitemsg_users, fields=('has_read', 'read_at'))
|
site_msg_users, fields=('has_read', 'read_at'))
|
||||||
|
9
apps/notifications/urls/ws_urls.py
Normal file
9
apps/notifications/urls/ws_urls.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from .. import ws
|
||||||
|
|
||||||
|
app_name = 'notifications'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('ws/notifications/site-msg/', ws.SiteMsgWebsocket, name='site-msg-ws'),
|
||||||
|
]
|
70
apps/notifications/ws.py
Normal file
70
apps/notifications/ws.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import threading
|
||||||
|
import json
|
||||||
|
|
||||||
|
from channels.generic.websocket import JsonWebsocketConsumer
|
||||||
|
|
||||||
|
from common.utils import get_logger
|
||||||
|
from .models import SiteMessage
|
||||||
|
from .site_msg import SiteMessageUtil
|
||||||
|
from .signals_handler import new_site_msg_chan
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SiteMsgWebsocket(JsonWebsocketConsumer):
|
||||||
|
disconnected = False
|
||||||
|
refresh_every_seconds = 10
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
user = self.scope["user"]
|
||||||
|
if user.is_authenticated:
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
thread = threading.Thread(target=self.unread_site_msg_count)
|
||||||
|
thread.start()
|
||||||
|
else:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def receive(self, text_data=None, bytes_data=None, **kwargs):
|
||||||
|
data = json.loads(text_data)
|
||||||
|
refresh_every_seconds = data.get('refresh_every_seconds')
|
||||||
|
|
||||||
|
try:
|
||||||
|
refresh_every_seconds = int(refresh_every_seconds)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return
|
||||||
|
|
||||||
|
if refresh_every_seconds > 0:
|
||||||
|
self.refresh_every_seconds = refresh_every_seconds
|
||||||
|
|
||||||
|
def send_unread_msg_count(self):
|
||||||
|
user_id = self.scope["user"].id
|
||||||
|
unread_count = SiteMessageUtil.get_user_unread_msgs_count(user_id)
|
||||||
|
logger.debug('Send unread count to user: {} {}'.format(user_id, unread_count))
|
||||||
|
self.send_json({'type': 'unread_count', 'unread_count': unread_count})
|
||||||
|
|
||||||
|
def unread_site_msg_count(self):
|
||||||
|
user_id = str(self.scope["user"].id)
|
||||||
|
self.send_unread_msg_count()
|
||||||
|
|
||||||
|
while not self.disconnected:
|
||||||
|
subscribe = new_site_msg_chan.subscribe()
|
||||||
|
for message in subscribe.listen():
|
||||||
|
if message['type'] != 'message':
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
msg = json.loads(message['data'].decode())
|
||||||
|
logger.debug('New site msg recv, may be mine: {}'.format(msg))
|
||||||
|
if not msg:
|
||||||
|
continue
|
||||||
|
users = msg.get('users', [])
|
||||||
|
logger.debug('Message users: {}'.format(users))
|
||||||
|
if user_id in users:
|
||||||
|
self.send_unread_msg_count()
|
||||||
|
except json.JSONDecoder as e:
|
||||||
|
logger.debug('Decode json error: ', e)
|
||||||
|
|
||||||
|
def disconnect(self, close_code):
|
||||||
|
self.disconnected = True
|
||||||
|
self.close()
|
Loading…
Reference in New Issue
Block a user