diff --git a/Dockerfile b/Dockerfile
index a45e41a45..6569db2a3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,7 +10,7 @@ RUN yum -y install epel-release && \
echo -e "[mysql]\nname=mysql\nbaseurl=https://mirrors.tuna.tsinghua.edu.cn/mysql/yum/mysql57-community-el6/\ngpgcheck=0\nenabled=1" > /etc/yum.repos.d/mysql.repo
RUN cd /tmp/requirements && yum -y install $(cat rpm_requirements.txt)
RUN cd /tmp/requirements && pip install --upgrade pip setuptools && pip install wheel && \
- pip install -i https://mirrors.aliyun.com/pypi/simple/ -r requirements.txt || pip install -r requirements.txt
+ pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt || pip install -r requirements.txt
RUN mkdir -p /root/.ssh/ && echo -e "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null" > /root/.ssh/config
COPY . /opt/jumpserver
diff --git a/apps/assets/utils.py b/apps/assets/utils.py
index 3238e8fca..6b4d8111a 100644
--- a/apps/assets/utils.py
+++ b/apps/assets/utils.py
@@ -1,6 +1,5 @@
# ~*~ coding: utf-8 ~*~
#
-import time
from treelib import Tree
from treelib.exceptions import NodeIDAbsentError
from collections import defaultdict
@@ -77,21 +76,9 @@ class TreeService(Tree):
ancestor_ids.pop(0)
return ancestor_ids
- def ancestors(self, nid, with_self=False, deep=False, with_assets=True):
- # now = time.time()
- # print("Start get ancestor_ids")
+ def ancestors(self, nid, with_self=False, deep=False):
ancestor_ids = self.ancestors_ids(nid, with_self=with_self)
- # now2 = time.time()
- # interval = (now2 - now) * 1000
- # print("Start get ancestor_ids using: {}".format(interval))
ancestors = [self.get_node(i, deep=deep) for i in ancestor_ids]
- # interval = (time.time() - now2) * 1000
- # print("Get ancestors done: {}".format(interval))
- if with_assets:
- return ancestors
- for n in ancestors:
- n.data['assets'] = set()
- n.data['all_assets'] = None
return ancestors
def get_node_full_tag(self, nid):
@@ -116,6 +103,7 @@ class TreeService(Tree):
node = super().get_node(nid)
if deep:
node = self.copy_node(node)
+ node.data = {}
return node
def parent(self, nid, deep=False):
diff --git a/apps/audits/signals_handler.py b/apps/audits/signals_handler.py
index dab56fa5c..b95f6fbdf 100644
--- a/apps/audits/signals_handler.py
+++ b/apps/audits/signals_handler.py
@@ -136,8 +136,8 @@ def on_user_auth_success(sender, user, request, **kwargs):
@receiver(post_auth_failed)
-def on_user_auth_failed(sender, username, request, reason, **kwargs):
+def on_user_auth_failed(sender, username, request, reason='', **kwargs):
logger.debug('User login failed: {}'.format(username))
data = generate_data(username, request)
- data.update({'reason': reason, 'status': False})
+ data.update({'reason': reason[:128], 'status': False})
write_login_log(**data)
diff --git a/apps/authentication/backends/openid.py b/apps/authentication/backends/openid.py
new file mode 100644
index 000000000..a82161b8e
--- /dev/null
+++ b/apps/authentication/backends/openid.py
@@ -0,0 +1,4 @@
+"""
+使用下面的工程,进行jumpserver 的 oidc 认证
+https://github.com/BaiJiangJie/jumpserver-django-oidc-rp
+"""
\ No newline at end of file
diff --git a/apps/authentication/backends/openid/__init__.py b/apps/authentication/backends/openid/__init__.py
deleted file mode 100644
index 9ed3bea78..000000000
--- a/apps/authentication/backends/openid/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-
-from .backends import *
-from .middleware import *
-from .utils import *
-from .decorator import *
diff --git a/apps/authentication/backends/openid/backends.py b/apps/authentication/backends/openid/backends.py
deleted file mode 100644
index 938566e2a..000000000
--- a/apps/authentication/backends/openid/backends.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# coding:utf-8
-#
-
-from django.contrib.auth import get_user_model
-from django.conf import settings
-
-from common.utils import get_logger
-from .utils import new_client
-from .models import OIDT_ACCESS_TOKEN
-
-UserModel = get_user_model()
-
-logger = get_logger(__file__)
-client = new_client()
-
-
-__all__ = [
- 'OpenIDAuthorizationCodeBackend', 'OpenIDAuthorizationPasswordBackend',
-]
-
-
-class BaseOpenIDAuthorizationBackend(object):
- @staticmethod
- def user_can_authenticate(user):
- """
- Reject users with is_active=False. Custom user models that don't have
- that attribute are allowed.
- """
- is_valid = getattr(user, 'is_valid', None)
- return is_valid or is_valid is None
-
- def get_user(self, user_id):
- try:
- user = UserModel._default_manager.get(pk=user_id)
- except UserModel.DoesNotExist:
- return None
-
- return user if self.user_can_authenticate(user) else None
-
-
-class OpenIDAuthorizationCodeBackend(BaseOpenIDAuthorizationBackend):
- def authenticate(self, request, **kwargs):
- logger.info('Authentication OpenID code backend')
- code = kwargs.get('code')
- redirect_uri = kwargs.get('redirect_uri')
- if not code or not redirect_uri:
- logger.info('Authenticate failed: No code or No redirect uri')
- return None
- try:
- oidt_profile = client.update_or_create_from_code(
- code=code, redirect_uri=redirect_uri
- )
- except Exception as e:
- logger.info('Authenticate failed: get oidt_profile: {}'.format(e))
- return None
- else:
- # Check openid user single logout or not with access_token
- request.session[OIDT_ACCESS_TOKEN] = oidt_profile.access_token
- user = oidt_profile.user
- logger.info('Authenticate success: user -> {}'.format(user))
- return user if self.user_can_authenticate(user) else None
-
-
-class OpenIDAuthorizationPasswordBackend(BaseOpenIDAuthorizationBackend):
- def authenticate(self, request, username=None, password=None, **kwargs):
- logger.info('Authentication OpenID password backend')
- if not username:
- logger.info('Authenticate failed: Not username')
- return None
- try:
- oidt_profile = client.update_or_create_from_password(
- username=username, password=password
- )
- except Exception as e:
- logger.error(e, exc_info=True)
- logger.info('Authenticate failed: get oidt_profile: {}'.format(e))
- return None
- else:
- user = oidt_profile.user
- logger.info('Authenticate success: user -> {}'.format(user))
- return user if self.user_can_authenticate(user) else None
-
diff --git a/apps/authentication/backends/openid/decorator.py b/apps/authentication/backends/openid/decorator.py
deleted file mode 100644
index 7286b7a2f..000000000
--- a/apps/authentication/backends/openid/decorator.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# coding: utf-8
-#
-
-import warnings
-import contextlib
-
-import requests
-from urllib3.exceptions import InsecureRequestWarning
-from django.conf import settings
-
-__all__ = [
- 'ssl_verification',
-]
-
-old_merge_environment_settings = requests.Session.merge_environment_settings
-
-
-@contextlib.contextmanager
-def no_ssl_verification():
- """
- https://stackoverflow.com/questions/15445981/
- how-do-i-disable-the-security-certificate-check-in-python-requests
- """
- opened_adapters = set()
-
- def merge_environment_settings(self, url, proxies, stream, verify, cert):
- # Verification happens only once per connection so we need to close
- # all the opened adapters once we're done. Otherwise, the effects of
- # verify=False persist beyond the end of this context manager.
- opened_adapters.add(self.get_adapter(url))
- _settings = old_merge_environment_settings(
- self, url, proxies, stream, verify, cert
- )
- _settings['verify'] = False
- return _settings
-
- requests.Session.merge_environment_settings = merge_environment_settings
- try:
- with warnings.catch_warnings():
- warnings.simplefilter('ignore', InsecureRequestWarning)
- yield
- finally:
- requests.Session.merge_environment_settings = old_merge_environment_settings
- for adapter in opened_adapters:
- try:
- adapter.close()
- except:
- pass
-
-
-def ssl_verification(func):
- def wrapper(*args, **kwargs):
- if not settings.AUTH_OPENID_IGNORE_SSL_VERIFICATION:
- return func(*args, **kwargs)
- with no_ssl_verification():
- return func(*args, **kwargs)
- return wrapper
diff --git a/apps/authentication/backends/openid/middleware.py b/apps/authentication/backends/openid/middleware.py
deleted file mode 100644
index bacb4858c..000000000
--- a/apps/authentication/backends/openid/middleware.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# coding:utf-8
-#
-
-from django.conf import settings
-from django.contrib.auth import logout
-from django.utils.deprecation import MiddlewareMixin
-from django.contrib.auth import BACKEND_SESSION_KEY
-
-from common.utils import get_logger
-from .utils import new_client
-from .models import OIDT_ACCESS_TOKEN
-
-BACKEND_OPENID_AUTH_CODE = 'OpenIDAuthorizationCodeBackend'
-logger = get_logger(__file__)
-__all__ = ['OpenIDAuthenticationMiddleware']
-
-
-class OpenIDAuthenticationMiddleware(MiddlewareMixin):
- """
- Check openid user single logout (with access_token)
- """
- def process_request(self, request):
- # Don't need openid auth if AUTH_OPENID is False
- if not settings.AUTH_OPENID:
- return
- # Don't need openid auth if no shared session enabled
- if not settings.AUTH_OPENID_SHARE_SESSION:
- return
- # Don't need check single logout if user not authenticated
- if not request.user.is_authenticated:
- return
- elif not request.session[BACKEND_SESSION_KEY].endswith(
- BACKEND_OPENID_AUTH_CODE):
- return
- # Check openid user single logout or not with access_token
- try:
- client = new_client()
- client.get_userinfo(token=request.session.get(OIDT_ACCESS_TOKEN))
- except Exception as e:
- logout(request)
- logger.error(e)
diff --git a/apps/authentication/backends/openid/models.py b/apps/authentication/backends/openid/models.py
deleted file mode 100644
index a945e8eb3..000000000
--- a/apps/authentication/backends/openid/models.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-
-from django.db import transaction
-from django.contrib.auth import get_user_model
-from keycloak.realm import KeycloakRealm
-from keycloak.keycloak_openid import KeycloakOpenID
-from users.utils import construct_user_email
-
-from .signals import post_create_or_update_openid_user
-from .decorator import ssl_verification
-
-OIDT_ACCESS_TOKEN = 'oidt_access_token'
-
-
-class Nonce(object):
- """
- The openid-login is stored in cache as a temporary object, recording the
- user's redirect_uri and next_pat
- """
- def __init__(self, redirect_uri, next_path):
- import uuid
- self.state = uuid.uuid4()
- self.redirect_uri = redirect_uri
- self.next_path = next_path
-
-
-class OpenIDTokenProfile(object):
- def __init__(self, user, access_token, refresh_token):
- """
- :param user: User object
- :param access_token:
- :param refresh_token:
- """
- self.user = user
- self.access_token = access_token
- self.refresh_token = refresh_token
-
- def __str__(self):
- return "{}'s OpenID token profile".format(self.user.username)
-
-
-class Client(object):
- def __init__(self, server_url, realm_name, client_id, client_secret):
- self.server_url = server_url
- self.realm_name = realm_name
- self.client_id = client_id
- self.client_secret = client_secret
- self._openid_client = None
- self._realm = None
- self._openid_connect_client = None
-
- @property
- def realm(self):
- if self._realm is None:
- self._realm = KeycloakRealm(
- server_url=self.server_url,
- realm_name=self.realm_name,
- headers={}
- )
- return self._realm
-
- @property
- def openid_connect_client(self):
- """
- :rtype: keycloak.openid_connect.KeycloakOpenidConnect
- """
- if self._openid_connect_client is None:
- self._openid_connect_client = self.realm.open_id_connect(
- client_id=self.client_id,
- client_secret=self.client_secret
- )
- return self._openid_connect_client
-
- @property
- def openid_client(self):
- """
- :rtype: keycloak.keycloak_openid.KeycloakOpenID
- """
- if self._openid_client is None:
- self._openid_client = KeycloakOpenID(
- server_url='%sauth/' % self.server_url,
- realm_name=self.realm_name,
- client_id=self.client_id,
- client_secret_key=self.client_secret,
- )
- return self._openid_client
-
- @ssl_verification
- def get_url(self, name):
- return self.openid_connect_client.get_url(name=name)
-
- def get_url_end_session_endpoint(self):
- return self.get_url(name='end_session_endpoint')
-
- @ssl_verification
- def get_authorization_url(self, redirect_uri, scope, state):
- url = self.openid_connect_client.authorization_url(
- redirect_uri=redirect_uri, scope=scope, state=state
- )
- return url
-
- @ssl_verification
- def get_userinfo(self, token):
- user_info = self.openid_connect_client.userinfo(token=token)
- return user_info
-
- @ssl_verification
- def authorization_code(self, code, redirect_uri):
- token_response = self.openid_connect_client.authorization_code(
- code=code, redirect_uri=redirect_uri
- )
- return token_response
-
- @ssl_verification
- def authorization_password(self, username, password):
- token_response = self.openid_client.token(
- username=username, password=password
- )
- return token_response
-
- def update_or_create_from_code(self, code, redirect_uri):
- """
- Update or create an user based on an authentication code.
- Response as specified in:
- https://tools.ietf.org/html/rfc6749#section-4.1.4
- :param str code: authentication code
- :param str redirect_uri:
- :rtype: OpenIDTokenProfile
- """
- token_response = self.authorization_code(code, redirect_uri)
- return self._update_or_create(token_response=token_response)
-
- def update_or_create_from_password(self, username, password):
- """
- Update or create an user based on an authentication username and password.
- :param str username: authentication username
- :param str password: authentication password
- :return: OpenIDTokenProfile
- """
- token_response = self.authorization_password(username, password)
- return self._update_or_create(token_response=token_response)
-
- def _update_or_create(self, token_response):
- """
- Update or create an user based on a token response.
- `token_response` contains the items returned by the OpenIDConnect Token API
- end-point:
- - id_token
- - access_token
- - expires_in
- - refresh_token
- - refresh_expires_in
- :param dict token_response:
- :rtype: OpenIDTokenProfile
- """
- userinfo = self.get_userinfo(token=token_response['access_token'])
- with transaction.atomic():
- name = userinfo.get('name', '')
- username = userinfo.get('preferred_username', '')
- email = userinfo.get('email', '')
- email = construct_user_email(username, email)
-
- user, created = get_user_model().objects.update_or_create(
- username=username,
- defaults={
- 'name': name, 'email': email,
- 'first_name': userinfo.get('given_name', ''),
- 'last_name': userinfo.get('family_name', ''),
- }
- )
- oidt_profile = OpenIDTokenProfile(
- user=user,
- access_token=token_response['access_token'],
- refresh_token=token_response['refresh_token'],
- )
- if user:
- post_create_or_update_openid_user.send(
- sender=user.__class__, user=user, created=created
- )
-
- return oidt_profile
-
- def __str__(self):
- return self.client_id
diff --git a/apps/authentication/backends/openid/signals.py b/apps/authentication/backends/openid/signals.py
deleted file mode 100644
index ad81bca4a..000000000
--- a/apps/authentication/backends/openid/signals.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.dispatch import Signal
-
-
-post_create_or_update_openid_user = Signal(providing_args=('user',))
-post_openid_login_success = Signal(providing_args=('user', 'request'))
diff --git a/apps/authentication/backends/openid/tests.py b/apps/authentication/backends/openid/tests.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/apps/authentication/backends/openid/urls.py b/apps/authentication/backends/openid/urls.py
deleted file mode 100644
index 019529e12..000000000
--- a/apps/authentication/backends/openid/urls.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-from django.urls import path
-
-from . import views
-
-urlpatterns = [
- path('login/', views.OpenIDLoginView.as_view(), name='openid-login'),
- path('login/complete/', views.OpenIDLoginCompleteView.as_view(),
- name='openid-login-complete'),
-]
diff --git a/apps/authentication/backends/openid/utils.py b/apps/authentication/backends/openid/utils.py
deleted file mode 100644
index 15160d224..000000000
--- a/apps/authentication/backends/openid/utils.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-
-from django.conf import settings
-from .models import Client
-
-__all__ = ['new_client']
-
-
-def new_client():
- """
- :return: authentication.models.Client
- """
- return Client(
- server_url=settings.AUTH_OPENID_SERVER_URL,
- realm_name=settings.AUTH_OPENID_REALM_NAME,
- client_id=settings.AUTH_OPENID_CLIENT_ID,
- client_secret=settings.AUTH_OPENID_CLIENT_SECRET
- )
diff --git a/apps/authentication/backends/openid/views.py b/apps/authentication/backends/openid/views.py
deleted file mode 100644
index 89c935452..000000000
--- a/apps/authentication/backends/openid/views.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-
-import logging
-
-from django.conf import settings
-from django.core.cache import cache
-from django.views.generic.base import RedirectView
-from django.contrib.auth import authenticate, login
-from django.http.response import (
- HttpResponseBadRequest,
- HttpResponseServerError,
- HttpResponseRedirect
-)
-
-from .utils import new_client
-from .models import Nonce
-from .signals import post_openid_login_success
-
-logger = logging.getLogger(__name__)
-client = new_client()
-
-__all__ = ['OpenIDLoginView', 'OpenIDLoginCompleteView']
-
-
-class OpenIDLoginView(RedirectView):
- def get_redirect_url(self, *args, **kwargs):
- redirect_uri = settings.BASE_SITE_URL + \
- str(settings.AUTH_OPENID_LOGIN_COMPLETE_URL)
- nonce = Nonce(
- redirect_uri=redirect_uri,
- next_path=self.request.GET.get('next')
- )
- cache.set(str(nonce.state), nonce, 24*3600)
-
- self.request.session['openid_state'] = str(nonce.state)
- authorization_url = client.get_authorization_url(
- redirect_uri=nonce.redirect_uri,
- scope='code',
- state=str(nonce.state)
- )
- return authorization_url
-
-
-class OpenIDLoginCompleteView(RedirectView):
- def get(self, request, *args, **kwargs):
- if 'error' in request.GET:
- return HttpResponseServerError(self.request.GET['error'])
- if 'code' not in self.request.GET and 'state' not in self.request.GET:
- return HttpResponseBadRequest(content='Code or State is empty')
- if self.request.GET['state'] != self.request.session['openid_state']:
- return HttpResponseBadRequest(content='State invalid')
- nonce = cache.get(self.request.GET['state'])
- if not nonce:
- return HttpResponseBadRequest(content='State failure')
-
- user = authenticate(
- request=self.request,
- code=self.request.GET['code'],
- redirect_uri=nonce.redirect_uri
- )
- cache.delete(str(nonce.state))
- if not user:
- return HttpResponseBadRequest(content='Authenticate user failed')
-
- login(self.request, user)
- post_openid_login_success.send(
- sender=self.__class__, user=user, request=self.request
- )
- return HttpResponseRedirect(nonce.next_path or '/')
-
diff --git a/apps/authentication/errors.py b/apps/authentication/errors.py
index 183e69288..d782a05fc 100644
--- a/apps/authentication/errors.py
+++ b/apps/authentication/errors.py
@@ -93,6 +93,9 @@ class AuthFailedError(Exception):
'msg': self.msg,
}
+ def __str__(self):
+ return str(self.msg)
+
class CredentialError(AuthFailedNeedLogMixin, AuthFailedNeedBlockMixin, AuthFailedError):
def __init__(self, error, username, ip, request):
@@ -168,7 +171,7 @@ class MFARequiredError(NeedMoreInfoError):
'error': self.error,
'msg': self.msg,
'data': {
- 'choices': ['otp'],
+ 'choices': ['code'],
'url': reverse('api-auth:mfa-challenge')
}
}
diff --git a/apps/authentication/mixins.py b/apps/authentication/mixins.py
index 1c4fb5aa1..5b3738c98 100644
--- a/apps/authentication/mixins.py
+++ b/apps/authentication/mixins.py
@@ -62,8 +62,7 @@ class AuthMixin:
password = request.POST.get('password', '')
public_key = request.POST.get('public_key', '')
user, error = check_user_valid(
- username=username, password=password,
- public_key=public_key
+ request=request, username=username, password=password, public_key=public_key
)
ip = self.get_request_ip()
if not user:
diff --git a/apps/authentication/signals_handlers.py b/apps/authentication/signals_handlers.py
index aac64df4c..461ddbb99 100644
--- a/apps/authentication/signals_handlers.py
+++ b/apps/authentication/signals_handlers.py
@@ -1,54 +1,15 @@
-from django.http.request import QueryDict
-from django.conf import settings
from django.dispatch import receiver
-from django.contrib.auth.signals import user_logged_out
-from django_auth_ldap.backend import populate_user
-from users.models import User
-from .backends.openid import new_client
-from .backends.openid.signals import (
- post_create_or_update_openid_user, post_openid_login_success
-)
-from .signals import post_auth_success
+from jms_oidc_rp.signals import openid_user_login_failed, openid_user_login_success
+from .signals import post_auth_success, post_auth_failed
-@receiver(user_logged_out)
-def on_user_logged_out(sender, request, user, **kwargs):
- if not settings.AUTH_OPENID:
- return
- if not settings.AUTH_OPENID_SHARE_SESSION:
- return
- query = QueryDict('', mutable=True)
- query.update({
- 'redirect_uri': settings.BASE_SITE_URL
- })
- client = new_client()
- openid_logout_url = "%s?%s" % (
- client.get_url_end_session_endpoint(),
- query.urlencode()
- )
- request.COOKIES['next'] = openid_logout_url
-
-
-@receiver(post_create_or_update_openid_user)
-def on_post_create_or_update_openid_user(sender, user=None, created=True, **kwargs):
- if created and user and user.username != 'admin':
- user.source = user.SOURCE_OPENID
- user.save()
-
-
-@receiver(post_openid_login_success)
-def on_openid_login_success(sender, user=None, request=None, **kwargs):
- post_auth_success.send(sender=sender, user=user, request=request)
-
-
-@receiver(populate_user)
-def on_ldap_create_user(sender, user, ldap_user, **kwargs):
- if user and user.username not in ['admin']:
- exists = User.objects.filter(username=user.username).exists()
- if not exists:
- user.source = user.SOURCE_LDAP
- user.save()
+@receiver(openid_user_login_success)
+def on_oidc_user_login_success(sender, request, user, **kwargs):
+ post_auth_success.send(sender, user=user, request=request)
+@receiver(openid_user_login_failed)
+def on_oidc_user_login_failed(sender, username, request, reason, **kwargs):
+ post_auth_failed.send(sender, username=username, request=request, reason=reason)
diff --git a/apps/authentication/templates/authentication/login.html b/apps/authentication/templates/authentication/login.html
index bff33eb17..9cacdf4ff 100644
--- a/apps/authentication/templates/authentication/login.html
+++ b/apps/authentication/templates/authentication/login.html
@@ -56,9 +56,9 @@
{% trans "More login options" %}
-
+
- {% trans 'Keycloak' %}
+ {% trans 'OpenID' %}
{% endif %}
diff --git a/apps/authentication/urls/view_urls.py b/apps/authentication/urls/view_urls.py
index b9f76e731..bee5f8517 100644
--- a/apps/authentication/urls/view_urls.py
+++ b/apps/authentication/urls/view_urls.py
@@ -16,6 +16,6 @@ urlpatterns = [
path('logout/', views.UserLogoutView.as_view(), name='logout'),
# openid
- path('openid/', include(('authentication.backends.openid.urls', 'authentication'), namespace='openid')),
path('cas/', include(('authentication.backends.cas.urls', 'authentication'), namespace='cas')),
+ path('openid/', include(('jms_oidc_rp.urls', 'authentication'), namespace='openid')),
]
diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py
index c51ccbfc6..c67cf2090 100644
--- a/apps/authentication/views/login.py
+++ b/apps/authentication/views/login.py
@@ -58,7 +58,7 @@ class UserLoginView(mixins.AuthMixin, FormView):
if self.request.GET.get("admin", 0):
return None
if settings.AUTH_OPENID:
- redirect_url = reverse("authentication:openid:openid-login")
+ redirect_url = reverse(settings.AUTH_OPENID_AUTH_LOGIN_URL_NAME)
elif settings.AUTH_CAS:
redirect_url = reverse(settings.CAS_LOGIN_URL_NAME)
@@ -133,7 +133,7 @@ class UserLoginGuardView(mixins.AuthMixin, RedirectView):
user = self.check_user_auth_if_need()
self.check_user_mfa_if_need(user)
self.check_user_login_confirm_if_need(user)
- except errors.CredentialError:
+ except (errors.CredentialError, errors.SessionEmptyError):
return self.format_redirect_url(self.login_url)
except errors.MFARequiredError:
return self.format_redirect_url(self.login_otp_url)
@@ -185,18 +185,18 @@ class UserLogoutView(TemplateView):
@staticmethod
def get_backend_logout_url():
+ if settings.AUTH_OPENID:
+ return settings.AUTH_OPENID_AUTH_LOGOUT_URL_NAME
# if settings.AUTH_CAS:
# return settings.CAS_LOGOUT_URL_NAME
return None
def get(self, request, *args, **kwargs):
- auth_logout(request)
backend_logout_url = self.get_backend_logout_url()
if backend_logout_url:
return redirect(backend_logout_url)
- next_uri = request.COOKIES.get("next")
- if next_uri:
- return redirect(next_uri)
+
+ auth_logout(request)
response = super().get(request, *args, **kwargs)
return response
diff --git a/apps/authentication/views/mfa.py b/apps/authentication/views/mfa.py
index 57d6751da..bedbf9bcf 100644
--- a/apps/authentication/views/mfa.py
+++ b/apps/authentication/views/mfa.py
@@ -6,6 +6,9 @@ from django.views.generic.edit import FormView
from .. import forms, errors, mixins
from .utils import redirect_to_guard_view
+from common.utils import get_logger
+
+logger = get_logger(__name__)
__all__ = ['UserLoginOtpView']
@@ -22,4 +25,7 @@ class UserLoginOtpView(mixins.AuthMixin, FormView):
except errors.MFAFailedError as e:
form.add_error('otp_code', e.msg)
return super().form_invalid(form)
+ except Exception as e:
+ logger.error(e)
+ return redirect_to_guard_view()
diff --git a/apps/jumpserver/api.py b/apps/jumpserver/api.py
new file mode 100644
index 000000000..fd580093e
--- /dev/null
+++ b/apps/jumpserver/api.py
@@ -0,0 +1,295 @@
+from django.core.cache import cache
+from django.utils import timezone
+from django.utils.timesince import timesince
+from django.db.models import Count, Max
+from django.http.response import JsonResponse
+from rest_framework.views import APIView
+from collections import Counter
+
+from users.models import User
+from assets.models import Asset
+from terminal.models import Session
+from orgs.utils import current_org
+from common.permissions import IsOrgAdmin
+from common.utils import lazyproperty
+
+__all__ = ['IndexApi']
+
+
+class MonthLoginMetricMixin:
+
+ @lazyproperty
+ def session_month(self):
+ month_ago = timezone.now() - timezone.timedelta(days=30)
+ session_month = Session.objects.filter(date_start__gt=month_ago)
+ return session_month
+
+ @lazyproperty
+ def session_month_dates(self):
+ dates = self.session_month.dates('date_start', 'day')
+ return dates
+
+ def get_month_metrics_date(self):
+ month_metrics_date = [d.strftime('%m-%d') for d in self.session_month_dates] or ['0']
+ return month_metrics_date
+
+ @staticmethod
+ def get_cache_key(date, tp):
+ date_str = date.strftime("%Y%m%d")
+ key = "SESSION_MONTH_{}_{}_{}".format(current_org.id, tp, date_str)
+ return key
+
+ def __get_data_from_cache(self, date, tp):
+ if date == timezone.now().date():
+ return None
+ cache_key = self.get_cache_key(date, tp)
+ count = cache.get(cache_key)
+ return count
+
+ def __set_data_to_cache(self, date, tp, count):
+ cache_key = self.get_cache_key(date, tp)
+ cache.set(cache_key, count, 3600*24*7)
+
+ @staticmethod
+ def get_date_start_2_end(d):
+ time_min = timezone.datetime.min.time()
+ time_max = timezone.datetime.max.time()
+ tz = timezone.get_current_timezone()
+ ds = timezone.datetime.combine(d, time_min).replace(tzinfo=tz)
+ de = timezone.datetime.combine(d, time_max).replace(tzinfo=tz)
+ return ds, de
+
+ def get_date_login_count(self, date):
+ tp = "LOGIN"
+ count = self.__get_data_from_cache(date, tp)
+ if count is not None:
+ return count
+ ds, de = self.get_date_start_2_end(date)
+ count = Session.objects.filter(date_start__range=(ds, de)).count()
+ self.__set_data_to_cache(date, tp, count)
+ return count
+
+ def get_month_metrics_total_count_login(self):
+ data = []
+ for d in self.session_month_dates:
+ count = self.get_date_login_count(d)
+ data.append(count)
+ if len(data) == 0:
+ data = [0]
+ return data
+
+ def get_date_user_count(self, date):
+ tp = "USER"
+ count = self.__get_data_from_cache(date, tp)
+ if count is not None:
+ return count
+ ds, de = self.get_date_start_2_end(date)
+ count = len(set(Session.objects.filter(date_start__range=(ds, de)).values_list('user', flat=True)))
+ self.__set_data_to_cache(date, tp, count)
+ return count
+
+ def get_month_metrics_total_count_active_users(self):
+ data = []
+ for d in self.session_month_dates:
+ count = self.get_date_user_count(d)
+ data.append(count)
+ return data
+
+ def get_date_asset_count(self, date):
+ tp = "ASSET"
+ count = self.__get_data_from_cache(date, tp)
+ if count is not None:
+ return count
+ ds, de = self.get_date_start_2_end(date)
+ count = len(set(Session.objects.filter(date_start__range=(ds, de)).values_list('asset', flat=True)))
+ self.__set_data_to_cache(date, tp, count)
+ return count
+
+ def get_month_metrics_total_count_active_assets(self):
+ data = []
+ for d in self.session_month_dates:
+ count = self.get_date_asset_count(d)
+ data.append(count)
+ return data
+
+ @lazyproperty
+ def month_total_count_active_users(self):
+ count = len(set(self.session_month.values_list('user', flat=True)))
+ return count
+
+ @lazyproperty
+ def month_total_count_inactive_users(self):
+ total = current_org.get_org_members().count()
+ active = self.month_total_count_active_users
+ count = total - active
+ if count < 0:
+ count = 0
+ return count
+
+ @lazyproperty
+ def month_total_count_disabled_users(self):
+ return current_org.get_org_members().filter(is_active=False).count()
+
+ @lazyproperty
+ def month_total_count_active_assets(self):
+ return len(set(self.session_month.values_list('asset', flat=True)))
+
+ @lazyproperty
+ def month_total_count_inactive_assets(self):
+ total = Asset.objects.all().count()
+ active = self.month_total_count_active_assets
+ count = total - active
+ if count < 0:
+ count = 0
+ return count
+
+ @lazyproperty
+ def month_total_count_disabled_assets(self):
+ return Asset.objects.filter(is_active=False).count()
+
+
+class WeekSessionMetricMixin:
+ session_week = None
+
+ @lazyproperty
+ def session_week(self):
+ week_ago = timezone.now() - timezone.timedelta(weeks=1)
+ session_week = Session.objects.filter(date_start__gt=week_ago)
+ return session_week
+
+ def get_week_login_times_top5_users(self):
+ users = self.session_week.values_list('user', flat=True)
+ users = [
+ {'user': user, 'total': total}
+ for user, total in Counter(users).most_common(5)
+ ]
+ return users
+
+ def get_week_total_count_login_users(self):
+ return len(set(self.session_week.values_list('user', flat=True)))
+
+ def get_week_total_count_login_times(self):
+ return self.session_week.count()
+
+ def get_week_login_times_top10_assets(self):
+ assets = self.session_week.values("asset")\
+ .annotate(total=Count("asset"))\
+ .annotate(last=Max("date_start")).order_by("-total")[:10]
+ for asset in assets:
+ asset['last'] = str(asset['last'])
+ return list(assets)
+
+ def get_week_login_times_top10_users(self):
+ users = self.session_week.values("user") \
+ .annotate(total=Count("user")) \
+ .annotate(last=Max("date_start")).order_by("-total")[:10]
+ for user in users:
+ user['last'] = str(user['last'])
+ return list(users)
+
+ def get_week_login_record_top10_sessions(self):
+ sessions = self.session_week.order_by('-date_start')[:10]
+ for session in sessions:
+ session.avatar_url = User.get_avatar_url("")
+ sessions = [
+ {
+ 'user': session.user,
+ 'asset': session.asset,
+ 'is_finished': session.is_finished,
+ 'date_start': str(session.date_start),
+ 'timesince': timesince(session.date_start)
+ }
+ for session in sessions
+ ]
+ return sessions
+
+
+class TotalCountMixin:
+ @staticmethod
+ def get_total_count_users():
+ return current_org.get_org_members().count()
+
+ @staticmethod
+ def get_total_count_assets():
+ return Asset.objects.all().count()
+
+ @staticmethod
+ def get_total_count_online_users():
+ count = len(set(Session.objects.filter(is_finished=False).values_list('user', flat=True)))
+ return count
+
+ @staticmethod
+ def get_total_count_online_sessions():
+ return Session.objects.filter(is_finished=False).count()
+
+
+class IndexApi(TotalCountMixin, WeekSessionMetricMixin, MonthLoginMetricMixin, APIView):
+ permission_classes = (IsOrgAdmin,)
+ http_method_names = ['get']
+
+ def get(self, request, *args, **kwargs):
+ data = {}
+
+ query_params = self.request.query_params
+
+ _all = query_params.get('all')
+
+ if _all or query_params.get('total_count'):
+ data.update({
+ 'total_count_assets': self.get_total_count_assets(),
+ 'total_count_users': self.get_total_count_users(),
+ 'total_count_online_users': self.get_total_count_online_users(),
+ 'total_count_online_sessions': self.get_total_count_online_sessions(),
+ })
+
+ if _all or query_params.get('month_metrics'):
+ data.update({
+ 'month_metrics_date': self.get_month_metrics_date(),
+ 'month_metrics_total_count_login': self.get_month_metrics_total_count_login(),
+ 'month_metrics_total_count_active_users': self.get_month_metrics_total_count_active_users(),
+ 'month_metrics_total_count_active_assets': self.get_month_metrics_total_count_active_assets(),
+ })
+
+ if _all or query_params.get('month_total_count_users'):
+ data.update({
+ 'month_total_count_active_users': self.month_total_count_active_users,
+ 'month_total_count_inactive_users': self.month_total_count_inactive_users,
+ 'month_total_count_disabled_users': self.month_total_count_disabled_users,
+ })
+
+ if _all or query_params.get('month_total_count_assets'):
+ data.update({
+ 'month_total_count_active_assets': self.month_total_count_active_assets,
+ 'month_total_count_inactive_assets': self.month_total_count_inactive_assets,
+ 'month_total_count_disabled_assets': self.month_total_count_disabled_assets,
+ })
+
+ if _all or query_params.get('week_total_count'):
+ data.update({
+ 'week_total_count_login_users': self.get_week_total_count_login_users(),
+ 'week_total_count_login_times': self.get_week_total_count_login_times(),
+ })
+
+ if _all or query_params.get('week_login_times_top5_users'):
+ data.update({
+ 'week_login_times_top5_users': self.get_week_login_times_top5_users(),
+ })
+
+ if _all or query_params.get('week_login_times_top10_assets'):
+ data.update({
+ 'week_login_times_top10_assets': self.get_week_login_times_top10_assets(),
+ })
+
+ if _all or query_params.get('week_login_times_top10_users'):
+ data.update({
+ 'week_login_times_top10_users': self.get_week_login_times_top10_users(),
+ })
+
+ if _all or query_params.get('week_login_record_top10_sessions'):
+ data.update({
+ 'week_login_record_top10_sessions': self.get_week_login_record_top10_sessions()
+ })
+
+ return JsonResponse(data, status=200)
+
+
diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py
index 04988d1d9..04002a87f 100644
--- a/apps/jumpserver/conf.py
+++ b/apps/jumpserver/conf.py
@@ -8,6 +8,7 @@
3. 程序需要, 用户需要更改的写到本config中
"""
import os
+import re
import sys
import types
import errno
@@ -15,9 +16,12 @@ import json
import yaml
from importlib import import_module
from django.urls import reverse_lazy
+from urllib.parse import urljoin, urlparse
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_DIR = os.path.dirname(BASE_DIR)
+XPACK_DIR = os.path.join(BASE_DIR, 'xpack')
+HAS_XPACK = os.path.isdir(XPACK_DIR)
def import_string(dotted_path):
@@ -36,6 +40,38 @@ def import_string(dotted_path):
) from err
+def is_absolute_uri(uri):
+ """ 判断一个uri是否是绝对地址 """
+ if not isinstance(uri, str):
+ return False
+
+ result = re.match(r'^http[s]?://.*', uri)
+ if result is None:
+ return False
+
+ return True
+
+
+def build_absolute_uri(base, uri):
+ """ 构建绝对uri地址 """
+ if uri is None:
+ return base
+
+ if isinstance(uri, int):
+ uri = str(uri)
+
+ if not isinstance(uri, str):
+ return base
+
+ if is_absolute_uri(uri):
+ return uri
+
+ parsed_base = urlparse(base)
+ url = "{}://{}".format(parsed_base.scheme, parsed_base.netloc)
+ path = '{}/{}/'.format(parsed_base.path.strip('/'), uri.strip('/'))
+ return urljoin(url, path)
+
+
class DoesNotExist(Exception):
pass
@@ -134,14 +170,32 @@ class Config(dict):
'AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS': False,
'AUTH_LDAP_OPTIONS_OPT_REFERRALS': -1,
+ # OpenID 配置参数
+ # OpenID 公有配置参数 (version <= 1.5.8 或 version >= 1.5.8)
'AUTH_OPENID': False,
+ 'AUTH_OPENID_CLIENT_ID': 'client-id',
+ 'AUTH_OPENID_CLIENT_SECRET': 'client-secret',
+ 'AUTH_OPENID_SHARE_SESSION': True,
+ 'AUTH_OPENID_IGNORE_SSL_VERIFICATION': True,
+ # OpenID 新配置参数 (version >= 1.5.8)
+ 'AUTH_OPENID_PROVIDER_ENDPOINT': 'https://op-example.com/',
+ 'AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT': 'https://op-example.com/authorize',
+ 'AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT': 'https://op-example.com/token',
+ 'AUTH_OPENID_PROVIDER_JWKS_ENDPOINT': 'https://op-example.com/jwks',
+ 'AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT': 'https://op-example.com/userinfo',
+ 'AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT': 'https://op-example.com/logout',
+ 'AUTH_OPENID_PROVIDER_SIGNATURE_ALG': 'HS256',
+ 'AUTH_OPENID_PROVIDER_SIGNATURE_KEY': None,
+ 'AUTH_OPENID_SCOPES': 'openid profile email',
+ 'AUTH_OPENID_ID_TOKEN_MAX_AGE': 60,
+ 'AUTH_OPENID_ID_TOKEN_INCLUDE_CLAIMS': True,
+ 'AUTH_OPENID_USE_STATE': True,
+ 'AUTH_OPENID_USE_NONCE': True,
+ 'AUTH_OPENID_ALWAYS_UPDATE_USER': True,
+ # OpenID 旧配置参数 (version <= 1.5.8 (discarded))
'BASE_SITE_URL': 'http://localhost:8080',
'AUTH_OPENID_SERVER_URL': 'http://openid',
- 'AUTH_OPENID_REALM_NAME': 'jumpserver',
- 'AUTH_OPENID_CLIENT_ID': 'jumpserver',
- 'AUTH_OPENID_CLIENT_SECRET': '',
- 'AUTH_OPENID_IGNORE_SSL_VERIFICATION': True,
- 'AUTH_OPENID_SHARE_SESSION': True,
+ 'AUTH_OPENID_REALM_NAME': None,
'AUTH_RADIUS': False,
'RADIUS_SERVER': 'localhost',
@@ -190,7 +244,7 @@ class Config(dict):
'TASK_LOG_KEEP_DAYS': 10,
'ASSETS_PERM_CACHE_TIME': 3600 * 24,
'SECURITY_MFA_VERIFY_TTL': 3600,
- 'ASSETS_PERM_CACHE_ENABLE': False,
+ 'ASSETS_PERM_CACHE_ENABLE': HAS_XPACK,
'SYSLOG_ADDR': '', # '192.168.0.1:514'
'SYSLOG_FACILITY': 'user',
'SYSLOG_SOCKTYPE': 2,
@@ -207,6 +261,88 @@ class Config(dict):
'TIME_ZONE': 'Asia/Shanghai'
}
+ def compatible_auth_openid_of_key(self):
+ """
+ 兼容OpenID旧配置 (即 version <= 1.5.8)
+ 因为旧配置只支持OpenID协议的Keycloak实现,
+ 所以只需要根据旧配置和Keycloak的Endpoint说明文档,
+ 构造出新配置中标准OpenID协议中所需的Endpoint即可
+ (Keycloak说明文档参考: https://www.keycloak.org/docs/latest/securing_apps/)
+ """
+ if not self.AUTH_OPENID:
+ return
+
+ realm_name = self.AUTH_OPENID_REALM_NAME
+ if realm_name is None:
+ return
+
+ compatible_keycloak_config = [
+ (
+ 'AUTH_OPENID_PROVIDER_ENDPOINT',
+ self.AUTH_OPENID_SERVER_URL
+ ),
+ (
+ 'AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT',
+ '/realms/{}/protocol/openid-connect/auth'.format(realm_name)
+ ),
+ (
+ 'AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT',
+ '/realms/{}/protocol/openid-connect/token'.format(realm_name)
+ ),
+ (
+ 'AUTH_OPENID_PROVIDER_JWKS_ENDPOINT',
+ '/realms/{}/protocol/openid-connect/certs'.format(realm_name)
+ ),
+ (
+ 'AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT',
+ '/realms/{}/protocol/openid-connect/userinfo'.format(realm_name)
+ ),
+ (
+ 'AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT',
+ '/realms/{}/protocol/openid-connect/logout'.format(realm_name)
+ )
+ ]
+ for key, value in compatible_keycloak_config:
+ self[key] = value
+
+ def compatible_auth_openid_of_value(self):
+ """
+ 兼容值的绝对路径、相对路径
+ (key 为 AUTH_OPENID_PROVIDER_*_ENDPOINT 的配置)
+ """
+ if not self.AUTH_OPENID:
+ return
+
+ base = self.AUTH_OPENID_PROVIDER_ENDPOINT
+ config = list(self.items())
+ for key, value in config:
+ result = re.match(r'^AUTH_OPENID_PROVIDER_.*_ENDPOINT$', key)
+ if result is None:
+ continue
+ if value is None:
+ # None 在 url 中有特殊含义 (比如对于: end_session_endpoint)
+ continue
+ value = build_absolute_uri(base, value)
+ self[key] = value
+
+ def compatible(self):
+ """
+ 对配置做兼容处理
+ 1. 对`key`的兼容 (例如:版本升级)
+ 2. 对`value`做兼容 (例如:True、true、1 => True)
+
+ 处理顺序要保持先对key做处理, 再对value做处理,
+ 因为处理value的时候,只根据最新版本支持的key进行
+ """
+ parts = ['key', 'value']
+ targets = ['auth_openid']
+ for part in parts:
+ for target in targets:
+ method_name = 'compatible_{}_of_{}'.format(target, part)
+ method = getattr(self, method_name, None)
+ if method is not None:
+ method()
+
def convert_type(self, k, v):
default_value = self.defaults.get(k)
if default_value is None:
@@ -283,9 +419,6 @@ class DynamicConfig:
return lambda: self.get(item)
def LOGIN_URL(self):
- auth_openid = self.get('AUTH_OPENID')
- if auth_openid:
- return reverse_lazy("authentication:openid:openid-login")
return self.get('LOGIN_URL')
def AUTHENTICATION_BACKENDS(self):
@@ -298,8 +431,8 @@ class DynamicConfig:
if self.static_config.get('AUTH_CAS'):
backends.insert(0, 'authentication.backends.cas.CASBackend')
if self.static_config.get('AUTH_OPENID'):
- backends.insert(0, 'authentication.backends.openid.backends.OpenIDAuthorizationPasswordBackend')
- backends.insert(0, 'authentication.backends.openid.backends.OpenIDAuthorizationCodeBackend')
+ backends.insert(0, 'jms_oidc_rp.backends.OIDCAuthPasswordBackend')
+ backends.insert(0, 'jms_oidc_rp.backends.OIDCAuthCodeBackend')
if self.static_config.get('AUTH_RADIUS'):
backends.insert(0, 'authentication.backends.radius.RadiusBackend')
return backends
@@ -480,9 +613,9 @@ class ConfigManager:
manager = cls(root_path=root_path)
if manager.load_from_object():
- return manager.config
+ config = manager.config
elif manager.load_from_yml():
- return manager.config
+ config = manager.config
else:
msg = """
@@ -492,6 +625,10 @@ class ConfigManager:
"""
raise ImportError(msg)
+ # 对config进行兼容处理
+ config.compatible()
+ return config
+
@classmethod
def get_dynamic_config(cls, config):
return DynamicConfig(config)
diff --git a/apps/jumpserver/settings/auth.py b/apps/jumpserver/settings/auth.py
index bad698e4c..516671980 100644
--- a/apps/jumpserver/settings/auth.py
+++ b/apps/jumpserver/settings/auth.py
@@ -2,7 +2,6 @@
#
import os
import ldap
-from django.urls import reverse_lazy
from ..const import CONFIG, DYNAMIC, PROJECT_DIR
@@ -43,19 +42,35 @@ AUTH_LDAP_SYNC_INTERVAL = CONFIG.AUTH_LDAP_SYNC_INTERVAL
AUTH_LDAP_SYNC_CRONTAB = CONFIG.AUTH_LDAP_SYNC_CRONTAB
AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS = CONFIG.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS
-# openid
-# Auth OpenID settings
-BASE_SITE_URL = CONFIG.BASE_SITE_URL
+
+# ==============================================================================
+# 认证 OpenID 配置参数
+# 参考: https://django-oidc-rp.readthedocs.io/en/stable/settings.html
+# ==============================================================================
AUTH_OPENID = CONFIG.AUTH_OPENID
-AUTH_OPENID_SERVER_URL = CONFIG.AUTH_OPENID_SERVER_URL
-AUTH_OPENID_REALM_NAME = CONFIG.AUTH_OPENID_REALM_NAME
AUTH_OPENID_CLIENT_ID = CONFIG.AUTH_OPENID_CLIENT_ID
AUTH_OPENID_CLIENT_SECRET = CONFIG.AUTH_OPENID_CLIENT_SECRET
-AUTH_OPENID_IGNORE_SSL_VERIFICATION = CONFIG.AUTH_OPENID_IGNORE_SSL_VERIFICATION
-AUTH_OPENID_SHARE_SESSION = CONFIG.AUTH_OPENID_SHARE_SESSION
-AUTH_OPENID_LOGIN_URL = reverse_lazy("authentication:openid:openid-login")
-AUTH_OPENID_LOGIN_COMPLETE_URL = reverse_lazy("authentication:openid:openid-login-complete")
+AUTH_OPENID_PROVIDER_ENDPOINT = CONFIG.AUTH_OPENID_PROVIDER_ENDPOINT
+AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT = CONFIG.AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT
+AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT = CONFIG.AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT
+AUTH_OPENID_PROVIDER_JWKS_ENDPOINT = CONFIG.AUTH_OPENID_PROVIDER_JWKS_ENDPOINT
+AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT = CONFIG.AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT
+AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT = CONFIG.AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT
+AUTH_OPENID_PROVIDER_SIGNATURE_ALG = CONFIG.AUTH_OPENID_PROVIDER_SIGNATURE_ALG
+AUTH_OPENID_PROVIDER_SIGNATURE_KEY = CONFIG.AUTH_OPENID_PROVIDER_SIGNATURE_KEY
+AUTH_OPENID_SCOPES = CONFIG.AUTH_OPENID_SCOPES
+AUTH_OPENID_ID_TOKEN_MAX_AGE = CONFIG.AUTH_OPENID_ID_TOKEN_MAX_AGE
+AUTH_OPENID_ID_TOKEN_INCLUDE_CLAIMS = CONFIG.AUTH_OPENID_ID_TOKEN_INCLUDE_CLAIMS
+AUTH_OPENID_USE_STATE = CONFIG.AUTH_OPENID_USE_STATE
+AUTH_OPENID_USE_NONCE = CONFIG.AUTH_OPENID_USE_NONCE
+AUTH_OPENID_SHARE_SESSION = CONFIG.AUTH_OPENID_SHARE_SESSION
+AUTH_OPENID_IGNORE_SSL_VERIFICATION = CONFIG.AUTH_OPENID_IGNORE_SSL_VERIFICATION
+AUTH_OPENID_ALWAYS_UPDATE_USER = CONFIG.AUTH_OPENID_ALWAYS_UPDATE_USER
+AUTH_OPENID_AUTH_LOGIN_URL_NAME = 'authentication:openid:login'
+AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:openid:login-callback'
+AUTH_OPENID_AUTH_LOGOUT_URL_NAME = 'authentication:openid:logout'
+# ==============================================================================
# Radius Auth
AUTH_RADIUS = CONFIG.AUTH_RADIUS
diff --git a/apps/jumpserver/settings/base.py b/apps/jumpserver/settings/base.py
index b4757ff99..0f8cf98a8 100644
--- a/apps/jumpserver/settings/base.py
+++ b/apps/jumpserver/settings/base.py
@@ -48,6 +48,7 @@ INSTALLED_APPS = [
'authentication.apps.AuthenticationConfig', # authentication
'applications.apps.ApplicationsConfig',
'tickets.apps.TicketsConfig',
+ 'jms_oidc_rp',
'rest_framework',
'rest_framework_swagger',
'drf_yasg',
@@ -75,7 +76,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'authentication.backends.openid.middleware.OpenIDAuthenticationMiddleware',
+ 'jms_oidc_rp.middleware.OIDCRefreshIDTokenMiddleware',
'django_cas_ng.middleware.CASMiddleware',
'jumpserver.middleware.TimezoneMiddleware',
'jumpserver.middleware.DemoMiddleware',
@@ -103,6 +104,7 @@ TEMPLATES = [
'django.template.context_processors.media',
'jumpserver.context_processor.jumpserver_processor',
'orgs.context_processor.org_processor',
+ 'jms_oidc_rp.context_processors.oidc',
],
},
},
diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py
index c4653969b..ade854397 100644
--- a/apps/jumpserver/urls.py
+++ b/apps/jumpserver/urls.py
@@ -7,9 +7,10 @@ from django.conf.urls.static import static
from django.conf.urls.i18n import i18n_patterns
from django.views.i18n import JavaScriptCatalog
-from . import views
+from . import views, api
api_v1 = [
+ path('index/', api.IndexApi.as_view()),
path('users/', include('users.urls.api_urls', namespace='api-users')),
path('assets/', include('assets.urls.api_urls', namespace='api-assets')),
path('perms/', include('perms.urls.api_urls', namespace='api-perms')),
diff --git a/apps/jumpserver/utils.py b/apps/jumpserver/utils.py
index aa808d2f2..72a836892 100644
--- a/apps/jumpserver/utils.py
+++ b/apps/jumpserver/utils.py
@@ -18,4 +18,3 @@ def get_current_request():
current_request = LocalProxy(partial(_find, 'current_request'))
-
diff --git a/apps/jumpserver/views/index.py b/apps/jumpserver/views/index.py
index 1bcb2a57e..ffcb10493 100644
--- a/apps/jumpserver/views/index.py
+++ b/apps/jumpserver/views/index.py
@@ -1,224 +1,12 @@
-from django.core.cache import cache
from django.views.generic import TemplateView
-from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
-from django.db.models import Count, Max
from django.shortcuts import redirect
-
-from users.models import User
-from assets.models import Asset
-from terminal.models import Session
-from orgs.utils import current_org
from common.permissions import PermissionsMixin, IsValidUser
-from common.utils import timeit, lazyproperty
__all__ = ['IndexView']
-class MonthLoginMetricMixin:
- @lazyproperty
- def session_month(self):
- month_ago = timezone.now() - timezone.timedelta(days=30)
- session_month = Session.objects.filter(date_start__gt=month_ago)
- return session_month
-
- @lazyproperty
- def session_month_dates(self):
- dates = self.session_month.dates('date_start', 'day')
- return dates
-
- def get_month_day_metrics(self):
- month_str = [
- d.strftime('%m-%d') for d in self.session_month_dates
- ] or ['0']
- return month_str
-
- @staticmethod
- def get_cache_key(date, tp):
- date_str = date.strftime("%Y%m%d")
- key = "SESSION_MONTH_{}_{}".format(tp, date_str)
- return key
-
- def __get_data_from_cache(self, date, tp):
- if date == timezone.now().date():
- return None
- cache_key = self.get_cache_key(date, tp)
- count = cache.get(cache_key)
- return count
-
- def __set_data_to_cache(self, date, tp, count):
- cache_key = self.get_cache_key(date, tp)
- cache.set(cache_key, count, 3600*24*7)
-
- @lazyproperty
- def user_disabled_total(self):
- return current_org.get_org_members().filter(is_active=False).count()
-
- @lazyproperty
- def asset_disabled_total(self):
- return Asset.objects.filter(is_active=False).count()
-
- @staticmethod
- def get_date_start_2_end(d):
- time_min = timezone.datetime.min.time()
- time_max = timezone.datetime.max.time()
- tz = timezone.get_current_timezone()
- ds = timezone.datetime.combine(d, time_min).replace(tzinfo=tz)
- de = timezone.datetime.combine(d, time_max).replace(tzinfo=tz)
- return ds, de
-
- def get_date_login_count(self, date):
- tp = "LOGIN"
- count = self.__get_data_from_cache(date, tp)
- if count is not None:
- return count
- ds, de = self.get_date_start_2_end(date)
- count = Session.objects.filter(date_start__range=(ds, de)).count()
- self.__set_data_to_cache(date, tp, count)
- return count
-
- def get_month_login_metrics(self):
- data = []
- for d in self.session_month_dates:
- count = self.get_date_login_count(d)
- data.append(count)
- if len(data) == 0:
- data = [0]
- return data
-
- def get_date_user_count(self, date):
- tp = "USER"
- count = self.__get_data_from_cache(date, tp)
- if count is not None:
- return count
- ds, de = self.get_date_start_2_end(date)
- count = Session.objects.filter(date_start__range=(ds, de))\
- .values('user').distinct().count()
- self.__set_data_to_cache(date, tp, count)
- return count
-
- def get_month_active_user_metrics(self):
- data = []
- for d in self.session_month_dates:
- count = self.get_date_user_count(d)
- data.append(count)
- return data
-
- def get_date_asset_count(self, date):
- tp = "ASSET"
- count = self.__get_data_from_cache(date, tp)
- if count is not None:
- return count
- ds, de = self.get_date_start_2_end(date)
- count = Session.objects.filter(date_start__range=(ds, de)) \
- .values('asset').distinct().count()
- self.__set_data_to_cache(date, tp, count)
- return count
-
- def get_month_active_asset_metrics(self):
- data = []
- for d in self.session_month_dates:
- count = self.get_date_asset_count(d)
- data.append(count)
- return data
-
- @lazyproperty
- def month_active_user_total(self):
- count = self.session_month.values('user').distinct().count()
- return count
-
- @lazyproperty
- def month_inactive_user_total(self):
- total = current_org.get_org_members().count()
- active = self.month_active_user_total
- count = total - active
- if count < 0:
- count = 0
- return count
-
- @lazyproperty
- def month_active_asset_total(self):
- return self.session_month.values('asset').distinct().count()
-
- @lazyproperty
- def month_inactive_asset_total(self):
- total = Asset.objects.all().count()
- active = self.month_active_asset_total
- count = total - active
- if count < 0:
- count = 0
- return count
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context.update({
- 'month_str': self.get_month_day_metrics(),
- 'month_total_visit_count': self.get_month_login_metrics(),
- 'month_user': self.get_month_active_user_metrics(),
- 'mouth_asset': self.get_month_active_asset_metrics(),
- 'month_user_active': self.month_active_user_total,
- 'month_user_inactive': self.month_inactive_user_total,
- 'month_user_disabled': self.user_disabled_total,
- 'month_asset_active': self.month_active_asset_total,
- 'month_asset_inactive': self.month_inactive_asset_total,
- 'month_asset_disabled': self.asset_disabled_total,
- })
- return context
-
-
-class WeekSessionMetricMixin:
- session_week = None
-
- @lazyproperty
- def session_week(self):
- week_ago = timezone.now() - timezone.timedelta(weeks=1)
- session_week = Session.objects.filter(date_start__gt=week_ago)
- return session_week
-
- def get_top5_user_a_week(self):
- users = self.session_week.values('user') \
- .annotate(total=Count('user')) \
- .order_by('-total')[:5]
- return users
-
- def get_week_login_user_count(self):
- return self.session_week.values('user').distinct().count()
-
- def get_week_login_asset_count(self):
- return self.session_week.count()
-
- def get_week_top10_assets(self):
- assets = self.session_week.values("asset")\
- .annotate(total=Count("asset"))\
- .annotate(last=Max("date_start")).order_by("-total")[:10]
- return assets
-
- def get_week_top10_users(self):
- users = self.session_week.values("user") \
- .annotate(total=Count("user")) \
- .annotate(last=Max("date_start")).order_by("-total")[:10]
- return users
-
- def get_last10_sessions(self):
- sessions = self.session_week.order_by('-date_start')[:10]
- for session in sessions:
- session.avatar_url = User.get_avatar_url("")
- return sessions
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context.update({
- 'user_visit_count_weekly': self.get_week_login_user_count(),
- 'asset_visit_count_weekly': self.get_week_login_asset_count(),
- 'user_visit_count_top_five': self.get_top5_user_a_week(),
- 'last_login_ten': self.get_last10_sessions(),
- 'week_asset_hot_ten': self.get_week_top10_assets(),
- 'week_user_hot_ten': self.get_week_top10_users(),
- })
- return context
-
-
-class IndexView(PermissionsMixin, MonthLoginMetricMixin, WeekSessionMetricMixin, TemplateView):
+class IndexView(PermissionsMixin, TemplateView):
template_name = 'index.html'
permission_classes = [IsValidUser]
@@ -229,31 +17,9 @@ class IndexView(PermissionsMixin, MonthLoginMetricMixin, WeekSessionMetricMixin,
return redirect('assets:user-asset-list')
return super(IndexView, self).dispatch(request, *args, **kwargs)
- @staticmethod
- def get_user_count():
- return current_org.get_org_members().count()
-
- @staticmethod
- def get_asset_count():
- return Asset.objects.all().count()
-
- @staticmethod
- def get_online_user_count():
- count = Session.objects.filter(is_finished=False)\
- .values_list('user', flat=True).distinct().count()
- return count
-
- @staticmethod
- def get_online_session_count():
- return Session.objects.filter(is_finished=False).count()
-
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
- 'assets_count': self.get_asset_count(),
- 'users_count': self.get_user_count(),
- 'online_user_count': self.get_online_user_count(),
- 'online_asset_count': self.get_online_session_count(),
'app': _("Dashboard"),
})
return context
diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo
index d505bc23d..34a68aacd 100644
Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ
diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po
index 1ff08d362..0b752ed50 100644
--- a/apps/locale/zh/LC_MESSAGES/django.po
+++ b/apps/locale/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: 2020-05-09 14:47+0800\n"
+"POT-Creation-Date: 2020-05-11 16:53+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler \n"
"Language-Team: JumpServer team\n"
@@ -26,7 +26,7 @@ msgstr "自定义"
#: applications/templates/applications/remote_app_list.html:27
#: applications/templates/applications/user_remote_app_list.html:18
#: assets/forms/domain.py:15 assets/forms/label.py:13
-#: assets/models/asset.py:352 assets/models/authbook.py:27
+#: assets/models/asset.py:353 assets/models/authbook.py:27
#: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:32
#: assets/serializers/asset_user.py:47 assets/serializers/asset_user.py:84
#: assets/serializers/system_user.py:44 assets/serializers/system_user.py:176
@@ -42,7 +42,7 @@ msgstr "自定义"
#: perms/templates/perms/asset_permission_asset.html:53
#: perms/templates/perms/asset_permission_create_update.html:57
#: perms/templates/perms/asset_permission_list.html:35
-#: perms/templates/perms/asset_permission_list.html:87
+#: perms/templates/perms/asset_permission_list.html:87 templates/index.html:82
#: terminal/backends/command/models.py:19 terminal/models.py:187
#: terminal/templates/terminal/command_list.html:31
#: terminal/templates/terminal/command_list.html:106
@@ -52,6 +52,17 @@ msgstr "自定义"
#: users/templates/users/user_asset_permission.html:40
#: users/templates/users/user_asset_permission.html:70
#: users/templates/users/user_granted_remote_app.html:36
+#: xpack/plugins/change_auth_plan/forms.py:74
+#: xpack/plugins/change_auth_plan/models.py:265
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:40
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14
+#: xpack/plugins/cloud/models.py:269
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:37
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:47
+#: xpack/plugins/orgs/templates/orgs/org_list.html:17
+#: xpack/plugins/vault/forms.py:13 xpack/plugins/vault/forms.py:15
msgid "Asset"
msgstr "资产"
@@ -101,7 +112,7 @@ msgstr "运行参数"
#: applications/templates/applications/user_database_app_list.html:16
#: applications/templates/applications/user_remote_app_list.html:16
#: assets/forms/asset.py:21 assets/forms/domain.py:77 assets/forms/user.py:74
-#: assets/forms/user.py:96 assets/models/asset.py:145 assets/models/base.py:232
+#: assets/forms/user.py:96 assets/models/asset.py:146 assets/models/base.py:232
#: assets/models/cluster.py:18 assets/models/cmd_filter.py:21
#: assets/models/domain.py:20 assets/models/group.py:20
#: assets/models/label.py:18 assets/templates/assets/_node_detail_modal.html:27
@@ -150,6 +161,18 @@ msgstr "运行参数"
#: users/templates/users/user_profile.html:51
#: users/templates/users/user_pubkey_update.html:57
#: users/templates/users/user_remote_app_permission.html:36
+#: xpack/plugins/change_auth_plan/forms.py:57
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:59
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
+#: xpack/plugins/cloud/models.py:35
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:47
+#: xpack/plugins/cloud/templates/cloud/account_list.html:12
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:53
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:12
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:16
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:51
+#: xpack/plugins/orgs/templates/orgs/org_list.html:12
+#: xpack/plugins/orgs/templates/orgs/org_users.html:46
msgid "Name"
msgstr "名称"
@@ -173,7 +196,7 @@ msgstr "类型"
#: applications/templates/applications/database_app_detail.html:56
#: applications/templates/applications/database_app_list.html:25
#: applications/templates/applications/user_database_app_list.html:18
-#: ops/models/adhoc.py:146 templates/index.html:90
+#: ops/models/adhoc.py:146
#: users/templates/users/user_granted_database_app.html:36
msgid "Host"
msgstr "主机"
@@ -181,7 +204,7 @@ msgstr "主机"
#: applications/models/database_app.py:27
#: applications/templates/applications/database_app_detail.html:60
#: applications/templates/applications/database_app_list.html:26
-#: assets/forms/asset.py:25 assets/models/asset.py:191
+#: assets/forms/asset.py:25 assets/models/asset.py:192
#: assets/models/domain.py:50
#: assets/templates/assets/domain_gateway_list.html:64
msgid "Port"
@@ -204,7 +227,7 @@ msgstr "数据库"
#: applications/templates/applications/remote_app_list.html:28
#: applications/templates/applications/user_database_app_list.html:20
#: applications/templates/applications/user_remote_app_list.html:19
-#: assets/models/asset.py:150 assets/models/asset.py:226
+#: assets/models/asset.py:151 assets/models/asset.py:227
#: assets/models/base.py:237 assets/models/cluster.py:29
#: assets/models/cmd_filter.py:23 assets/models/cmd_filter.py:56
#: assets/models/domain.py:21 assets/models/domain.py:53
@@ -236,6 +259,17 @@ msgstr "数据库"
#: users/templates/users/user_group_detail.html:62
#: users/templates/users/user_group_list.html:16
#: users/templates/users/user_profile.html:138
+#: xpack/plugins/change_auth_plan/models.py:75
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:115
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
+#: xpack/plugins/cloud/models.py:53 xpack/plugins/cloud/models.py:139
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:67
+#: xpack/plugins/cloud/templates/cloud/account_list.html:15
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:128
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:18
+#: xpack/plugins/gathered_user/models.py:26
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:63
+#: xpack/plugins/orgs/templates/orgs/org_list.html:23
msgid "Comment"
msgstr "备注"
@@ -275,7 +309,7 @@ msgstr "参数"
#: applications/models/remote_app.py:39
#: applications/templates/applications/database_app_detail.html:72
#: applications/templates/applications/remote_app_detail.html:68
-#: assets/models/asset.py:224 assets/models/base.py:240
+#: assets/models/asset.py:225 assets/models/base.py:240
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
#: assets/models/cmd_filter.py:59 assets/models/group.py:21
#: assets/templates/assets/admin_user_detail.html:63
@@ -289,6 +323,10 @@ msgstr "参数"
#: perms/templates/perms/remote_app_permission_detail.html:85
#: users/models/user.py:481 users/serializers/group.py:35
#: users/templates/users/user_detail.html:97
+#: xpack/plugins/change_auth_plan/models.py:79
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:111
+#: xpack/plugins/cloud/models.py:56 xpack/plugins/cloud/models.py:145
+#: xpack/plugins/gathered_user/models.py:30
msgid "Created by"
msgstr "创建者"
@@ -297,7 +335,7 @@ msgstr "创建者"
#: applications/models/remote_app.py:42
#: applications/templates/applications/database_app_detail.html:68
#: applications/templates/applications/remote_app_detail.html:64
-#: assets/models/asset.py:225 assets/models/base.py:238
+#: assets/models/asset.py:226 assets/models/base.py:238
#: assets/models/cluster.py:26 assets/models/domain.py:23
#: assets/models/gathered_user.py:19 assets/models/group.py:22
#: assets/models/label.py:25 assets/templates/assets/admin_user_detail.html:59
@@ -313,6 +351,11 @@ msgstr "创建者"
#: terminal/templates/terminal/terminal_detail.html:59
#: tickets/templates/tickets/ticket_detail.html:52 users/models/group.py:18
#: users/templates/users/user_group_detail.html:58
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:103
+#: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:148
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:63
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:108
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:59
msgid "Date created"
msgstr "创建日期"
@@ -362,6 +405,13 @@ msgstr "远程应用"
#: users/templates/users/user_profile_update.html:67
#: users/templates/users/user_pubkey_update.html:74
#: users/templates/users/user_pubkey_update.html:80
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:65
+#: xpack/plugins/cloud/templates/cloud/account_create_update.html:29
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:52
+#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:40
+#: xpack/plugins/interface/templates/interface/interface.html:72
+#: xpack/plugins/orgs/templates/orgs/org_create_update.html:29
+#: xpack/plugins/vault/templates/vault/vault_create.html:41
msgid "Reset"
msgstr "重置"
@@ -399,6 +449,10 @@ msgstr "重置"
#: users/templates/users/user_password_update.html:76
#: users/templates/users/user_profile_update.html:68
#: users/templates/users/user_pubkey_update.html:81
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:66
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:67
+#: xpack/plugins/interface/templates/interface/interface.html:74
+#: xpack/plugins/vault/templates/vault/vault_create.html:42
msgid "Submit"
msgstr "提交"
@@ -426,6 +480,11 @@ msgstr "提交"
#: perms/templates/perms/remote_app_permission_detail.html:13
#: perms/templates/perms/remote_app_permission_remote_app.html:13
#: perms/templates/perms/remote_app_permission_user.html:13
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:13
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:18
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:17
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:106
+#: xpack/plugins/change_auth_plan/views.py:91
msgid "Detail"
msgstr "详情"
@@ -472,6 +531,15 @@ msgstr "详情"
#: users/templates/users/user_profile.html:191
#: users/templates/users/user_profile.html:201
#: users/templates/users/user_remote_app_permission.html:110
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:27
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:56
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:20
+#: xpack/plugins/cloud/templates/cloud/account_list.html:40
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:26
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:60
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:46
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:24
+#: xpack/plugins/orgs/templates/orgs/org_list.html:93
msgid "Update"
msgstr "更新"
@@ -515,6 +583,15 @@ msgstr "更新"
#: users/templates/users/user_list.html:94
#: users/templates/users/user_list.html:98
#: users/templates/users/user_remote_app_permission.html:111
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:31
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:58
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:24
+#: xpack/plugins/cloud/templates/cloud/account_list.html:42
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:30
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:61
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:47
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:28
+#: xpack/plugins/orgs/templates/orgs/org_list.html:95
msgid "Delete"
msgstr "删除"
@@ -565,6 +642,15 @@ msgstr "创建数据库应用"
#: users/templates/users/user_group_list.html:17
#: users/templates/users/user_list.html:20
#: users/templates/users/user_remote_app_permission.html:42
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:60
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:18
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:20
+#: xpack/plugins/cloud/templates/cloud/account_list.html:16
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:52
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:19
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:20
+#: xpack/plugins/orgs/templates/orgs/org_list.html:24
+#: xpack/plugins/orgs/templates/orgs/org_users.html:47
msgid "Action"
msgstr "动作"
@@ -651,18 +737,24 @@ msgstr "不能移除资产的管理用户账号"
msgid "Latest version could not be delete"
msgstr "最新版本的不能被删除"
-#: assets/forms/asset.py:83 assets/models/asset.py:195
+#: assets/forms/asset.py:83 assets/models/asset.py:196
#: assets/models/user.py:109 assets/templates/assets/asset_detail.html:186
#: assets/templates/assets/asset_detail.html:194
#: assets/templates/assets/system_user_assets.html:118
#: perms/models/asset_permission.py:81
+#: xpack/plugins/change_auth_plan/models.py:54
+#: xpack/plugins/gathered_user/models.py:24
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:17
msgid "Nodes"
msgstr "节点"
-#: assets/forms/asset.py:86 assets/models/asset.py:199
+#: assets/forms/asset.py:86 assets/models/asset.py:200
#: assets/models/cluster.py:19 assets/models/user.py:65
#: assets/templates/assets/admin_user_list.html:62
#: assets/templates/assets/asset_detail.html:72 templates/_nav.html:44
+#: xpack/plugins/cloud/models.py:133
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:65
+#: xpack/plugins/orgs/templates/orgs/org_list.html:19
msgid "Admin user"
msgstr "管理用户"
@@ -670,18 +762,20 @@ msgstr "管理用户"
#: assets/templates/assets/asset_create.html:48
#: assets/templates/assets/asset_create.html:50
#: assets/templates/assets/asset_list.html:13
+#: xpack/plugins/orgs/templates/orgs/org_list.html:21
msgid "Label"
msgstr "标签"
-#: assets/forms/asset.py:92 assets/models/asset.py:194
+#: assets/forms/asset.py:92 assets/models/asset.py:195
#: assets/models/domain.py:26 assets/models/domain.py:52
#: assets/templates/assets/asset_detail.html:76
#: assets/templates/assets/user_asset_list.html:80
+#: xpack/plugins/orgs/templates/orgs/org_list.html:18
msgid "Domain"
msgstr "网域"
-#: assets/forms/asset.py:95 assets/models/asset.py:169
-#: assets/models/asset.py:193 assets/serializers/asset.py:67
+#: assets/forms/asset.py:95 assets/models/asset.py:170
+#: assets/models/asset.py:194 assets/serializers/asset.py:67
#: assets/templates/assets/asset_detail.html:100
#: assets/templates/assets/user_asset_list.html:78
msgid "Platform"
@@ -697,6 +791,11 @@ msgstr "系统平台"
#: users/templates/users/user_asset_permission.html:41
#: users/templates/users/user_asset_permission.html:73
#: users/templates/users/user_asset_permission.html:158
+#: xpack/plugins/change_auth_plan/forms.py:75
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:55
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:15
+#: xpack/plugins/cloud/models.py:129
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:61
msgid "Node"
msgstr "节点"
@@ -721,6 +820,8 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,
#: assets/forms/domain.py:17 assets/forms/label.py:15
#: assets/templates/assets/system_user_assets.html:102
#: perms/templates/perms/asset_permission_asset.html:74
+#: xpack/plugins/change_auth_plan/forms.py:65
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:70
msgid "Select assets"
msgstr "选择资产"
@@ -758,16 +859,30 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: users/templates/users/user_detail.html:53
#: users/templates/users/user_list.html:15
#: users/templates/users/user_profile.html:47
+#: xpack/plugins/change_auth_plan/forms.py:59
+#: xpack/plugins/change_auth_plan/models.py:45
+#: xpack/plugins/change_auth_plan/models.py:261
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:63
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:13
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:25
msgid "Username"
msgstr "用户名"
#: assets/forms/platform.py:20 ops/templates/ops/task_detail.html:85
#: ops/templates/ops/task_detail.html:95
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:82
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:72
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:82
msgid "Yes"
msgstr "是"
#: assets/forms/platform.py:21 ops/templates/ops/task_detail.html:87
#: ops/templates/ops/task_detail.html:97
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:84
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:74
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:84
msgid "No"
msgstr "否"
@@ -804,6 +919,9 @@ msgstr "密码或密钥密码"
#: users/templates/users/user_profile_update.html:41
#: users/templates/users/user_pubkey_update.html:41
#: users/templates/users/user_update.html:20
+#: xpack/plugins/change_auth_plan/models.py:66
+#: xpack/plugins/change_auth_plan/models.py:181
+#: xpack/plugins/change_auth_plan/models.py:268
msgid "Password"
msgstr "密码"
@@ -862,24 +980,24 @@ msgstr "SFTP的起始路径,tmp目录, 用户home目录或者自定义"
msgid "Username is dynamic, When connect asset, using current user's username"
msgstr "用户名是动态的,登录资产时使用当前用户的用户名登录"
-#: assets/models/asset.py:146
+#: assets/models/asset.py:147 xpack/plugins/cloud/providers/base.py:16
msgid "Base"
msgstr "基础"
-#: assets/models/asset.py:147 assets/templates/assets/platform_detail.html:56
+#: assets/models/asset.py:148 assets/templates/assets/platform_detail.html:56
msgid "Charset"
msgstr "编码"
-#: assets/models/asset.py:148 assets/templates/assets/platform_detail.html:60
+#: assets/models/asset.py:149 assets/templates/assets/platform_detail.html:60
#: tickets/models/ticket.py:38
msgid "Meta"
msgstr "元数据"
-#: assets/models/asset.py:149
+#: assets/models/asset.py:150
msgid "Internal"
msgstr "内部的"
-#: assets/models/asset.py:186 assets/models/domain.py:49
+#: assets/models/asset.py:187 assets/models/domain.py:49
#: assets/serializers/asset_user.py:46
#: assets/templates/assets/_asset_list_modal.html:47
#: assets/templates/assets/_asset_user_list.html:20
@@ -892,10 +1010,12 @@ msgstr "内部的"
#: settings/forms/terminal.py:16 settings/serializers/settings.py:52
#: users/templates/users/_granted_assets.html:26
#: users/templates/users/user_asset_permission.html:156
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:50
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:24
msgid "IP"
msgstr "IP"
-#: assets/models/asset.py:187 assets/serializers/asset_user.py:45
+#: assets/models/asset.py:188 assets/serializers/asset_user.py:45
#: assets/serializers/gathered_user.py:20
#: assets/templates/assets/_asset_list_modal.html:46
#: assets/templates/assets/_asset_user_auth_update_modal.html:9
@@ -908,10 +1028,12 @@ msgstr "IP"
#: settings/forms/terminal.py:15 settings/serializers/settings.py:51
#: users/templates/users/_granted_assets.html:25
#: users/templates/users/user_asset_permission.html:157
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:49
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:23
msgid "Hostname"
msgstr "主机名"
-#: assets/models/asset.py:190 assets/models/domain.py:51
+#: assets/models/asset.py:191 assets/models/domain.py:51
#: assets/models/user.py:114 assets/templates/assets/asset_detail.html:68
#: assets/templates/assets/domain_gateway_list.html:65
#: assets/templates/assets/system_user_detail.html:78
@@ -923,84 +1045,84 @@ msgstr "主机名"
msgid "Protocol"
msgstr "协议"
-#: assets/models/asset.py:192 assets/serializers/asset.py:69
+#: assets/models/asset.py:193 assets/serializers/asset.py:69
#: assets/templates/assets/asset_create.html:24
#: assets/templates/assets/user_asset_list.html:77
#: perms/serializers/user_permission.py:60
msgid "Protocols"
msgstr "协议组"
-#: assets/models/asset.py:196 assets/models/cmd_filter.py:22
+#: assets/models/asset.py:197 assets/models/cmd_filter.py:22
#: assets/models/domain.py:54 assets/models/label.py:22
#: assets/templates/assets/asset_detail.html:108 authentication/models.py:45
msgid "Is active"
msgstr "激活"
-#: assets/models/asset.py:202 assets/templates/assets/asset_detail.html:64
+#: assets/models/asset.py:203 assets/templates/assets/asset_detail.html:64
msgid "Public IP"
msgstr "公网IP"
-#: assets/models/asset.py:203 assets/templates/assets/asset_detail.html:116
+#: assets/models/asset.py:204 assets/templates/assets/asset_detail.html:116
msgid "Asset number"
msgstr "资产编号"
-#: assets/models/asset.py:206 assets/templates/assets/asset_detail.html:80
+#: assets/models/asset.py:207 assets/templates/assets/asset_detail.html:80
msgid "Vendor"
msgstr "制造商"
-#: assets/models/asset.py:207 assets/templates/assets/asset_detail.html:84
+#: assets/models/asset.py:208 assets/templates/assets/asset_detail.html:84
msgid "Model"
msgstr "型号"
-#: assets/models/asset.py:208 assets/templates/assets/asset_detail.html:112
+#: assets/models/asset.py:209 assets/templates/assets/asset_detail.html:112
msgid "Serial number"
msgstr "序列号"
-#: assets/models/asset.py:210
+#: assets/models/asset.py:211
msgid "CPU model"
msgstr "CPU型号"
-#: assets/models/asset.py:211
+#: assets/models/asset.py:212
msgid "CPU count"
msgstr "CPU数量"
-#: assets/models/asset.py:212
+#: assets/models/asset.py:213
msgid "CPU cores"
msgstr "CPU核数"
-#: assets/models/asset.py:213
+#: assets/models/asset.py:214
msgid "CPU vcpus"
msgstr "CPU总数"
-#: assets/models/asset.py:214 assets/templates/assets/asset_detail.html:92
+#: assets/models/asset.py:215 assets/templates/assets/asset_detail.html:92
msgid "Memory"
msgstr "内存"
-#: assets/models/asset.py:215
+#: assets/models/asset.py:216
msgid "Disk total"
msgstr "硬盘大小"
-#: assets/models/asset.py:216
+#: assets/models/asset.py:217
msgid "Disk info"
msgstr "硬盘信息"
-#: assets/models/asset.py:218 assets/templates/assets/asset_detail.html:104
+#: assets/models/asset.py:219 assets/templates/assets/asset_detail.html:104
msgid "OS"
msgstr "操作系统"
-#: assets/models/asset.py:219
+#: assets/models/asset.py:220
msgid "OS version"
msgstr "系统版本"
-#: assets/models/asset.py:220
+#: assets/models/asset.py:221
msgid "OS arch"
msgstr "系统架构"
-#: assets/models/asset.py:221
+#: assets/models/asset.py:222
msgid "Hostname raw"
msgstr "主机名原始"
-#: assets/models/asset.py:223 assets/templates/assets/asset_create.html:46
+#: assets/models/asset.py:224 assets/templates/assets/asset_create.html:46
#: assets/templates/assets/asset_detail.html:220 templates/_nav.html:46
msgid "Labels"
msgstr "标签管理"
@@ -1025,17 +1147,23 @@ msgstr "版本"
msgid "AuthBook"
msgstr ""
-#: assets/models/base.py:235
+#: assets/models/base.py:235 xpack/plugins/change_auth_plan/models.py:70
+#: xpack/plugins/change_auth_plan/models.py:188
+#: xpack/plugins/change_auth_plan/models.py:275
msgid "SSH private key"
msgstr "ssh密钥"
-#: assets/models/base.py:236
+#: assets/models/base.py:236 xpack/plugins/change_auth_plan/models.py:73
+#: xpack/plugins/change_auth_plan/models.py:184
+#: xpack/plugins/change_auth_plan/models.py:271
msgid "SSH public key"
msgstr "ssh公钥"
#: assets/models/base.py:239 assets/models/gathered_user.py:20
#: assets/templates/assets/cmd_filter_detail.html:68 common/mixins/models.py:51
#: ops/models/adhoc.py:39
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:107
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:29
msgid "Date updated"
msgstr "更新日期"
@@ -1133,6 +1261,7 @@ msgstr "优先级可选范围为1-100,1最低优先级,100最高优先级"
#: assets/models/cmd_filter.py:54
#: assets/templates/assets/cmd_filter_rule_list.html:54
+#: xpack/plugins/license/models.py:29
msgid "Content"
msgstr "内容"
@@ -1152,14 +1281,17 @@ msgid "Gateway"
msgstr "网关"
#: assets/models/gathered_user.py:16
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:28
msgid "Present"
msgstr "存在"
#: assets/models/gathered_user.py:17
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:26
msgid "Date last login"
msgstr "最后登录日期"
#: assets/models/gathered_user.py:18
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:27
msgid "IP last login"
msgstr "最后登录IP"
@@ -1193,7 +1325,7 @@ msgstr "默认资产组"
#: perms/templates/perms/database_app_permission_list.html:15
#: perms/templates/perms/remote_app_permission_create_update.html:41
#: perms/templates/perms/remote_app_permission_list.html:15
-#: templates/index.html:86 terminal/backends/command/models.py:18
+#: templates/index.html:78 terminal/backends/command/models.py:18
#: terminal/models.py:185 terminal/templates/terminal/command_list.html:30
#: terminal/templates/terminal/command_list.html:105
#: terminal/templates/terminal/session_detail.html:48
@@ -1212,7 +1344,8 @@ msgstr "默认资产组"
#: users/templates/users/user_group_list.html:15
#: users/templates/users/user_remote_app_permission.html:37
#: users/templates/users/user_remote_app_permission.html:58
-#: users/views/profile/base.py:46
+#: users/views/profile/base.py:46 xpack/plugins/orgs/forms.py:27
+#: xpack/plugins/orgs/templates/orgs/org_list.html:15
msgid "User"
msgstr "用户"
@@ -1273,7 +1406,7 @@ msgstr "手动登录"
#: assets/views/platform.py:58 assets/views/platform.py:74
#: assets/views/system_user.py:30 assets/views/system_user.py:47
#: assets/views/system_user.py:64 assets/views/system_user.py:80
-#: templates/_nav.html:39
+#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:50
msgid "Assets"
msgstr "资产管理"
@@ -1347,6 +1480,7 @@ msgstr "SFTP根路径"
#: users/templates/users/user_database_app_permission.html:67
#: users/templates/users/user_remote_app_permission.html:40
#: users/templates/users/user_remote_app_permission.html:67
+#: xpack/plugins/orgs/templates/orgs/org_list.html:20
msgid "System user"
msgstr "系统用户"
@@ -1396,6 +1530,8 @@ msgstr "连接"
#: ops/templates/ops/adhoc_history_detail.html:47
#: ops/templates/ops/task_detail.html:54
#: terminal/templates/terminal/session_list.html:24
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:45
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:44
msgid "ID"
msgstr "ID"
@@ -1588,7 +1724,7 @@ msgstr "启用多因子认证"
#: assets/templates/assets/system_user_assets.html:26
#: assets/templates/assets/system_user_detail.html:18
#: assets/templates/assets/system_user_users.html:25 assets/views/asset.py:38
-#: templates/_nav.html:42
+#: templates/_nav.html:42 xpack/plugins/change_auth_plan/views.py:118
msgid "Asset list"
msgstr "资产列表"
@@ -1598,6 +1734,7 @@ msgstr "资产列表"
#: ops/templates/ops/command_execution_create.html:112
#: settings/templates/settings/_ldap_list_users_modal.html:41
#: users/templates/users/_granted_assets.html:7
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:65
msgid "Loading"
msgstr "加载中"
@@ -1607,6 +1744,7 @@ msgstr "更新资产用户认证信息"
#: assets/templates/assets/_asset_user_auth_update_modal.html:23
#: settings/templates/settings/_ldap_test_user_login_modal.html:18
+#: xpack/plugins/change_auth_plan/forms.py:61
msgid "Please input password"
msgstr "请输入密码"
@@ -1614,6 +1752,7 @@ msgstr "请输入密码"
#: assets/templates/assets/asset_detail.html:300
#: users/templates/users/user_detail.html:356
#: users/templates/users/user_detail.html:383
+#: xpack/plugins/interface/views.py:35
msgid "Update successfully!"
msgstr "更新成功"
@@ -1730,6 +1869,9 @@ msgstr "重命名成功"
#: perms/templates/perms/asset_permission_create_update.html:48
#: perms/templates/perms/database_app_permission_create_update.html:37
#: perms/templates/perms/remote_app_permission_create_update.html:37
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:37
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:23
+#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:23
msgid "Basic"
msgstr "基本"
@@ -1751,6 +1893,9 @@ msgstr "自动生成密钥"
#: perms/templates/perms/database_app_permission_create_update.html:51
#: perms/templates/perms/remote_app_permission_create_update.html:51
#: terminal/templates/terminal/terminal_update.html:38
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:61
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:47
+#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:35
msgid "Other"
msgstr "其它"
@@ -1767,6 +1912,7 @@ msgstr "资产列表"
#: assets/templates/assets/admin_user_assets.html:24
#: perms/templates/perms/asset_permission_asset.html:31
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:27
msgid "Asset list of "
msgstr "资产列表"
@@ -1792,6 +1938,9 @@ msgstr "替换资产的管理员"
#: assets/templates/assets/admin_user_detail.html:86
#: assets/templates/assets/system_user_assets.html:126
#: perms/templates/perms/asset_permission_asset.html:99
+#: xpack/plugins/change_auth_plan/forms.py:69
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:95
+#: xpack/plugins/gathered_user/forms.py:33
msgid "Select nodes"
msgstr "选择节点"
@@ -1813,6 +1962,11 @@ msgstr "选择节点"
#: users/templates/users/user_group_create_update.html:28
#: users/templates/users/user_list.html:184
#: users/templates/users/user_password_verify.html:20
+#: xpack/plugins/cloud/templates/cloud/account_create_update.html:30
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:53
+#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:41
+#: xpack/plugins/interface/templates/interface/interface.html:103
+#: xpack/plugins/orgs/templates/orgs/org_create_update.html:30
msgid "Confirm"
msgstr "确认"
@@ -1848,6 +2002,9 @@ msgstr "资产用户"
#: terminal/templates/terminal/session_detail.html:87
#: users/templates/users/user_detail.html:126
#: users/templates/users/user_profile.html:150
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:126
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:139
+#: xpack/plugins/license/templates/license/license_detail.html:80
msgid "Quick modify"
msgstr "快速修改"
@@ -1970,6 +2127,7 @@ msgstr "显示所有子节点资产"
#: users/templates/users/user_detail.html:437
#: users/templates/users/user_detail.html:505
#: users/templates/users/user_list.html:178
+#: xpack/plugins/interface/templates/interface/interface.html:97
msgid "Are you sure?"
msgstr "你确认吗?"
@@ -1982,6 +2140,7 @@ msgstr "删除选择资产"
#: users/templates/users/user_detail.html:441
#: users/templates/users/user_detail.html:509
#: users/templates/users/user_list.html:182
+#: xpack/plugins/interface/templates/interface/interface.html:101
msgid "Cancel"
msgstr "取消"
@@ -2182,6 +2341,7 @@ msgstr "创建系统用户"
#: assets/templates/assets/system_user_users.html:84 users/forms/group.py:19
#: users/forms/user.py:143 users/forms/user.py:148
+#: xpack/plugins/orgs/forms.py:17
msgid "Select users"
msgstr "选择用户"
@@ -2340,6 +2500,7 @@ msgstr "文件名"
#: ops/templates/ops/command_execution_list.html:71
#: ops/templates/ops/task_list.html:14
#: users/templates/users/user_detail.html:487
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:14
msgid "Success"
msgstr "成功"
@@ -2353,11 +2514,17 @@ msgstr "成功"
#: perms/templates/perms/remote_app_permission_detail.html:73
#: terminal/models.py:199 terminal/templates/terminal/session_detail.html:72
#: terminal/templates/terminal/session_list.html:32
+#: xpack/plugins/change_auth_plan/models.py:167
+#: xpack/plugins/change_auth_plan/models.py:290
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17
+#: xpack/plugins/gathered_user/models.py:76
msgid "Date start"
msgstr "开始日期"
#: audits/models.py:33
#: authentication/templates/authentication/_access_key_modal.html:22
+#: xpack/plugins/vault/templates/vault/vault.html:7
msgid "Create"
msgstr "创建"
@@ -2387,7 +2554,7 @@ msgstr "启用"
msgid "-"
msgstr ""
-#: audits/models.py:78
+#: audits/models.py:78 xpack/plugins/cloud/models.py:204
msgid "Failed"
msgstr "失败"
@@ -2418,6 +2585,9 @@ msgid "MFA"
msgstr "多因子认证"
#: audits/models.py:87 audits/templates/audits/login_log_list.html:63
+#: xpack/plugins/change_auth_plan/models.py:286
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15
+#: xpack/plugins/cloud/models.py:217
msgid "Reason"
msgstr "原因"
@@ -2425,6 +2595,9 @@ msgstr "原因"
#: tickets/templates/tickets/ticket_detail.html:34
#: tickets/templates/tickets/ticket_list.html:36
#: tickets/templates/tickets/ticket_list.html:104
+#: xpack/plugins/cloud/models.py:214 xpack/plugins/cloud/models.py:272
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:50
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:48
msgid "Status"
msgstr "状态"
@@ -2433,6 +2606,8 @@ msgid "Date login"
msgstr "登录日期"
#: audits/serializers.py:65 ops/models/command.py:24
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:56
+#: xpack/plugins/cloud/models.py:212
msgid "Result"
msgstr "结果"
@@ -2460,6 +2635,7 @@ msgstr "运行用户"
#: perms/templates/perms/asset_permission_user.html:74
#: perms/templates/perms/database_app_permission_user.html:74
#: perms/templates/perms/remote_app_permission_user.html:83
+#: xpack/plugins/orgs/templates/orgs/org_users.html:67
msgid "Select user"
msgstr "选择用户"
@@ -2621,7 +2797,7 @@ msgid ""
msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)"
#: authentication/errors.py:48 users/views/profile/otp.py:63
-#: users/views/profile/otp.py:102
+#: users/views/profile/otp.py:100 users/views/profile/otp.py:116
msgid "MFA code invalid, or ntp sync server time"
msgstr "MFA验证码不正确,或者服务器端时间不对"
@@ -2684,10 +2860,10 @@ msgid "Show"
msgstr "显示"
#: authentication/templates/authentication/_access_key_modal.html:66
-#: users/models/user.py:360 users/templates/users/user_disable_mfa.html:32
-#: users/templates/users/user_profile.html:94
+#: users/models/user.py:360 users/templates/users/user_profile.html:94
#: users/templates/users/user_profile.html:163
#: users/templates/users/user_profile.html:166
+#: users/templates/users/user_verify_mfa.html:32
msgid "Disable"
msgstr "禁用"
@@ -2746,10 +2922,10 @@ msgstr "请打开 Google Authenticator,输入6位动态码"
#: authentication/templates/authentication/login_otp.html:26
#: users/templates/users/first_login.html:100
-#: users/templates/users/user_disable_mfa.html:26
#: users/templates/users/user_otp_check_password.html:15
#: users/templates/users/user_otp_enable_bind.html:24
#: users/templates/users/user_otp_enable_install_app.html:29
+#: users/templates/users/user_verify_mfa.html:26
msgid "Next"
msgstr "下一步"
@@ -2864,7 +3040,7 @@ msgstr "字段必须唯一"
msgid "Flow service unavailable, check it "
msgstr ""
-#: jumpserver/views/index.py:257 templates/_nav.html:7
+#: jumpserver/views/index.py:23 templates/_nav.html:7
msgid "Dashboard"
msgstr "仪表盘"
@@ -2900,14 +3076,25 @@ msgid "Not has host {} permission"
msgstr "没有该主机 {} 权限"
#: ops/mixin.py:29 ops/mixin.py:92 ops/mixin.py:162
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:98
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:98
msgid "Cycle perform"
msgstr "周期执行"
#: ops/mixin.py:33 ops/mixin.py:90 ops/mixin.py:111 ops/mixin.py:150
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:90
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:90
msgid "Regularly perform"
msgstr "定期执行"
#: ops/mixin.py:108 ops/mixin.py:147
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:54
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:79
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:17
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:40
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:79
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:16
+#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:28
msgid "Periodic perform"
msgstr "定时执行"
@@ -2965,6 +3152,8 @@ msgid "Become"
msgstr "Become"
#: ops/models/adhoc.py:150 users/templates/users/user_group_detail.html:54
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:59
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:55
msgid "Create by"
msgstr "创建者"
@@ -2986,6 +3175,11 @@ msgstr "完成时间"
#: ops/models/adhoc.py:238 ops/templates/ops/adhoc_history.html:55
#: ops/templates/ops/task_history.html:61 ops/templates/ops/task_list.html:16
+#: xpack/plugins/change_auth_plan/models.py:170
+#: xpack/plugins/change_auth_plan/models.py:293
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16
+#: xpack/plugins/gathered_user/models.py:79
msgid "Time"
msgstr "时间"
@@ -3005,11 +3199,11 @@ msgstr "结果"
msgid "Adhoc result summary"
msgstr "汇总"
-#: ops/models/adhoc.py:282
+#: ops/models/adhoc.py:282 xpack/plugins/change_auth_plan/utils.py:137
msgid "{} Start task: {}"
msgstr "{} 任务开始: {}"
-#: ops/models/adhoc.py:291
+#: ops/models/adhoc.py:291 xpack/plugins/change_auth_plan/utils.py:149
msgid "{} Task finish"
msgstr "{} 任务结束"
@@ -3048,6 +3242,8 @@ msgid "Version run execution"
msgstr "执行历史"
#: ops/templates/ops/adhoc_detail.html:92 ops/templates/ops/task_list.html:12
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:18
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:19
msgid "Run times"
msgstr "执行次数"
@@ -3175,6 +3371,7 @@ msgid "Pending"
msgstr "等待"
#: ops/templates/ops/command_execution_list.html:70
+#: xpack/plugins/change_auth_plan/models.py:257
msgid "Finished"
msgstr "结束"
@@ -3212,6 +3409,11 @@ msgid "Contents"
msgstr "内容"
#: ops/templates/ops/task_list.html:73
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:135
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:54
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:148
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:58
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:44
msgid "Run"
msgstr "执行"
@@ -3234,6 +3436,7 @@ msgid "Ops"
msgstr "作业中心"
#: ops/views/adhoc.py:32 templates/_nav.html:124
+#: xpack/plugins/gathered_user/views.py:35
msgid "Task list"
msgstr "任务列表"
@@ -3284,6 +3487,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件"
#: users/templates/users/user_list.html:17
#: users/templates/users/user_remote_app_permission.html:38
#: users/templates/users/user_remote_app_permission.html:61
+#: xpack/plugins/orgs/templates/orgs/org_list.html:16
msgid "User group"
msgstr "用户组"
@@ -3359,6 +3563,9 @@ msgstr "用户或用户组"
#: perms/templates/perms/asset_permission_asset.html:23
#: perms/templates/perms/asset_permission_detail.html:22
#: perms/templates/perms/asset_permission_user.html:23
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:16
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:21
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:20
msgid "Assets and node"
msgstr "资产或节点"
@@ -3379,6 +3586,10 @@ msgstr "添加资产"
#: perms/templates/perms/remote_app_permission_user.html:92
#: perms/templates/perms/remote_app_permission_user.html:120
#: users/templates/users/user_group_detail.html:87
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:76
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:89
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:123
+#: xpack/plugins/orgs/templates/orgs/org_users.html:73
msgid "Add"
msgstr "添加"
@@ -3388,6 +3599,7 @@ msgstr "添加节点"
#: perms/templates/perms/asset_permission_asset.html:105
#: users/templates/users/user_detail.html:226
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:101
msgid "Join"
msgstr "加入"
@@ -3416,6 +3628,7 @@ msgid "User group count"
msgstr "用户组数量"
#: perms/templates/perms/asset_permission_detail.html:69
+#: xpack/plugins/license/templates/license/license_detail.html:63
msgid "Asset count"
msgstr "资产数量"
@@ -3447,6 +3660,9 @@ msgstr "刷新授权缓存"
#: users/templates/users/user_database_app_permission.html:41
#: users/templates/users/user_list.html:19
#: users/templates/users/user_remote_app_permission.html:41
+#: xpack/plugins/cloud/models.py:50
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:55
+#: xpack/plugins/cloud/templates/cloud/account_list.html:14
msgid "Validity"
msgstr "有效"
@@ -3472,6 +3688,7 @@ msgstr "刷新成功"
#: perms/templates/perms/asset_permission_user.html:31
#: perms/templates/perms/database_app_permission_user.html:31
#: perms/templates/perms/remote_app_permission_user.html:30
+#: xpack/plugins/orgs/templates/orgs/org_users.html:24
msgid "User list of "
msgstr "用户列表"
@@ -3552,6 +3769,7 @@ msgstr "添加用户组"
#: perms/views/remote_app_permission.py:84
#: perms/views/remote_app_permission.py:116
#: perms/views/remote_app_permission.py:149 templates/_nav.html:75
+#: xpack/plugins/orgs/templates/orgs/org_list.html:22
msgid "Perms"
msgstr "权限管理"
@@ -3975,6 +4193,7 @@ msgstr "当前无勾选用户,请勾选你想要导入的用户"
#: settings/templates/settings/_ldap_list_users_modal.html:172
#: templates/_csv_import_export.html:13 templates/_csv_import_modal.html:5
+#: xpack/plugins/license/templates/license/license_detail.html:88
msgid "Import"
msgstr "导入"
@@ -4153,6 +4372,7 @@ msgid "Update setting successfully"
msgstr "更新设置成功"
#: templates/_base_only_msg_content.html:28
+#: xpack/plugins/interface/models.py:36
msgid "Welcome to the JumpServer open source fortress"
msgstr "欢迎使用JumpServer开源堡垒机"
@@ -4386,7 +4606,7 @@ msgstr "工单管理"
msgid "XPack"
msgstr ""
-#: templates/_nav.html:171
+#: templates/_nav.html:171 xpack/plugins/cloud/views.py:28
msgid "Account list"
msgstr "账户列表"
@@ -4420,8 +4640,8 @@ msgid "Total users"
msgstr "用户总数"
#: templates/index.html:23
-msgid "Total hosts"
-msgstr "主机总数"
+msgid "Total assets"
+msgstr "资产总数"
#: templates/index.html:36
msgid "Online users"
@@ -4443,120 +4663,124 @@ msgstr " 位用户登录 "
msgid " times asset."
msgstr " 次资产."
-#: templates/index.html:66
-msgid " times/week"
-msgstr " 次/周"
-
-#: templates/index.html:77
+#: templates/index.html:69
msgid "Active user asset ratio"
msgstr "活跃用户资产占比"
-#: templates/index.html:80
+#: templates/index.html:72
msgid ""
"The following graphs describe the percentage of active users per month and "
"assets per user host per month, respectively."
msgstr "以下图形分别描述一个月活跃用户和资产占所有用户主机的百分比"
-#: templates/index.html:105 templates/index.html:120
+#: templates/index.html:97 templates/index.html:112
msgid "Top 10 assets in a week"
msgstr "一周Top10资产"
-#: templates/index.html:121
+#: templates/index.html:113
msgid "Login frequency and last login record."
msgstr "登录次数及最近一次登录记录."
-#: templates/index.html:132 templates/index.html:218
-msgid " times"
-msgstr " 次"
-
-#: templates/index.html:135 templates/index.html:221
-msgid "The time last logged in"
-msgstr "最近一次登录日期"
-
-#: templates/index.html:136 templates/index.html:222
-msgid "At"
-msgstr "于"
-
-#: templates/index.html:142 templates/index.html:180 templates/index.html:228
-msgid "(No)"
-msgstr "(暂无)"
-
-#: templates/index.html:150
+#: templates/index.html:122
msgid "Last 10 login"
msgstr "最近十次登录"
-#: templates/index.html:156
+#: templates/index.html:128
msgid "Login record"
msgstr "登录记录"
-#: templates/index.html:157
+#: templates/index.html:129
msgid "Last 10 login records."
msgstr "最近十次登录记录."
-#: templates/index.html:170 templates/index.html:172
-msgid "Before"
-msgstr "前"
-
-#: templates/index.html:174
-msgid "Login in "
-msgstr "登录了"
-
-#: templates/index.html:191 templates/index.html:206
+#: templates/index.html:143 templates/index.html:158
msgid "Top 10 users in a week"
msgstr "一周Top10用户"
-#: templates/index.html:207
+#: templates/index.html:159
msgid "User login frequency and last login record."
msgstr "用户登录次数及最近一次登录记录"
-#: templates/index.html:264
+#: templates/index.html:184
msgid "Monthly data overview"
msgstr "月数据总览"
-#: templates/index.html:265
+#: templates/index.html:185
msgid "History summary in one month"
msgstr "一个月内历史汇总"
-#: templates/index.html:273 templates/index.html:297
+#: templates/index.html:193 templates/index.html:217
msgid "Login count"
msgstr "登录次数"
-#: templates/index.html:273 templates/index.html:304
+#: templates/index.html:193 templates/index.html:224
msgid "Active users"
msgstr "活跃用户"
-#: templates/index.html:273 templates/index.html:311
+#: templates/index.html:193 templates/index.html:231
msgid "Active assets"
msgstr "活跃资产"
-#: templates/index.html:338 templates/index.html:388
+#: templates/index.html:262 templates/index.html:313
msgid "Monthly active users"
msgstr "月活跃用户"
-#: templates/index.html:338 templates/index.html:389
+#: templates/index.html:262 templates/index.html:314
msgid "Disable user"
msgstr "禁用用户"
-#: templates/index.html:338 templates/index.html:390
+#: templates/index.html:262 templates/index.html:315
msgid "Month not logged in user"
msgstr "月未登录用户"
-#: templates/index.html:364 templates/index.html:440
+#: templates/index.html:288 templates/index.html:368
msgid "Access to the source"
msgstr "访问来源"
-#: templates/index.html:414 templates/index.html:464
-msgid "Month is logged into the host"
-msgstr "月被登录主机"
+#: templates/index.html:342
+msgid "Month is logged into the asset"
+msgstr "月被登录资产"
-#: templates/index.html:414 templates/index.html:465
+#: templates/index.html:342 templates/index.html:393
msgid "Disable host"
msgstr "禁用主机"
-#: templates/index.html:414 templates/index.html:466
+#: templates/index.html:342 templates/index.html:394
msgid "Month not logged on host"
msgstr "月未登录主机"
+#: templates/index.html:392
+msgid "Month is logged into the host"
+msgstr "月被登录主机"
+
+#: templates/index.html:466
+msgid " times/week"
+msgstr " 次/周"
+
+#: templates/index.html:491 templates/index.html:527
+msgid " times"
+msgstr " 次"
+
+#: templates/index.html:494 templates/index.html:530
+msgid "The time last logged in"
+msgstr "最近一次登录日期"
+
+#: templates/index.html:495 templates/index.html:531
+msgid "At"
+msgstr "于"
+
+#: templates/index.html:510 templates/index.html:545 templates/index.html:580
+msgid "(No)"
+msgstr "(暂无)"
+
+#: templates/index.html:561
+msgid "Before"
+msgstr "前"
+
+#: templates/index.html:562
+msgid "Login in "
+msgstr "登录了"
+
#: templates/rest_framework/base.html:128
msgid "Filters"
msgstr "过滤"
@@ -4676,7 +4900,10 @@ msgid ""
" "
msgstr ""
-#: terminal/forms/storage.py:136
+#: terminal/forms/storage.py:136 xpack/plugins/cloud/models.py:266
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:29
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:112
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:46
msgid "Region"
msgstr "地域"
@@ -5167,7 +5394,7 @@ msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同"
#: users/forms/profile.py:137 users/forms/user.py:90
-#: users/serializers/user.py:154
+#: users/serializers/user.py:163
msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法"
@@ -5199,15 +5426,20 @@ msgstr "添加到用户组"
msgid "* Your password does not meet the requirements"
msgstr "* 您的密码不符合要求"
-#: users/forms/user.py:124 users/serializers/user.py:26
+#: users/forms/user.py:124 users/serializers/user.py:27
msgid "Reset link will be generated and sent to the user"
msgstr "生成重置密码链接,通过邮件发送给用户"
-#: users/forms/user.py:125 users/serializers/user.py:27
+#: users/forms/user.py:125 users/serializers/user.py:28
msgid "Set password"
msgstr "设置密码"
-#: users/forms/user.py:132 users/serializers/user.py:34
+#: users/forms/user.py:132 users/serializers/user.py:35
+#: xpack/plugins/change_auth_plan/models.py:59
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:45
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:67
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:16
msgid "Password strategy"
msgstr "密码策略"
@@ -5215,7 +5447,9 @@ msgstr "密码策略"
msgid "Administrator"
msgstr "管理员"
-#: users/models/user.py:145
+#: users/models/user.py:145 xpack/plugins/orgs/forms.py:29
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:109
+#: xpack/plugins/orgs/templates/orgs/org_list.html:14
msgid "Auditor"
msgstr "审计员"
@@ -5255,39 +5489,39 @@ msgstr "Administrator是初始的超级管理员"
msgid "Auditors cannot be join in the user group"
msgstr "审计员不能被加入到用户组"
-#: users/serializers/user.py:62
+#: users/serializers/user.py:66
msgid "Is first login"
msgstr "首次登录"
-#: users/serializers/user.py:63
+#: users/serializers/user.py:67
msgid "Is valid"
msgstr "账户是否有效"
-#: users/serializers/user.py:64
+#: users/serializers/user.py:68
msgid "Is expired"
msgstr " 是否过期"
-#: users/serializers/user.py:65
+#: users/serializers/user.py:69
msgid "Avatar url"
msgstr "头像路径"
-#: users/serializers/user.py:69
+#: users/serializers/user.py:73
msgid "Groups name"
msgstr "用户组名"
-#: users/serializers/user.py:70
+#: users/serializers/user.py:74
msgid "Source name"
msgstr "用户来源名"
-#: users/serializers/user.py:71
+#: users/serializers/user.py:75
msgid "Role name"
msgstr "角色名"
-#: users/serializers/user.py:90
+#: users/serializers/user.py:94
msgid "Role limit to {}"
msgstr "角色只能为 {}"
-#: users/serializers/user.py:102
+#: users/serializers/user.py:106
msgid "Password does not match security rules"
msgstr "密码不满足安全规则"
@@ -5301,6 +5535,9 @@ msgstr "安全令牌验证"
#: users/templates/users/_base_otp.html:14 users/templates/users/_user.html:13
#: users/templates/users/user_profile_update.html:55
+#: xpack/plugins/cloud/models.py:119
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:57
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:13
msgid "Account"
msgstr "账户"
@@ -5539,26 +5776,6 @@ msgstr "解除用户登录限制后,此用户即可正常登录"
msgid "Reset user MFA success"
msgstr "重置用户多因子认证成功"
-#: users/templates/users/user_disable_mfa.html:6
-#: users/templates/users/user_otp_check_password.html:6
-msgid "Authenticate"
-msgstr "验证身份"
-
-#: users/templates/users/user_disable_mfa.html:11
-msgid ""
-"The account protection has been opened, please complete the following "
-"operations according to the prompts"
-msgstr "账号保护已开启,请根据提示完成以下操作"
-
-#: users/templates/users/user_disable_mfa.html:13
-msgid "Open Authenticator and enter the 6-bit dynamic code"
-msgstr "请打开 验证器,输入6位动态码"
-
-#: users/templates/users/user_disable_mfa.html:23
-#: users/templates/users/user_otp_enable_bind.html:22
-msgid "Six figures"
-msgstr "6位数字"
-
#: users/templates/users/user_group_detail.html:17
#: users/templates/users/user_group_granted_asset.html:18
#: users/views/group.py:83
@@ -5617,6 +5834,11 @@ msgstr "用户已失效"
msgid "User is inactive"
msgstr "用户已禁用"
+#: users/templates/users/user_otp_check_password.html:6
+#: users/templates/users/user_verify_mfa.html:6
+msgid "Authenticate"
+msgstr "验证身份"
+
#: users/templates/users/user_otp_enable_bind.html:6
msgid "Bind one-time password authenticator"
msgstr "绑定一次性密码验证器"
@@ -5627,6 +5849,11 @@ msgid ""
"code for a 6-bit verification code"
msgstr "使用手机 Google Authenticator 应用扫描以下二维码,获取6位验证码"
+#: users/templates/users/user_otp_enable_bind.html:22
+#: users/templates/users/user_verify_mfa.html:23
+msgid "Six figures"
+msgstr "6位数字"
+
#: users/templates/users/user_otp_enable_install_app.html:6
msgid "Install app"
msgstr "安装应用"
@@ -5709,6 +5936,16 @@ msgstr "更新用户"
msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
+#: users/templates/users/user_verify_mfa.html:11
+msgid ""
+"The account protection has been opened, please complete the following "
+"operations according to the prompts"
+msgstr "账号保护已开启,请根据提示完成以下操作"
+
+#: users/templates/users/user_verify_mfa.html:13
+msgid "Open Authenticator and enter the 6-bit dynamic code"
+msgstr "请打开 验证器,输入6位动态码"
+
# msgid "Update user"
# msgstr "更新用户"
#: users/utils.py:24
@@ -5956,19 +6193,19 @@ msgstr "首次登录"
msgid "Profile setting"
msgstr "个人信息设置"
-#: users/views/profile/otp.py:130
+#: users/views/profile/otp.py:144
msgid "MFA enable success"
msgstr "多因子认证启用成功"
-#: users/views/profile/otp.py:131
+#: users/views/profile/otp.py:145
msgid "MFA enable success, return login page"
msgstr "多因子认证启用成功,返回到登录页面"
-#: users/views/profile/otp.py:133
+#: users/views/profile/otp.py:147
msgid "MFA disable success"
msgstr "多因子认证禁用成功"
-#: users/views/profile/otp.py:134
+#: users/views/profile/otp.py:148
msgid "MFA disable success, return login page"
msgstr "多因子认证禁用成功,返回登录页面"
@@ -6004,482 +6241,769 @@ msgstr "用户授权远程应用"
msgid "User granted DatabaseApp"
msgstr "用户授权数据库应用"
-#~ msgid "Password length"
-#~ msgstr "密码长度"
-
-#~ msgid ""
-#~ "Tips: The username of the user on the asset to be modified. if the user "
-#~ "exists, change the password; If the user does not exist, create the user."
-#~ msgstr ""
-#~ "提示:用户名为将要修改的资产上的用户的用户名。如果用户存在,则修改密码;如"
-#~ "果用户不存在,则创建用户。"
-
-#~ msgid "Change auth plan"
-#~ msgstr "改密计划"
-
-#~ msgid "Custom password"
-#~ msgstr "自定义密码"
-
-#~ msgid "All assets use the same random password"
-#~ msgstr "所有资产使用相同的随机密码"
+#: xpack/plugins/change_auth_plan/forms.py:21
+msgid "Password length"
+msgstr "密码长度"
-#~ msgid "All assets use different random password"
-#~ msgstr "所有资产使用不同的随机密码"
-
-#~ msgid "Password rules"
-#~ msgstr "密码规则"
-
-#~ msgid "Change auth plan snapshot"
-#~ msgstr "改密计划快照"
-
-#~ msgid "Change auth plan execution"
-#~ msgstr "改密计划执行"
-
-#~ msgid "Step"
-#~ msgstr "步骤"
-
-#~ msgid "Change auth plan task"
-#~ msgstr "改密计划任务"
-
-#~ msgid "* Please enter custom password"
-#~ msgstr "* 请输入自定义密码"
-
-#~ msgid "* Please enter the correct password length"
-#~ msgstr "* 请输入正确的密码长度"
-
-#~ msgid "* Password length range 6-30 bits"
-#~ msgstr "* 密码长度范围 6-30 位"
-
-#~ msgid "Plan execution list"
-#~ msgstr "执行列表"
-
-#~ msgid "Add asset to this plan"
-#~ msgstr "添加资产"
-
-#~ msgid "Add node to this plan"
-#~ msgstr "添加节点"
-
-#~ msgid ""
-#~ "When the user password on the asset is changed, the action is performed "
-#~ "using the admin user associated with the asset"
-#~ msgstr "更改资产上的用户密码时,将会使用与该资产关联的管理用户进行操作"
-
-#~ msgid "Length"
-#~ msgstr "长度"
-
-#~ msgid "Run plan manually"
-#~ msgstr "手动执行计划"
-
-#~ msgid "Execute failed"
-#~ msgstr "执行失败"
-
-#~ msgid "Execution list of plan"
-#~ msgstr "执行历史列表"
-
-#~ msgid "Log"
-#~ msgstr "日志"
-
-#~ msgid "Retry"
-#~ msgstr "重试"
-
-#~ msgid "Run failed"
-#~ msgstr "执行失败"
-
-#~ msgid "Create plan"
-#~ msgstr "创建计划"
-
-#~ msgid "Invalid/incorrect password"
-#~ msgstr "无效/错误 密码"
-
-#~ msgid "Failed to connect to the host"
-#~ msgstr "连接主机失败"
-
-#~ msgid "Data could not be sent to remote"
-#~ msgstr "无法将数据发送到远程"
-
-#~ msgid "Plan list"
-#~ msgstr "计划列表"
-
-#~ msgid "Update plan"
-#~ msgstr "更新计划"
-
-#~ msgid "Plan execution task list"
-#~ msgstr "执行任务列表"
-
-#~ msgid "Select account"
-#~ msgstr "选择账户"
-
-#~ msgid "Select regions"
-#~ msgstr "选择地域"
-
-#~ msgid "Select instances"
-#~ msgstr "选择实例"
-
-#~ msgid "Select node"
-#~ msgstr "选择节点"
-
-#~ msgid "Select admins"
-#~ msgstr "选择管理员"
-
-#~ msgid "Cloud center"
-#~ msgstr "云管中心"
-
-#~ msgid "Available"
-#~ msgstr "有效"
-
-#~ msgid "Unavailable"
-#~ msgstr "无效"
-
-#~ msgid "Provider"
-#~ msgstr "云服务商"
-
-#~ msgid "Cloud account"
-#~ msgstr "云账号"
-
-#~ msgid "Regions"
-#~ msgstr "地域"
-
-#~ msgid "Instances"
-#~ msgstr "实例"
-
-#~ msgid "Date last sync"
-#~ msgstr "最后同步日期"
-
-#~ msgid "Sync instance task"
-#~ msgstr "同步实例任务"
-
-#~ msgid "Succeed"
-#~ msgstr "成功"
-
-#~ msgid "Date sync"
-#~ msgstr "同步日期"
-
-#~ msgid "Unsync"
-#~ msgstr "未同步"
-
-#~ msgid "Synced"
-#~ msgstr "已同步"
-
-#~ msgid "Released"
-#~ msgstr "已释放"
-
-#~ msgid "Sync task"
-#~ msgstr "同步任务"
-
-#~ msgid "Sync instance task history"
-#~ msgstr "同步实例任务历史"
-
-#~ msgid "Instance"
-#~ msgstr "实例"
+#: xpack/plugins/change_auth_plan/forms.py:79
+msgid ""
+"Tips: The username of the user on the asset to be modified. if the user "
+"exists, change the password; If the user does not exist, create the user."
+msgstr ""
+"提示:用户名为将要修改的资产上的用户的用户名。如果用户存在,则修改密码;如果"
+"用户不存在,则创建用户。"
+
+#: xpack/plugins/change_auth_plan/meta.py:9
+#: xpack/plugins/change_auth_plan/models.py:87
+#: xpack/plugins/change_auth_plan/models.py:174
+#: xpack/plugins/change_auth_plan/views.py:33
+#: xpack/plugins/change_auth_plan/views.py:50
+#: xpack/plugins/change_auth_plan/views.py:74
+#: xpack/plugins/change_auth_plan/views.py:90
+#: xpack/plugins/change_auth_plan/views.py:117
+#: xpack/plugins/change_auth_plan/views.py:132
+#: xpack/plugins/change_auth_plan/views.py:147
+msgid "Change auth plan"
+msgstr "改密计划"
+
+#: xpack/plugins/change_auth_plan/models.py:39
+msgid "Custom password"
+msgstr "自定义密码"
+
+#: xpack/plugins/change_auth_plan/models.py:40
+msgid "All assets use the same random password"
+msgstr "所有资产使用相同的随机密码"
+
+#: xpack/plugins/change_auth_plan/models.py:41
+msgid "All assets use different random password"
+msgstr "所有资产使用不同的随机密码"
+
+#: xpack/plugins/change_auth_plan/models.py:63
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:72
+msgid "Password rules"
+msgstr "密码规则"
+
+#: xpack/plugins/change_auth_plan/models.py:178
+msgid "Change auth plan snapshot"
+msgstr "改密计划快照"
+
+#: xpack/plugins/change_auth_plan/models.py:193
+#: xpack/plugins/change_auth_plan/models.py:279
+msgid "Change auth plan execution"
+msgstr "改密计划执行"
+
+#: xpack/plugins/change_auth_plan/models.py:252
+msgid "Ready"
+msgstr ""
-#~ msgid "Alibaba Cloud"
-#~ msgstr "阿里云"
+#: xpack/plugins/change_auth_plan/models.py:253
+msgid "Preflight check"
+msgstr ""
-#~ msgid "AWS (International)"
-#~ msgstr "AWS (国际)"
+#: xpack/plugins/change_auth_plan/models.py:254
+msgid "Change auth"
+msgstr ""
-#~ msgid "AWS (China)"
-#~ msgstr "AWS (中国)"
+#: xpack/plugins/change_auth_plan/models.py:255
+msgid "Verify auth"
+msgstr ""
-#~ msgid "Huawei Cloud"
-#~ msgstr "华为云"
+#: xpack/plugins/change_auth_plan/models.py:256
+msgid "Keep auth"
+msgstr ""
-#~ msgid "CN North-Beijing4"
-#~ msgstr "华北-北京4"
+#: xpack/plugins/change_auth_plan/models.py:283
+msgid "Step"
+msgstr "步骤"
-#~ msgid "CN East-Shanghai1"
-#~ msgstr "华东-上海1"
+#: xpack/plugins/change_auth_plan/models.py:300
+msgid "Change auth plan task"
+msgstr "改密计划任务"
-#~ msgid "CN East-Shanghai2"
-#~ msgstr "华东-上海2"
+#: xpack/plugins/change_auth_plan/serializers.py:68
+msgid "* Please enter custom password"
+msgstr "* 请输入自定义密码"
-#~ msgid "CN South-Guangzhou"
-#~ msgstr "华南-广州"
+#: xpack/plugins/change_auth_plan/serializers.py:78
+msgid "* Please enter the correct password length"
+msgstr "* 请输入正确的密码长度"
-#~ msgid "CN Southwest-Guiyang1"
-#~ msgstr "西南-贵阳1"
+#: xpack/plugins/change_auth_plan/serializers.py:81
+msgid "* Password length range 6-30 bits"
+msgstr "* 密码长度范围 6-30 位"
-#~ msgid "AP-Hong-Kong"
-#~ msgstr "亚太-香港"
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:19
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:24
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:23
+#: xpack/plugins/change_auth_plan/views.py:133
+msgid "Plan execution list"
+msgstr "执行列表"
-#~ msgid "AP-Bangkok"
-#~ msgstr "亚太-曼谷"
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:62
+msgid "Add asset to this plan"
+msgstr "添加资产"
-#~ msgid "AP-Singapore"
-#~ msgstr "亚太-新加坡"
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:87
+msgid "Add node to this plan"
+msgstr "添加节点"
-#~ msgid "AF-Johannesburg"
-#~ msgstr "非洲-约翰内斯堡"
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:7
+msgid ""
+"When the user password on the asset is changed, the action is performed "
+"using the admin user associated with the asset"
+msgstr "更改资产上的用户密码时,将会使用与该资产关联的管理用户进行操作"
+
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74
+msgid "Length"
+msgstr "长度"
+
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:132
+msgid "Run plan manually"
+msgstr "手动执行计划"
+
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:176
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:102
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:90
+msgid "Execute failed"
+msgstr "执行失败"
+
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:31
+msgid "Execution list of plan"
+msgstr "执行历史列表"
+
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:104
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:106
+msgid "Log"
+msgstr "日志"
+
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:61
+msgid "Retry"
+msgstr "重试"
+
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:95
+msgid "Run failed"
+msgstr "执行失败"
+
+#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:5
+#: xpack/plugins/change_auth_plan/views.py:51
+msgid "Create plan"
+msgstr "创建计划"
+
+#: xpack/plugins/change_auth_plan/utils.py:437
+msgid "Invalid/incorrect password"
+msgstr "无效/错误 密码"
+
+#: xpack/plugins/change_auth_plan/utils.py:439
+msgid "Failed to connect to the host"
+msgstr "连接主机失败"
+
+#: xpack/plugins/change_auth_plan/utils.py:441
+msgid "Data could not be sent to remote"
+msgstr "无法将数据发送到远程"
+
+#: xpack/plugins/change_auth_plan/views.py:34
+msgid "Plan list"
+msgstr "计划列表"
+
+#: xpack/plugins/change_auth_plan/views.py:75
+msgid "Update plan"
+msgstr "更新计划"
+
+#: xpack/plugins/change_auth_plan/views.py:148
+msgid "Plan execution task list"
+msgstr "执行任务列表"
+
+#: xpack/plugins/cloud/forms.py:15
+msgid "Access Key"
+msgstr ""
-#~ msgid "LA-Santiago"
-#~ msgstr "拉美-圣地亚哥"
+#: xpack/plugins/cloud/forms.py:19
+msgid "Secret Key"
+msgstr ""
-#~ msgid "Tencent Cloud"
-#~ msgstr "腾讯云"
+#: xpack/plugins/cloud/forms.py:56
+msgid "Select account"
+msgstr "选择账户"
-#~ msgid "Account detail"
-#~ msgstr "账户详情"
+#: xpack/plugins/cloud/forms.py:62
+msgid "Select regions"
+msgstr "选择地域"
-#~ msgid "Create account"
-#~ msgstr "创建账户"
+#: xpack/plugins/cloud/forms.py:68
+msgid "Select instances"
+msgstr "选择实例"
-#~ msgid "Node & AdminUser"
-#~ msgstr "节点 & 管理用户"
+#: xpack/plugins/cloud/forms.py:74
+msgid "Select node"
+msgstr "选择节点"
-#~ msgid "Load failed"
-#~ msgstr "加载失败"
+#: xpack/plugins/cloud/forms.py:80 xpack/plugins/orgs/forms.py:20
+msgid "Select admins"
+msgstr "选择管理员"
-#~ msgid "Sync task detail"
-#~ msgstr "同步任务详情"
+#: xpack/plugins/cloud/forms.py:85
+msgid "Tips: The asset information is always covered"
+msgstr ""
-#~ msgid "Sync task history"
-#~ msgstr "同步历史列表"
+#: xpack/plugins/cloud/meta.py:9 xpack/plugins/cloud/views.py:27
+#: xpack/plugins/cloud/views.py:44 xpack/plugins/cloud/views.py:62
+#: xpack/plugins/cloud/views.py:78 xpack/plugins/cloud/views.py:92
+#: xpack/plugins/cloud/views.py:109 xpack/plugins/cloud/views.py:127
+#: xpack/plugins/cloud/views.py:143 xpack/plugins/cloud/views.py:158
+#: xpack/plugins/cloud/views.py:172
+msgid "Cloud center"
+msgstr "云管中心"
+
+#: xpack/plugins/cloud/models.py:29
+msgid "Available"
+msgstr "有效"
-#~ msgid "Sync instance list"
-#~ msgstr "同步实例列表"
+#: xpack/plugins/cloud/models.py:30
+msgid "Unavailable"
+msgstr "无效"
-#~ msgid "Run task manually"
-#~ msgstr "手动执行任务"
+#: xpack/plugins/cloud/models.py:39
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:51
+#: xpack/plugins/cloud/templates/cloud/account_list.html:13
+msgid "Provider"
+msgstr "云服务商"
-#~ msgid "Sync success"
-#~ msgstr "同步成功"
+#: xpack/plugins/cloud/models.py:42
+msgid "Access key id"
+msgstr ""
-#~ msgid "New count"
-#~ msgstr "新增"
+#: xpack/plugins/cloud/models.py:46
+msgid "Access key secret"
+msgstr ""
-#~ msgid "Unsync count"
-#~ msgstr "未同步"
+#: xpack/plugins/cloud/models.py:64
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:26
+msgid "Cloud account"
+msgstr "云账号"
-#~ msgid "Synced count"
-#~ msgstr "已同步"
+#: xpack/plugins/cloud/models.py:122
+msgid "Regions"
+msgstr "地域"
-#~ msgid "Released count"
-#~ msgstr "已释放"
+#: xpack/plugins/cloud/models.py:125
+msgid "Instances"
+msgstr "实例"
-#~ msgid "Delete released assets"
-#~ msgstr "删除已释放的资产"
+#: xpack/plugins/cloud/models.py:136
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:69
+msgid "Covered always"
+msgstr ""
-#~ msgid "Create sync instance task"
-#~ msgstr "创建同步实例任务"
+#: xpack/plugins/cloud/models.py:142
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:104
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:17
+msgid "Date last sync"
+msgstr "最后同步日期"
-#~ msgid "Run count"
-#~ msgstr "执行次数"
+#: xpack/plugins/cloud/models.py:153 xpack/plugins/cloud/models.py:210
+msgid "Sync instance task"
+msgstr "同步实例任务"
-#~ msgid "Instance count"
-#~ msgstr "实例个数"
+#: xpack/plugins/cloud/models.py:205
+msgid "Succeed"
+msgstr "成功"
-#~ msgid "Account unavailable"
-#~ msgstr "账户无效"
+#: xpack/plugins/cloud/models.py:220 xpack/plugins/cloud/models.py:275
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:51
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:49
+msgid "Date sync"
+msgstr "同步日期"
-#~ msgid "Update account"
-#~ msgstr "更新账户"
+#: xpack/plugins/cloud/models.py:248
+msgid "Unsync"
+msgstr "未同步"
-#~ msgid "Sync instance task list"
-#~ msgstr "同步实例任务列表"
+#: xpack/plugins/cloud/models.py:249 xpack/plugins/cloud/models.py:250
+msgid "Synced"
+msgstr "已同步"
-#~ msgid "Create sync Instance task"
-#~ msgstr "创建同步实例任务"
+#: xpack/plugins/cloud/models.py:251
+msgid "Released"
+msgstr "已释放"
-#~ msgid "Update sync Instance task"
-#~ msgstr "更新同步实例任务"
+#: xpack/plugins/cloud/models.py:256
+msgid "Sync task"
+msgstr "同步任务"
-#~ msgid "Gathered user"
-#~ msgstr "收集用户"
+#: xpack/plugins/cloud/models.py:260
+msgid "Sync instance task history"
+msgstr "同步实例任务历史"
-#~ msgid "Gather user task"
-#~ msgstr "收集用户任务"
+#: xpack/plugins/cloud/models.py:263
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:45
+msgid "Instance"
+msgstr "实例"
-#~ msgid "Task"
-#~ msgstr "任务"
+#: xpack/plugins/cloud/providers/aliyun.py:19
+msgid "Alibaba Cloud"
+msgstr "阿里云"
-#~ msgid "gather user task execution"
-#~ msgstr "收集用户执行"
+#: xpack/plugins/cloud/providers/aws.py:15
+msgid "AWS (International)"
+msgstr "AWS (国际)"
-#~ msgid "Assets is empty, please change nodes"
-#~ msgstr "资产为空,请更改节点"
+#: xpack/plugins/cloud/providers/aws_china.py:9
+msgid "AWS (China)"
+msgstr "AWS (中国)"
-#~ msgid "Asset user"
-#~ msgstr "资产用户"
+#: xpack/plugins/cloud/providers/huaweicloud.py:17
+msgid "Huawei Cloud"
+msgstr "华为云"
-#~ msgid "Create task"
-#~ msgstr "创建任务"
+#: xpack/plugins/cloud/providers/huaweicloud.py:20
+msgid "AF-Johannesburg"
+msgstr "非洲-约翰内斯堡"
-#~ msgid "Periodic"
-#~ msgstr "定时执行"
+#: xpack/plugins/cloud/providers/huaweicloud.py:21
+msgid "AP-Bangkok"
+msgstr "亚太-曼谷"
-#~ msgid "Gathered user list"
-#~ msgstr "收集用户列表"
+#: xpack/plugins/cloud/providers/huaweicloud.py:22
+msgid "AP-Hong Kong"
+msgstr "亚太-香港"
-#~ msgid "Update task"
-#~ msgstr "更新任务"
+#: xpack/plugins/cloud/providers/huaweicloud.py:23
+msgid "AP-Singapore"
+msgstr "亚太-新加坡"
-#~ msgid "Title of login page"
-#~ msgstr "登录页面标题"
+#: xpack/plugins/cloud/providers/huaweicloud.py:24
+msgid "CN East-Shanghai1"
+msgstr "华东-上海1"
-#~ msgid ""
-#~ "Tips: This will be displayed on the enterprise user login page. (eg: "
-#~ "Welcome to the JumpServer open source fortress)"
-#~ msgstr ""
-#~ "提示:将会显示在企业版用户登录页面(eg: 欢迎使用JumpServer开源堡垒机)"
+#: xpack/plugins/cloud/providers/huaweicloud.py:25
+msgid "CN East-Shanghai2"
+msgstr "华东-上海2"
-#~ msgid "Image of login page"
-#~ msgstr "登录页面图片"
+#: xpack/plugins/cloud/providers/huaweicloud.py:26
+msgid "CN North-Beijing1"
+msgstr "华北-北京1"
-#~ msgid ""
-#~ "Tips: This will be displayed on the enterprise user login page. (suggest "
-#~ "image size: 492px*472px)"
-#~ msgstr "提示:将会显示在企业版用户登录页面(建议图片大小为: 492*472px)"
+#: xpack/plugins/cloud/providers/huaweicloud.py:27
+msgid "CN North-Beijing4"
+msgstr "华北-北京4"
-#~ msgid "Website icon"
-#~ msgstr "网站图标"
+#: xpack/plugins/cloud/providers/huaweicloud.py:28
+msgid "CN Northeast-Dalian"
+msgstr "东北-大连"
-#~ msgid "Tips: website icon. (suggest image size: 16px*16px)"
-#~ msgstr "提示:网站图标(建议图片大小为: 16px*16px)"
+#: xpack/plugins/cloud/providers/huaweicloud.py:29
+msgid "CN South-Guangzhou"
+msgstr "华南-广州"
-#~ msgid "Logo of management page"
-#~ msgstr "管理页面logo"
+#: xpack/plugins/cloud/providers/huaweicloud.py:30
+msgid "CN Southwest-Guiyang1"
+msgstr "西南-贵阳1"
-#~ msgid ""
-#~ "Tips: This will appear at the top left of the administration page. "
-#~ "(suggest image size: 185px*55px)"
-#~ msgstr "提示:将会显示在管理页面左上方(建议图片大小为: 185px*55px)"
+#: xpack/plugins/cloud/providers/huaweicloud.py:31
+msgid "EU-Paris"
+msgstr ""
-#~ msgid "Logo of logout page"
-#~ msgstr "退出页面logo"
+#: xpack/plugins/cloud/providers/qcloud.py:17
+msgid "Tencent Cloud"
+msgstr "腾讯云"
+
+#: xpack/plugins/cloud/templates/cloud/account_detail.html:17
+#: xpack/plugins/cloud/views.py:79
+msgid "Account detail"
+msgstr "账户详情"
+
+#: xpack/plugins/cloud/templates/cloud/account_list.html:5
+#: xpack/plugins/cloud/views.py:45
+msgid "Create account"
+msgstr "创建账户"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:33
+msgid "Node & AdminUser"
+msgstr "节点 & 管理用户"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:66
+msgid "Load failed"
+msgstr "加载失败"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:17
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:19
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:18
+#: xpack/plugins/cloud/views.py:144
+msgid "Sync task detail"
+msgstr "同步任务详情"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:20
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:22
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:21
+#: xpack/plugins/cloud/views.py:159
+msgid "Sync task history"
+msgstr "同步历史列表"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:23
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:25
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:24
+#: xpack/plugins/cloud/views.py:173
+msgid "Sync instance list"
+msgstr "同步实例列表"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:145
+msgid "Run task manually"
+msgstr "手动执行任务"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:188
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:102
+msgid "Sync success"
+msgstr "同步成功"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:46
+msgid "New count"
+msgstr "新增"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:47
+msgid "Unsync count"
+msgstr "未同步"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:48
+msgid "Synced count"
+msgstr "已同步"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:49
+msgid "Released count"
+msgstr "已释放"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63
+msgid "Delete released assets"
+msgstr "删除已释放的资产"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:5
+msgid "Create sync instance task"
+msgstr "创建同步实例任务"
+
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:14
+msgid "Run count"
+msgstr "执行次数"
-#~ msgid ""
-#~ "Tips: This will be displayed on the enterprise user logout page. (suggest "
-#~ "image size: 82px*82px)"
-#~ msgstr "提示:将会显示在企业版用户退出页面(建议图片大小为:82px*82px)"
+#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:15
+msgid "Instance count"
+msgstr "实例个数"
-#~ msgid "Interface settings"
-#~ msgstr "界面设置"
+#: xpack/plugins/cloud/utils.py:38
+msgid "Account unavailable"
+msgstr "账户无效"
-#~ msgid "Interface setting"
-#~ msgstr "界面设置"
+#: xpack/plugins/cloud/views.py:63
+msgid "Update account"
+msgstr "更新账户"
-#~ msgid "Restore Default"
-#~ msgstr "恢复默认"
+#: xpack/plugins/cloud/views.py:93
+msgid "Sync instance task list"
+msgstr "同步实例任务列表"
-#~ msgid "This will restore default Settings of the interface !!!"
-#~ msgstr "您确定要恢复默认初始化吗?"
+#: xpack/plugins/cloud/views.py:110
+msgid "Create sync Instance task"
+msgstr "创建同步实例任务"
-#~ msgid "Restore default successfully."
-#~ msgstr "恢复默认成功!"
+#: xpack/plugins/cloud/views.py:128
+msgid "Update sync Instance task"
+msgstr "更新同步实例任务"
-#~ msgid "Restore default failed."
-#~ msgstr "恢复默认失败!"
+#: xpack/plugins/gathered_user/meta.py:11
+#: xpack/plugins/gathered_user/views.py:21
+#: xpack/plugins/gathered_user/views.py:34
+#: xpack/plugins/gathered_user/views.py:49
+#: xpack/plugins/gathered_user/views.py:69
+msgid "Gathered user"
+msgstr "收集用户"
-#~ msgid "It is already in the default setting state!"
-#~ msgstr "当前已经是初始化状态!"
+#: xpack/plugins/gathered_user/models.py:39
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:13
+msgid "Gather user task"
+msgstr "收集用户任务"
-#~ msgid "License"
-#~ msgstr "许可证"
+#: xpack/plugins/gathered_user/models.py:73
+msgid "Task"
+msgstr "任务"
-#~ msgid "Standard edition"
-#~ msgstr "标准版"
+#: xpack/plugins/gathered_user/models.py:85
+msgid "gather user task execution"
+msgstr "收集用户执行"
-#~ msgid "Enterprise edition"
-#~ msgstr "企业版"
+#: xpack/plugins/gathered_user/models.py:91
+msgid "Assets is empty, please change nodes"
+msgstr "资产为空,请更改节点"
-#~ msgid "Ultimate edition"
-#~ msgstr "旗舰版"
+#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:170
+msgid "Asset user"
+msgstr "资产用户"
-#~ msgid "Import license"
-#~ msgstr "导入许可证"
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:7
+#: xpack/plugins/gathered_user/views.py:50
+msgid "Create task"
+msgstr "创建任务"
-#~ msgid "License file"
-#~ msgstr "许可证文件"
+#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:18
+msgid "Periodic"
+msgstr "定时执行"
-#~ msgid "Please Import License"
-#~ msgstr "请导入许可证"
+#: xpack/plugins/gathered_user/views.py:22
+msgid "Gathered user list"
+msgstr "收集用户列表"
-#~ msgid "License has expired"
-#~ msgstr "许可证已经过期"
+#: xpack/plugins/gathered_user/views.py:70
+msgid "Update task"
+msgstr "更新任务"
-#~ msgid "The license will at "
-#~ msgstr "许可证将在 "
+#: xpack/plugins/interface/forms.py:17 xpack/plugins/interface/models.py:15
+msgid "Title of login page"
+msgstr "登录页面标题"
-#~ msgid " expired."
-#~ msgstr " 过期。"
+#: xpack/plugins/interface/forms.py:19
+msgid ""
+"Tips: This will be displayed on the enterprise user login page. (eg: Welcome "
+"to the JumpServer open source fortress)"
+msgstr "提示:将会显示在企业版用户登录页面(eg: 欢迎使用JumpServer开源堡垒机)"
-#~ msgid "License detail"
-#~ msgstr "许可证详情"
+#: xpack/plugins/interface/forms.py:25 xpack/plugins/interface/models.py:19
+msgid "Image of login page"
+msgstr "登录页面图片"
-#~ msgid "No license"
-#~ msgstr "暂无许可证"
+#: xpack/plugins/interface/forms.py:27
+msgid ""
+"Tips: This will be displayed on the enterprise user login page. (suggest "
+"image size: 492px*472px)"
+msgstr "提示:将会显示在企业版用户登录页面(建议图片大小为: 492*472px)"
-#~ msgid "Subscription ID"
-#~ msgstr "订阅授权ID"
+#: xpack/plugins/interface/forms.py:33 xpack/plugins/interface/models.py:23
+msgid "Website icon"
+msgstr "网站图标"
-#~ msgid "Corporation"
-#~ msgstr "公司"
+#: xpack/plugins/interface/forms.py:35
+msgid "Tips: website icon. (suggest image size: 16px*16px)"
+msgstr "提示:网站图标(建议图片大小为: 16px*16px)"
-#~ msgid "Expired"
-#~ msgstr "过期时间"
+#: xpack/plugins/interface/forms.py:40 xpack/plugins/interface/models.py:27
+msgid "Logo of management page"
+msgstr "管理页面logo"
-#~ msgid "Edition"
-#~ msgstr "版本"
+#: xpack/plugins/interface/forms.py:42
+msgid ""
+"Tips: This will appear at the top left of the administration page. (suggest "
+"image size: 185px*55px)"
+msgstr "提示:将会显示在管理页面左上方(建议图片大小为: 185px*55px)"
-#~ msgid "Technology consulting"
-#~ msgstr "技术咨询"
+#: xpack/plugins/interface/forms.py:48 xpack/plugins/interface/models.py:31
+msgid "Logo of logout page"
+msgstr "退出页面logo"
-#~ msgid "Consult"
-#~ msgstr "咨询"
+#: xpack/plugins/interface/forms.py:50
+msgid ""
+"Tips: This will be displayed on the enterprise user logout page. (suggest "
+"image size: 82px*82px)"
+msgstr "提示:将会显示在企业版用户退出页面(建议图片大小为:82px*82px)"
+
+#: xpack/plugins/interface/meta.py:10
+msgid "Interface settings"
+msgstr "界面设置"
+
+#: xpack/plugins/interface/templates/interface/interface.html:15
+#: xpack/plugins/interface/views.py:24 xpack/plugins/interface/views.py:25
+msgid "Interface setting"
+msgstr "界面设置"
+
+#: xpack/plugins/interface/templates/interface/interface.html:73
+#: xpack/plugins/interface/templates/interface/interface.html:108
+#: xpack/plugins/interface/templates/interface/interface.html:115
+msgid "Restore Default"
+msgstr "恢复默认"
+
+#: xpack/plugins/interface/templates/interface/interface.html:98
+msgid "This will restore default Settings of the interface !!!"
+msgstr "您确定要恢复默认初始化吗?"
+
+#: xpack/plugins/interface/templates/interface/interface.html:107
+#: xpack/plugins/interface/views.py:55
+msgid "Restore default successfully."
+msgstr "恢复默认成功!"
+
+#: xpack/plugins/interface/templates/interface/interface.html:114
+msgid "Restore default failed."
+msgstr "恢复默认失败!"
+
+#: xpack/plugins/interface/views.py:51
+msgid "It is already in the default setting state!"
+msgstr "当前已经是初始化状态!"
+
+#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:94
+#: xpack/plugins/license/templates/license/license_detail.html:41
+#: xpack/plugins/license/templates/license/license_detail.html:46
+#: xpack/plugins/license/views.py:32
+msgid "License"
+msgstr "许可证"
+
+#: xpack/plugins/license/models.py:74
+msgid "Standard edition"
+msgstr "标准版"
+
+#: xpack/plugins/license/models.py:76
+msgid "Enterprise edition"
+msgstr "企业版"
+
+#: xpack/plugins/license/models.py:78
+msgid "Ultimate edition"
+msgstr "旗舰版"
+
+#: xpack/plugins/license/templates/license/_license_import_modal.html:4
+#: xpack/plugins/license/templates/license/license_detail.html:86
+msgid "Import license"
+msgstr "导入许可证"
+
+#: xpack/plugins/license/templates/license/_license_import_modal.html:9
+msgid "License file"
+msgstr "许可证文件"
+
+#: xpack/plugins/license/templates/license/license_detail.html:11
+msgid "Please Import License"
+msgstr "请导入许可证"
+
+#: xpack/plugins/license/templates/license/license_detail.html:13
+#: xpack/plugins/license/templates/license/license_detail.html:47
+msgid "License has expired"
+msgstr "许可证已经过期"
+
+#: xpack/plugins/license/templates/license/license_detail.html:15
+msgid "The license will at "
+msgstr "许可证将在 "
+
+#: xpack/plugins/license/templates/license/license_detail.html:15
+msgid " expired."
+msgstr " 过期。"
+
+#: xpack/plugins/license/templates/license/license_detail.html:28
+#: xpack/plugins/license/views.py:33
+msgid "License detail"
+msgstr "许可证详情"
+
+#: xpack/plugins/license/templates/license/license_detail.html:42
+msgid "No license"
+msgstr "暂无许可证"
+
+#: xpack/plugins/license/templates/license/license_detail.html:51
+msgid "Subscription ID"
+msgstr "订阅授权ID"
+
+#: xpack/plugins/license/templates/license/license_detail.html:55
+msgid "Corporation"
+msgstr "公司"
+
+#: xpack/plugins/license/templates/license/license_detail.html:59
+msgid "Expired"
+msgstr "过期时间"
+
+#: xpack/plugins/license/templates/license/license_detail.html:67
+msgid "Edition"
+msgstr "版本"
-#~ msgid "License import successfully"
-#~ msgstr "许可证导入成功"
+#: xpack/plugins/license/templates/license/license_detail.html:93
+msgid "Technology consulting"
+msgstr "技术咨询"
-#~ msgid "License is invalid"
-#~ msgstr "无效的许可证"
+#: xpack/plugins/license/templates/license/license_detail.html:96
+msgid "Consult"
+msgstr "咨询"
-#~ msgid "Select auditor"
-#~ msgstr "选择审计员"
+#: xpack/plugins/license/views.py:47
+msgid "License import successfully"
+msgstr "许可证导入成功"
-#~ msgid "Admin"
-#~ msgstr "管理员"
+#: xpack/plugins/license/views.py:49
+msgid "License is invalid"
+msgstr "无效的许可证"
-#~ msgid "Organizations"
-#~ msgstr "组织管理"
+#: xpack/plugins/orgs/forms.py:23
+msgid "Select auditor"
+msgstr "选择审计员"
-#~ msgid "Org detail"
-#~ msgstr "组织详情"
+#: xpack/plugins/orgs/forms.py:28
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:75
+#: xpack/plugins/orgs/templates/orgs/org_list.html:13
+msgid "Admin"
+msgstr "管理员"
-#~ msgid "Add admin"
-#~ msgstr "添加管理员"
+#: xpack/plugins/orgs/meta.py:8 xpack/plugins/orgs/views.py:27
+#: xpack/plugins/orgs/views.py:44 xpack/plugins/orgs/views.py:62
+#: xpack/plugins/orgs/views.py:85 xpack/plugins/orgs/views.py:116
+msgid "Organizations"
+msgstr "组织管理"
+
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:17
+#: xpack/plugins/orgs/templates/orgs/org_users.html:13
+#: xpack/plugins/orgs/views.py:86
+msgid "Org detail"
+msgstr "组织详情"
+
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:20
+#: xpack/plugins/orgs/templates/orgs/org_users.html:16
+msgid "Org users"
+msgstr "组织用户"
+
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:83
+msgid "Add admin"
+msgstr "添加管理员"
+
+#: xpack/plugins/orgs/templates/orgs/org_detail.html:117
+msgid "Add auditor"
+msgstr "添加审计员"
+
+#: xpack/plugins/orgs/templates/orgs/org_list.html:5
+msgid "Create organization "
+msgstr "创建组织"
+
+#: xpack/plugins/orgs/templates/orgs/org_users.html:59
+msgid "Add user to organization"
+msgstr "添加用户"
-#~ msgid "Create organization "
-#~ msgstr "创建组织"
+#: xpack/plugins/orgs/views.py:28
+msgid "Org list"
+msgstr "组织列表"
-#~ msgid "Org list"
-#~ msgstr "组织列表"
+#: xpack/plugins/orgs/views.py:45
+msgid "Create org"
+msgstr "创建组织"
-#~ msgid "Create org"
-#~ msgstr "创建组织"
+#: xpack/plugins/orgs/views.py:63
+msgid "Update org"
+msgstr "更新组织"
-#~ msgid "Update org"
-#~ msgstr "更新组织"
+#: xpack/plugins/orgs/views.py:117
+msgid "Org user list"
+msgstr "组织用户列表"
-#~ msgid "Vault"
-#~ msgstr "密码匣子"
+#: xpack/plugins/vault/meta.py:11 xpack/plugins/vault/views.py:23
+#: xpack/plugins/vault/views.py:38
+msgid "Vault"
+msgstr "密码匣子"
-#~ msgid "Import vault"
-#~ msgstr "导入密码"
+#: xpack/plugins/vault/templates/vault/_xpack_import_modal.html:4
+msgid "Import vault"
+msgstr "导入密码"
-#~ msgid "vault"
-#~ msgstr "密码匣子"
+#: xpack/plugins/vault/templates/vault/vault.html:64
+msgid "vault"
+msgstr "密码匣子"
-#~ msgid "vault list"
-#~ msgstr "密码匣子"
+#: xpack/plugins/vault/views.py:24
+msgid "vault list"
+msgstr "密码匣子"
-#~ msgid "vault create"
-#~ msgstr "创建"
+#: xpack/plugins/vault/views.py:39
+msgid "vault create"
+msgstr "创建"
-#~ msgid "Tips: The asset information is always covered"
-#~ msgstr "提示:资产信息总是被覆盖"
+#~ msgid "LA-Santiago"
+#~ msgstr "拉美-圣地亚哥"
-#~ msgid "Covered always"
-#~ msgstr "总是覆盖"
+#~ msgid "Total hosts"
+#~ msgstr "主机总数"
#~ msgid "* For security, do not change {}'s password"
#~ msgstr "* 为了安全,不能修改 {} 的密码"
diff --git a/apps/orgs/api.py b/apps/orgs/api.py
index ea4a48955..63eebb25d 100644
--- a/apps/orgs/api.py
+++ b/apps/orgs/api.py
@@ -1,14 +1,16 @@
# -*- coding: utf-8 -*-
#
-from rest_framework import status
+from django.shortcuts import get_object_or_404
+from rest_framework import status, generics
from rest_framework.views import Response
from rest_framework_bulk import BulkModelViewSet
from common.permissions import IsSuperUserOrAppUser
from .models import Organization
from .serializers import OrgSerializer, OrgReadSerializer, \
- OrgMembershipUserSerializer, OrgMembershipAdminSerializer
+ OrgMembershipUserSerializer, OrgMembershipAdminSerializer, \
+ OrgAllUserSerializer
from users.models import User, UserGroup
from assets.models import Asset, Domain, AdminUser, SystemUser, Label
from perms.models import AssetPermission
@@ -67,3 +69,15 @@ class OrgMembershipUsersViewSet(OrgMembershipModelViewSetMixin, BulkModelViewSet
membership_class = Organization.users.through
permission_classes = (IsSuperUserOrAppUser, )
+
+class OrgAllUserListApi(generics.ListAPIView):
+ permission_classes = (IsSuperUserOrAppUser,)
+ serializer_class = OrgAllUserSerializer
+ filter_fields = ("username", "name")
+ search_fields = filter_fields
+
+ def get_queryset(self):
+ pk = self.kwargs.get("pk")
+ org = get_object_or_404(Organization, pk=pk)
+ users = org.get_org_users().only(*self.serializer_class.Meta.only_fields)
+ return users
diff --git a/apps/orgs/serializers.py b/apps/orgs/serializers.py
index 281b9ec75..5ff3b5f4d 100644
--- a/apps/orgs/serializers.py
+++ b/apps/orgs/serializers.py
@@ -80,3 +80,15 @@ class OrgMembershipUserSerializer(OrgMembershipSerializerMixin, ModelSerializer)
model = Organization.users.through
list_serializer_class = AdaptedBulkListSerializer
fields = '__all__'
+
+
+class OrgAllUserSerializer(serializers.Serializer):
+ user = serializers.UUIDField(read_only=True, source='id')
+ user_display = serializers.SerializerMethodField()
+
+ class Meta:
+ only_fields = ['id', 'username', 'name']
+
+ @staticmethod
+ def get_user_display(obj):
+ return str(obj)
diff --git a/apps/orgs/urls/api_urls.py b/apps/orgs/urls/api_urls.py
index 782269c81..17f8c3c8a 100644
--- a/apps/orgs/urls/api_urls.py
+++ b/apps/orgs/urls/api_urls.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
-from django.urls import re_path
+from django.urls import re_path, path
from rest_framework.routers import DefaultRouter
from common import api as capi
@@ -24,6 +24,7 @@ old_version_urlpatterns = [
]
urlpatterns = [
+ path('/users/all/', api.OrgAllUserListApi.as_view(), name='org-all-users'),
]
urlpatterns += router.urls + old_version_urlpatterns
diff --git a/apps/perms/apps.py b/apps/perms/apps.py
index d6fa5f712..d40373e08 100644
--- a/apps/perms/apps.py
+++ b/apps/perms/apps.py
@@ -1,14 +1,7 @@
from __future__ import unicode_literals
-from django.conf import settings
from django.apps import AppConfig
class PermsConfig(AppConfig):
name = 'perms'
-
- def ready(self):
- from . import signals_handler
- if not settings.XPACK_ENABLED:
- settings.ASSETS_PERM_CACHE_ENABLE = False
- return super().ready()
diff --git a/apps/perms/utils/asset_permission.py b/apps/perms/utils/asset_permission.py
index cdcf4389f..fce4ee12d 100644
--- a/apps/perms/utils/asset_permission.py
+++ b/apps/perms/utils/asset_permission.py
@@ -302,7 +302,6 @@ class AssetPermissionUtil(AssetPermissionUtilCacheMixin):
continue
ancestors = self.full_tree.ancestors(
child.identifier, with_self=False, deep=True,
- with_assets=False,
)
# print("Get ancestors: {}".format(len(ancestors)))
if not ancestors:
diff --git a/apps/templates/index.html b/apps/templates/index.html
index b667f473d..1942ad2cb 100644
--- a/apps/templates/index.html
+++ b/apps/templates/index.html
@@ -11,7 +11,7 @@
{% trans 'Total users' %}
@@ -19,12 +19,12 @@
- Hosts
-
{% trans 'Total hosts' %}
+ Assets
+ {% trans 'Total assets' %}
-
-
All hosts
+
+
All assets
@@ -36,7 +36,7 @@
{% trans 'Online users' %}
@@ -50,7 +50,7 @@
@@ -58,19 +58,11 @@
-
+
@@ -81,13 +73,13 @@
-
-
-
{% trans 'Host' %}
+
+
{% trans 'Asset' %}
@@ -120,27 +112,7 @@
{% trans 'Top 10 assets in a week'%}
{% trans 'Login frequency and last login record.' %}
-
- {% if week_asset_hot_ten %}
- {% for data in week_asset_hot_ten %}
-
-
-
-
- {{ data.asset }}
-
- {{ data.total }}{% trans ' times' %}
-
-
-
{% trans 'The time last logged in' %}
-
{% trans 'At' %} {{ data.last|date:"Y-m-d H:i:s" }}
-
-
-
- {% endfor %}
- {% else %}
-
{% trans '(No)' %}
- {% endif %}
+
@@ -158,27 +130,7 @@
-
- {% if last_login_ten %}
- {% for login in last_login_ten %}
-
-
-
-
-
- {% ifequal login.is_finished 0 %}
- {{ login.date_start|timesince }} {% trans 'Before' %}
- {% else %}
- {{ login.date_start|timesince }} {% trans 'Before' %}
- {% endifequal %}
- {{ login.user }} {% trans 'Login in ' %}{{ login.asset }}
- {{ login.date_start }}
-
-
- {% endfor %}
- {% else %}
-
{% trans '(No)' %}
- {% endif %}
+
@@ -206,27 +158,7 @@
{% trans 'Top 10 users in a week' %}
{% trans 'User login frequency and last login record.' %}
-
- {% if week_user_hot_ten %}
- {% for data in week_user_hot_ten %}
-
-
-
-
- {{ data.user }}
-
- {{ data.total }}{% trans ' times' %}
-
-
-
{% trans 'The time last logged in' %}
-
{% trans 'At' %} {{ data.last|date:"Y-m-d H:i:s" }}
-
-
-
- {% endfor %}
- {% else %}
-
{% trans '(No)' %}
- {% endif %}
+
@@ -238,27 +170,15 @@
{% block custom_foot_js %}
{% endblock %}
diff --git a/apps/terminal/serializers/terminal.py b/apps/terminal/serializers/terminal.py
index 1df4b40e6..c7a91d009 100644
--- a/apps/terminal/serializers/terminal.py
+++ b/apps/terminal/serializers/terminal.py
@@ -16,7 +16,7 @@ class TerminalSerializer(serializers.ModelSerializer):
fields = [
'id', 'name', 'remote_addr', 'http_port', 'ssh_port',
'comment', 'is_accepted', "is_active", 'session_online',
- 'is_alive'
+ 'is_alive', 'date_created', 'command_storage', 'replay_storage'
]
@staticmethod
diff --git a/apps/users/api/mixins.py b/apps/users/api/mixins.py
index 273ab9074..2ff60b615 100644
--- a/apps/users/api/mixins.py
+++ b/apps/users/api/mixins.py
@@ -1,9 +1,13 @@
# -*- coding: utf-8 -*-
#
from .. import utils
+from users.models import User
class UserQuerysetMixin:
def get_queryset(self):
- queryset = utils.get_current_org_members()
+ if self.request.query_params.get('all'):
+ queryset = User.objects.exclude(role=User.ROLE_APP)
+ else:
+ queryset = utils.get_current_org_members()
return queryset
diff --git a/apps/users/signals_handler.py b/apps/users/signals_handler.py
index aa1458c7d..37f7c42fc 100644
--- a/apps/users/signals_handler.py
+++ b/apps/users/signals_handler.py
@@ -3,12 +3,17 @@
from django.dispatch import receiver
from django.db.models.signals import m2m_changed
+from django_auth_ldap.backend import populate_user
+from django.conf import settings
from django_cas_ng.signals import cas_user_authenticated
+from jms_oidc_rp.signals import openid_create_or_update_user
+
from common.utils import get_logger
from .signals import post_user_create
from .models import User
+
logger = get_logger(__file__)
@@ -37,3 +42,33 @@ def on_cas_user_authenticated(sender, user, created, **kwargs):
if created:
user.source = user.SOURCE_CAS
user.save()
+
+
+@receiver(populate_user)
+def on_ldap_create_user(sender, user, ldap_user, **kwargs):
+ if user and user.username not in ['admin']:
+ exists = User.objects.filter(username=user.username).exists()
+ if not exists:
+ user.source = user.SOURCE_LDAP
+ user.save()
+
+
+@receiver(openid_create_or_update_user)
+def on_openid_create_or_update_user(sender, request, user, created, name, username, email, **kwargs):
+ if created:
+ logger.debug(
+ "Receive OpenID user created signal: {}, "
+ "Set user source is: {}".format(user, User.SOURCE_OPENID)
+ )
+ user.source = User.SOURCE_OPENID
+ user.save()
+ elif not created and settings.AUTH_OPENID_ALWAYS_UPDATE_USER:
+ logger.debug(
+ "Receive OpenID user updated signal: {}, "
+ "Update user info: {}"
+ "".format(user, "name: {}|username: {}|email: {}".format(name, username, email))
+ )
+ user.name = name
+ user.username = username
+ user.email = email
+ user.save()
diff --git a/apps/users/templates/users/user_disable_mfa.html b/apps/users/templates/users/user_verify_mfa.html
similarity index 100%
rename from apps/users/templates/users/user_disable_mfa.html
rename to apps/users/templates/users/user_verify_mfa.html
diff --git a/apps/users/views/profile/otp.py b/apps/users/views/profile/otp.py
index 7bae70c58..2d823f5ab 100644
--- a/apps/users/views/profile/otp.py
+++ b/apps/users/views/profile/otp.py
@@ -83,12 +83,26 @@ class UserOtpEnableBindView(TemplateView, FormView):
return super().get_context_data(**kwargs)
-class UserDisableMFAView(FormView):
- template_name = 'users/user_disable_mfa.html'
+class UserVerifyMFAView(FormView):
+ template_name = 'users/user_verify_mfa.html'
form_class = forms.UserCheckOtpCodeForm
success_url = reverse_lazy('users:user-otp-settings-success')
permission_classes = [IsValidUser]
+ def form_valid(self, form):
+ user = self.request.user
+ otp_code = form.cleaned_data.get('otp_code')
+
+ valid = user.check_mfa(otp_code)
+ if valid:
+ return super().form_valid(form)
+ else:
+ error = _('MFA code invalid, or ntp sync server time')
+ form.add_error('otp_code', error)
+ return super().form_invalid(form)
+
+
+class UserDisableMFAView(UserVerifyMFAView):
def form_valid(self, form):
user = self.request.user
otp_code = form.cleaned_data.get('otp_code')
@@ -104,7 +118,7 @@ class UserDisableMFAView(FormView):
return super().form_invalid(form)
-class UserOtpUpdateView(UserDisableMFAView):
+class UserOtpUpdateView(UserVerifyMFAView):
success_url = reverse_lazy('users:user-otp-enable-bind')
diff --git a/config_example.yml b/config_example.yml
index 5699f4ff0..30cfabc3e 100644
--- a/config_example.yml
+++ b/config_example.yml
@@ -29,7 +29,6 @@ BOOTSTRAP_TOKEN:
# 使用单文件sqlite数据库
# DB_ENGINE: sqlite3
# DB_NAME:
-
# MySQL or postgres setting like:
# 使用Mysql作为数据库
DB_ENGINE: mysql
@@ -54,16 +53,27 @@ REDIS_PORT: 6379
# REDIS_DB_CELERY: 3
# REDIS_DB_CACHE: 4
-# Use OpenID authorization
-# 使用OpenID 来进行认证设置
-# BASE_SITE_URL: http://localhost:8080
-# AUTH_OPENID: false # True or False
-# AUTH_OPENID_SERVER_URL: https://openid-auth-server.com/
-# AUTH_OPENID_REALM_NAME: realm-name
+# Use OpenID Authorization
+# 使用 OpenID 进行认证设置
+# AUTH_OPENID: False # True or False
# AUTH_OPENID_CLIENT_ID: client-id
# AUTH_OPENID_CLIENT_SECRET: client-secret
-# AUTH_OPENID_IGNORE_SSL_VERIFICATION: True
+# AUTH_OPENID_PROVIDER_ENDPOINT: https://op-example.com/
+# AUTH_OPENID_PROVIDER_AUTHORIZATION_ENDPOINT: https://op-example.com/authorize
+# AUTH_OPENID_PROVIDER_TOKEN_ENDPOINT: https://op-example.com/token
+# AUTH_OPENID_PROVIDER_JWKS_ENDPOINT: https://op-example.com/jwks
+# AUTH_OPENID_PROVIDER_USERINFO_ENDPOINT: https://op-example.com/userinfo
+# AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT: https://op-example.com/logout
+# AUTH_OPENID_PROVIDER_SIGNATURE_ALG: HS256
+# AUTH_OPENID_PROVIDER_SIGNATURE_KEY: None
+# AUTH_OPENID_SCOPES: "openid profile email"
+# AUTH_OPENID_ID_TOKEN_MAX_AGE: 60
+# AUTH_OPENID_ID_TOKEN_INCLUDE_CLAIMS: True
+# AUTH_OPENID_USE_STATE: True
+# AUTH_OPENID_USE_NONCE: True
# AUTH_OPENID_SHARE_SESSION: True
+# AUTH_OPENID_IGNORE_SSL_VERIFICATION: True
+# AUTH_OPENID_ALWAYS_UPDATE_USER: True
# Use Radius authorization
# 使用Radius来认证
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 06b420565..4eb34c22c 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -74,8 +74,6 @@ Werkzeug==0.15.3
drf-nested-routers==0.91
aliyun-python-sdk-core-v3==2.9.1
aliyun-python-sdk-ecs==4.10.1
-python-keycloak==0.13.3
-python-keycloak-client==0.1.3
rest_condition==1.0.3
python-ldap==3.1.0
tencentcloud-sdk-python==3.0.40
@@ -98,3 +96,4 @@ ipython
huaweicloud-sdk-python==1.0.21
django-redis==4.11.0
python-redis-lock==3.5.0
+jumpserver-django-oidc-rp==0.3.7.3