feat: 认证方式支持OAuth2.0协议 (#8686)

* feat: 认证方式支持OAuth2.0协议

* perf: 优化 OAuth2 认证逻辑和Logo (对接 Github)

* perf: 优化 OAuth2 认证逻辑和Logo,支持上传图标

* perf: 优化 OAuth2 认证逻辑和Logo,支持上传图标

* perf: 优化 OAuth2 认证逻辑和Logo,支持上传图标

* perf: 优化 OAuth2 认证逻辑和Logo,支持上传图标

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
pull/8698/head
jiangweidong 2022-08-04 14:40:33 +08:00 committed by GitHub
parent b22aed0cc3
commit 2099baaaff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 649 additions and 209 deletions

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
#
from .backends import *

View File

@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
#
import requests
from django.contrib.auth import get_user_model
from django.utils.http import urlencode
from django.conf import settings
from django.urls import reverse
from common.utils import get_logger
from users.utils import construct_user_email
from authentication.utils import build_absolute_uri
from common.exceptions import JMSException
from .signals import (
oauth2_create_or_update_user, oauth2_user_login_failed,
oauth2_user_login_success
)
from ..base import JMSModelBackend
__all__ = ['OAuth2Backend']
logger = get_logger(__name__)
class OAuth2Backend(JMSModelBackend):
@staticmethod
def is_enabled():
return settings.AUTH_OAUTH2
def get_or_create_user_from_userinfo(self, request, userinfo):
log_prompt = "Get or Create user [OAuth2Backend]: {}"
logger.debug(log_prompt.format('start'))
# Construct user attrs value
user_attrs = {}
for field, attr in settings.AUTH_OAUTH2_USER_ATTR_MAP.items():
user_attrs[field] = userinfo.get(attr, '')
username = user_attrs.get('username')
if not username:
error_msg = 'username is missing'
logger.error(log_prompt.format(error_msg))
raise JMSException(error_msg)
email = user_attrs.get('email', '')
email = construct_user_email(user_attrs.get('username'), email)
user_attrs.update({'email': email})
logger.debug(log_prompt.format(user_attrs))
user, created = get_user_model().objects.get_or_create(
username=username, defaults=user_attrs
)
logger.debug(log_prompt.format("user: {}|created: {}".format(user, created)))
logger.debug(log_prompt.format("Send signal => oauth2 create or update user"))
oauth2_create_or_update_user.send(
sender=self.__class__, request=request, user=user, created=created,
attrs=user_attrs
)
return user, created
@staticmethod
def get_response_data(response_data):
if response_data.get('data') is not None:
response_data = response_data['data']
return response_data
@staticmethod
def get_query_dict(response_data, query_dict):
query_dict.update({
'uid': response_data.get('uid', ''),
'access_token': response_data.get('access_token', '')
})
return query_dict
def authenticate(self, request, code=None, **kwargs):
log_prompt = "Process authenticate [OAuth2Backend]: {}"
logger.debug(log_prompt.format('Start'))
if code is None:
logger.error(log_prompt.format('code is missing'))
return None
query_dict = {
'client_id': settings.AUTH_OAUTH2_CLIENT_ID,
'client_secret': settings.AUTH_OAUTH2_CLIENT_SECRET,
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': build_absolute_uri(
request, path=reverse(settings.AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME)
)
}
access_token_url = '{url}?{query}'.format(
url=settings.AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT, query=urlencode(query_dict)
)
token_method = settings.AUTH_OAUTH2_ACCESS_TOKEN_METHOD.lower()
requests_func = getattr(requests, token_method, requests.get)
logger.debug(log_prompt.format('Call the access token endpoint[method: %s]' % token_method))
headers = {
'Accept': 'application/json'
}
access_token_response = requests_func(access_token_url, headers=headers)
try:
access_token_response.raise_for_status()
access_token_response_data = access_token_response.json()
response_data = self.get_response_data(access_token_response_data)
except Exception as e:
error = "Json access token response error, access token response " \
"content is: {}, error is: {}".format(access_token_response.content, str(e))
logger.error(log_prompt.format(error))
return None
query_dict = self.get_query_dict(response_data, query_dict)
headers = {
'Accept': 'application/json',
'Authorization': 'token {}'.format(response_data.get('access_token', ''))
}
logger.debug(log_prompt.format('Get userinfo endpoint'))
userinfo_url = '{url}?{query}'.format(
url=settings.AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT,
query=urlencode(query_dict)
)
userinfo_response = requests.get(userinfo_url, headers=headers)
try:
userinfo_response.raise_for_status()
userinfo_response_data = userinfo_response.json()
if 'data' in userinfo_response_data:
userinfo = userinfo_response_data['data']
else:
userinfo = userinfo_response_data
except Exception as e:
error = "Json userinfo response error, userinfo response " \
"content is: {}, error is: {}".format(userinfo_response.content, str(e))
logger.error(log_prompt.format(error))
return None
try:
logger.debug(log_prompt.format('Update or create oauth2 user'))
user, created = self.get_or_create_user_from_userinfo(request, userinfo)
except JMSException:
return None
if self.user_can_authenticate(user):
logger.debug(log_prompt.format('OAuth2 user login success'))
logger.debug(log_prompt.format('Send signal => oauth2 user login success'))
oauth2_user_login_success.send(sender=self.__class__, request=request, user=user)
return user
else:
logger.debug(log_prompt.format('OAuth2 user login failed'))
logger.debug(log_prompt.format('Send signal => oauth2 user login failed'))
oauth2_user_login_failed.send(
sender=self.__class__, request=request, username=user.username,
reason=_('User invalid, disabled or expired')
)
return None

View File

@ -0,0 +1,9 @@
from django.dispatch import Signal
oauth2_create_or_update_user = Signal(
providing_args=['request', 'user', 'created', 'name', 'username', 'email']
)
oauth2_user_login_success = Signal(providing_args=['request', 'user'])
oauth2_user_login_failed = Signal(providing_args=['request', 'username', 'reason'])

View File

@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
#
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.OAuth2AuthRequestView.as_view(), name='login'),
path('callback/', views.OAuth2AuthCallbackView.as_view(), name='login-callback')
]

View File

@ -0,0 +1,56 @@
from django.views import View
from django.conf import settings
from django.contrib.auth import login
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.http import urlencode
from authentication.utils import build_absolute_uri
from common.utils import get_logger
from authentication.mixins import authenticate
logger = get_logger(__file__)
class OAuth2AuthRequestView(View):
def get(self, request):
log_prompt = "Process OAuth2 GET requests: {}"
logger.debug(log_prompt.format('Start'))
base_url = settings.AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT
query_dict = {
'client_id': settings.AUTH_OAUTH2_CLIENT_ID, 'response_type': 'code',
'scope': settings.AUTH_OAUTH2_SCOPE,
'redirect_uri': build_absolute_uri(
request, path=reverse(settings.AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME)
)
}
redirect_url = '{url}?{query}'.format(url=base_url, query=urlencode(query_dict))
logger.debug(log_prompt.format('Redirect login url'))
return HttpResponseRedirect(redirect_url)
class OAuth2AuthCallbackView(View):
http_method_names = ['get', ]
def get(self, request):
""" Processes GET requests. """
log_prompt = "Process GET requests [OAuth2AuthCallbackView]: {}"
logger.debug(log_prompt.format('Start'))
callback_params = request.GET
if 'code' in callback_params:
logger.debug(log_prompt.format('Process authenticate'))
user = authenticate(code=callback_params['code'], request=request)
if user and user.is_valid:
logger.debug(log_prompt.format('Login: {}'.format(user)))
login(self.request, user)
logger.debug(log_prompt.format('Redirect'))
return HttpResponseRedirect(
settings.AUTH_OAUTH2_AUTHENTICATION_REDIRECT_URI
)
logger.debug(log_prompt.format('Redirect'))
return HttpResponseRedirect(settings.AUTH_OAUTH2_AUTHENTICATION_FAILURE_REDIRECT_URI)

View File

@ -9,6 +9,7 @@
import base64 import base64
import requests import requests
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend from django.contrib.auth.backends import ModelBackend
@ -18,10 +19,11 @@ from django.urls import reverse
from django.conf import settings from django.conf import settings
from common.utils import get_logger from common.utils import get_logger
from authentication.utils import build_absolute_uri_for_oidc
from users.utils import construct_user_email from users.utils import construct_user_email
from ..base import JMSBaseAuthBackend from ..base import JMSBaseAuthBackend
from .utils import validate_and_return_id_token, build_absolute_uri from .utils import validate_and_return_id_token
from .decorator import ssl_verification from .decorator import ssl_verification
from .signals import ( from .signals import (
openid_create_or_update_user, openid_user_login_failed, openid_user_login_success openid_create_or_update_user, openid_user_login_failed, openid_user_login_success
@ -127,7 +129,7 @@ class OIDCAuthCodeBackend(OIDCBaseBackend):
token_payload = { token_payload = {
'grant_type': 'authorization_code', 'grant_type': 'authorization_code',
'code': code, 'code': code,
'redirect_uri': build_absolute_uri( 'redirect_uri': build_absolute_uri_for_oidc(
request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME) request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME)
) )
} }

View File

@ -8,7 +8,7 @@
import datetime as dt import datetime as dt
from calendar import timegm from calendar import timegm
from urllib.parse import urlparse, urljoin from urllib.parse import urlparse
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
from django.utils.encoding import force_bytes, smart_bytes from django.utils.encoding import force_bytes, smart_bytes
@ -110,17 +110,3 @@ def _validate_claims(id_token, nonce=None, validate_nonce=True):
raise SuspiciousOperation('Incorrect id_token: nonce') raise SuspiciousOperation('Incorrect id_token: nonce')
logger.debug(log_prompt.format('End')) logger.debug(log_prompt.format('End'))
def build_absolute_uri(request, path=None):
"""
Build absolute redirect uri
"""
if path is None:
path = '/'
if settings.BASE_SITE_URL:
redirect_uri = urljoin(settings.BASE_SITE_URL, path)
else:
redirect_uri = request.build_absolute_uri(path)
return redirect_uri

View File

@ -20,7 +20,8 @@ from django.utils.crypto import get_random_string
from django.utils.http import is_safe_url, urlencode from django.utils.http import is_safe_url, urlencode
from django.views.generic import View from django.views.generic import View
from .utils import get_logger, build_absolute_uri from authentication.utils import build_absolute_uri_for_oidc
from .utils import get_logger
logger = get_logger(__file__) logger = get_logger(__file__)
@ -50,7 +51,7 @@ class OIDCAuthRequestView(View):
'scope': settings.AUTH_OPENID_SCOPES, 'scope': settings.AUTH_OPENID_SCOPES,
'response_type': 'code', 'response_type': 'code',
'client_id': settings.AUTH_OPENID_CLIENT_ID, 'client_id': settings.AUTH_OPENID_CLIENT_ID,
'redirect_uri': build_absolute_uri( 'redirect_uri': build_absolute_uri_for_oidc(
request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME) request, path=reverse(settings.AUTH_OPENID_AUTH_LOGIN_CALLBACK_URL_NAME)
) )
}) })
@ -216,7 +217,7 @@ class OIDCEndSessionView(View):
""" Returns the end-session URL. """ """ Returns the end-session URL. """
q = QueryDict(mutable=True) q = QueryDict(mutable=True)
q[settings.AUTH_OPENID_PROVIDER_END_SESSION_REDIRECT_URI_PARAMETER] = \ q[settings.AUTH_OPENID_PROVIDER_END_SESSION_REDIRECT_URI_PARAMETER] = \
build_absolute_uri(self.request, path=settings.LOGOUT_REDIRECT_URL or '/') build_absolute_uri_for_oidc(self.request, path=settings.LOGOUT_REDIRECT_URL or '/')
q[settings.AUTH_OPENID_PROVIDER_END_SESSION_ID_TOKEN_PARAMETER] = \ q[settings.AUTH_OPENID_PROVIDER_END_SESSION_ID_TOKEN_PARAMETER] = \
self.request.session['oidc_auth_id_token'] self.request.session['oidc_auth_id_token']
return '{}?{}'.format(settings.AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT, q.urlencode()) return '{}?{}'.format(settings.AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT, q.urlencode())

View File

@ -39,7 +39,7 @@ class SAML2Backend(JMSModelBackend):
return user, created return user, created
def authenticate(self, request, saml_user_data=None, **kwargs): def authenticate(self, request, saml_user_data=None, **kwargs):
log_prompt = "Process authenticate [SAML2AuthCodeBackend]: {}" log_prompt = "Process authenticate [SAML2Backend]: {}"
logger.debug(log_prompt.format('Start')) logger.debug(log_prompt.format('Start'))
if saml_user_data is None: if saml_user_data is None:
logger.error(log_prompt.format('saml_user_data is missing')) logger.error(log_prompt.format('saml_user_data is missing'))
@ -48,7 +48,7 @@ class SAML2Backend(JMSModelBackend):
logger.debug(log_prompt.format('saml data, {}'.format(saml_user_data))) logger.debug(log_prompt.format('saml data, {}'.format(saml_user_data)))
username = saml_user_data.get('username') username = saml_user_data.get('username')
if not username: if not username:
logger.debug(log_prompt.format('username is missing')) logger.warning(log_prompt.format('username is missing'))
return None return None
user, created = self.get_or_create_from_saml_data(request, **saml_user_data) user, created = self.get_or_create_from_saml_data(request, **saml_user_data)

View File

@ -12,6 +12,9 @@ from authentication.backends.oidc.signals import (
from authentication.backends.saml2.signals import ( from authentication.backends.saml2.signals import (
saml2_user_authenticated, saml2_user_authentication_failed saml2_user_authenticated, saml2_user_authentication_failed
) )
from authentication.backends.oauth2.signals import (
oauth2_user_login_failed, oauth2_user_login_success
)
from .signals import post_auth_success, post_auth_failed from .signals import post_auth_success, post_auth_failed
@ -67,3 +70,15 @@ def on_saml2_user_login_success(sender, request, user, **kwargs):
def on_saml2_user_login_failed(sender, request, username, reason, **kwargs): def on_saml2_user_login_failed(sender, request, username, reason, **kwargs):
request.session['auth_backend'] = settings.AUTH_BACKEND_SAML2 request.session['auth_backend'] = settings.AUTH_BACKEND_SAML2
post_auth_failed.send(sender, username=username, request=request, reason=reason) post_auth_failed.send(sender, username=username, request=request, reason=reason)
@receiver(oauth2_user_login_success)
def on_oauth2_user_login_success(sender, request, user, **kwargs):
request.session['auth_backend'] = settings.AUTH_BACKEND_OAUTH2
post_auth_success.send(sender, user=user, request=request)
@receiver(oauth2_user_login_failed)
def on_oauth2_user_login_failed(sender, username, request, reason, **kwargs):
request.session['auth_backend'] = settings.AUTH_BACKEND_OAUTH2
post_auth_failed.send(sender, username=username, request=request, reason=reason)

View File

@ -56,9 +56,11 @@ urlpatterns = [
path('profile/otp/disable/', users_view.UserOtpDisableView.as_view(), path('profile/otp/disable/', users_view.UserOtpDisableView.as_view(),
name='user-otp-disable'), name='user-otp-disable'),
# openid # other authentication protocol
path('cas/', include(('authentication.backends.cas.urls', 'authentication'), namespace='cas')), path('cas/', include(('authentication.backends.cas.urls', 'authentication'), namespace='cas')),
path('openid/', include(('authentication.backends.oidc.urls', 'authentication'), namespace='openid')), path('openid/', include(('authentication.backends.oidc.urls', 'authentication'), namespace='openid')),
path('saml2/', include(('authentication.backends.saml2.urls', 'authentication'), namespace='saml2')), path('saml2/', include(('authentication.backends.saml2.urls', 'authentication'), namespace='saml2')),
path('oauth2/', include(('authentication.backends.oauth2.urls', 'authentication'), namespace='oauth2')),
path('captcha/', include('captcha.urls')), path('captcha/', include('captcha.urls')),
] ]

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from urllib.parse import urljoin
from django.conf import settings from django.conf import settings
@ -29,3 +30,23 @@ def check_different_city_login_if_need(user, request):
if last_user_login and last_user_login.city != city: if last_user_login and last_user_login.city != city:
DifferentCityLoginMessage(user, ip, city).publish_async() DifferentCityLoginMessage(user, ip, city).publish_async()
def build_absolute_uri(request, path=None):
""" Build absolute redirect """
if path is None:
path = '/'
redirect_uri = request.build_absolute_uri(path)
return redirect_uri
def build_absolute_uri_for_oidc(request, path=None):
""" Build absolute redirect uri for OIDC """
if path is None:
path = '/'
if settings.BASE_SITE_URL:
# OIDC 专用配置项
redirect_uri = urljoin(settings.BASE_SITE_URL, path)
else:
redirect_uri = build_absolute_uri(request, path)
return redirect_uri

View File

@ -21,7 +21,7 @@ from django.conf import settings
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib.auth import BACKEND_SESSION_KEY from django.contrib.auth import BACKEND_SESSION_KEY
from common.utils import FlashMessageUtil from common.utils import FlashMessageUtil, static_or_direct
from users.utils import ( from users.utils import (
redirect_user_first_login_or_index redirect_user_first_login_or_index
) )
@ -39,8 +39,7 @@ class UserLoginContextMixin:
get_user_mfa_context: Callable get_user_mfa_context: Callable
request: HttpRequest request: HttpRequest
@staticmethod def get_support_auth_methods(self):
def get_support_auth_methods():
auth_methods = [ auth_methods = [
{ {
'name': 'OpenID', 'name': 'OpenID',
@ -63,6 +62,13 @@ class UserLoginContextMixin:
'logo': static('img/login_saml2_logo.png'), 'logo': static('img/login_saml2_logo.png'),
'auto_redirect': True 'auto_redirect': True
}, },
{
'name': settings.AUTH_OAUTH2_PROVIDER,
'enabled': settings.AUTH_OAUTH2,
'url': reverse('authentication:oauth2:login'),
'logo': static_or_direct(settings.AUTH_OAUTH2_LOGO_PATH),
'auto_redirect': True
},
{ {
'name': _('WeCom'), 'name': _('WeCom'),
'enabled': settings.AUTH_WECOM, 'enabled': settings.AUTH_WECOM,

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import re import re
from django.templatetags.static import static
from collections import OrderedDict from collections import OrderedDict
from itertools import chain from itertools import chain
import logging import logging
@ -365,3 +366,10 @@ def pretty_string(data: str, max_length=128, ellipsis_str='...'):
def group_by_count(it, count): def group_by_count(it, count):
return [it[i:i+count] for i in range(0, len(it), count)] return [it[i:i+count] for i in range(0, len(it), count)]
def static_or_direct(logo_path):
if logo_path.startswith('img/'):
return static(logo_path)
else:
return logo_path

View File

@ -265,6 +265,22 @@ class Config(dict):
'AUTH_SAML2_PROVIDER_AUTHORIZATION_ENDPOINT': '/', 'AUTH_SAML2_PROVIDER_AUTHORIZATION_ENDPOINT': '/',
'AUTH_SAML2_AUTHENTICATION_FAILURE_REDIRECT_URI': '/', 'AUTH_SAML2_AUTHENTICATION_FAILURE_REDIRECT_URI': '/',
# OAuth2 认证
'AUTH_OAUTH2': False,
'AUTH_OAUTH2_LOGO_PATH': 'img/login_oauth2_logo.png',
'AUTH_OAUTH2_PROVIDER': 'OAuth2',
'AUTH_OAUTH2_ALWAYS_UPDATE_USER': True,
'AUTH_OAUTH2_CLIENT_ID': 'client-id',
'AUTH_OAUTH2_SCOPE': '',
'AUTH_OAUTH2_CLIENT_SECRET': '',
'AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT': 'https://oauth2.example.com/authorize',
'AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT': 'https://oauth2.example.com/userinfo',
'AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT': 'https://oauth2.example.com/access_token',
'AUTH_OAUTH2_ACCESS_TOKEN_METHOD': 'GET',
'AUTH_OAUTH2_USER_ATTR_MAP': {
'name': 'name', 'username': 'username', 'email': 'email'
},
'AUTH_TEMP_TOKEN': False, 'AUTH_TEMP_TOKEN': False,
# 企业微信 # 企业微信

View File

@ -143,6 +143,23 @@ SAML2_SP_ADVANCED_SETTINGS = CONFIG.SAML2_SP_ADVANCED_SETTINGS
SAML2_LOGIN_URL_NAME = "authentication:saml2:saml2-login" SAML2_LOGIN_URL_NAME = "authentication:saml2:saml2-login"
SAML2_LOGOUT_URL_NAME = "authentication:saml2:saml2-logout" SAML2_LOGOUT_URL_NAME = "authentication:saml2:saml2-logout"
# OAuth2 auth
AUTH_OAUTH2 = CONFIG.AUTH_OAUTH2
AUTH_OAUTH2_LOGO_PATH = CONFIG.AUTH_OAUTH2_LOGO_PATH
AUTH_OAUTH2_PROVIDER = CONFIG.AUTH_OAUTH2_PROVIDER
AUTH_OAUTH2_ALWAYS_UPDATE_USER = CONFIG.AUTH_OAUTH2_ALWAYS_UPDATE_USER
AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT = CONFIG.AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT
AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT = CONFIG.AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT
AUTH_OAUTH2_ACCESS_TOKEN_METHOD = CONFIG.AUTH_OAUTH2_ACCESS_TOKEN_METHOD
AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT = CONFIG.AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT
AUTH_OAUTH2_CLIENT_SECRET = CONFIG.AUTH_OAUTH2_CLIENT_SECRET
AUTH_OAUTH2_CLIENT_ID = CONFIG.AUTH_OAUTH2_CLIENT_ID
AUTH_OAUTH2_SCOPE = CONFIG.AUTH_OAUTH2_SCOPE
AUTH_OAUTH2_USER_ATTR_MAP = CONFIG.AUTH_OAUTH2_USER_ATTR_MAP
AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:oauth2:login-callback'
AUTH_OAUTH2_AUTHENTICATION_REDIRECT_URI = '/'
AUTH_OAUTH2_AUTHENTICATION_FAILURE_REDIRECT_URI = '/'
# 临时 token # 临时 token
AUTH_TEMP_TOKEN = CONFIG.AUTH_TEMP_TOKEN AUTH_TEMP_TOKEN = CONFIG.AUTH_TEMP_TOKEN
@ -170,6 +187,7 @@ AUTH_BACKEND_DINGTALK = 'authentication.backends.sso.DingTalkAuthentication'
AUTH_BACKEND_FEISHU = 'authentication.backends.sso.FeiShuAuthentication' AUTH_BACKEND_FEISHU = 'authentication.backends.sso.FeiShuAuthentication'
AUTH_BACKEND_AUTH_TOKEN = 'authentication.backends.sso.AuthorizationTokenAuthentication' AUTH_BACKEND_AUTH_TOKEN = 'authentication.backends.sso.AuthorizationTokenAuthentication'
AUTH_BACKEND_SAML2 = 'authentication.backends.saml2.SAML2Backend' AUTH_BACKEND_SAML2 = 'authentication.backends.saml2.SAML2Backend'
AUTH_BACKEND_OAUTH2 = 'authentication.backends.oauth2.OAuth2Backend'
AUTH_BACKEND_TEMP_TOKEN = 'authentication.backends.token.TempTokenAuthBackend' AUTH_BACKEND_TEMP_TOKEN = 'authentication.backends.token.TempTokenAuthBackend'
@ -180,6 +198,7 @@ AUTHENTICATION_BACKENDS = [
AUTH_BACKEND_MODEL, AUTH_BACKEND_PUBKEY, AUTH_BACKEND_LDAP, AUTH_BACKEND_RADIUS, AUTH_BACKEND_MODEL, AUTH_BACKEND_PUBKEY, AUTH_BACKEND_LDAP, AUTH_BACKEND_RADIUS,
# 跳转形式 # 跳转形式
AUTH_BACKEND_CAS, AUTH_BACKEND_OIDC_PASSWORD, AUTH_BACKEND_OIDC_CODE, AUTH_BACKEND_SAML2, AUTH_BACKEND_CAS, AUTH_BACKEND_OIDC_PASSWORD, AUTH_BACKEND_OIDC_CODE, AUTH_BACKEND_SAML2,
AUTH_BACKEND_OAUTH2,
# 扫码模式 # 扫码模式
AUTH_BACKEND_WECOM, AUTH_BACKEND_DINGTALK, AUTH_BACKEND_FEISHU, AUTH_BACKEND_WECOM, AUTH_BACKEND_DINGTALK, AUTH_BACKEND_FEISHU,
# Token模式 # Token模式

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:322701b975fe90b4b187c4a99ddd1837291150502c82accf0a4c6e32dddf91be oid sha256:73ea6289c22c329752330fae1fef6d174573c7f46355137ffbc864407b2b8270
size 128721 size 129073

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-08-02 11:39+0800\n" "POT-Creation-Date: 2022-08-04 14:17+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -28,11 +28,11 @@ msgstr "Acls"
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23 #: assets/models/cmd_filter.py:27 assets/models/domain.py:23
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24 #: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29 #: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
#: settings/models.py:29 settings/serializers/sms.py:6 #: settings/models.py:33 settings/serializers/sms.py:6
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86 #: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86
#: terminal/models/storage.py:26 terminal/models/task.py:16 #: terminal/models/storage.py:26 terminal/models/task.py:16
#: terminal/models/terminal.py:100 users/forms/profile.py:33 #: terminal/models/terminal.py:100 users/forms/profile.py:33
#: users/models/group.py:15 users/models/user.py:661 #: users/models/group.py:15 users/models/user.py:665
#: xpack/plugins/cloud/models.py:28 #: xpack/plugins/cloud/models.py:28
msgid "Name" msgid "Name"
msgstr "名前" msgstr "名前"
@ -60,11 +60,11 @@ msgstr "アクティブ"
#: assets/models/cmd_filter.py:96 assets/models/domain.py:24 #: assets/models/cmd_filter.py:96 assets/models/domain.py:24
#: assets/models/domain.py:65 assets/models/group.py:23 #: assets/models/domain.py:65 assets/models/group.py:23
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73 #: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:34 #: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:38
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96 #: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96
#: terminal/models/storage.py:29 terminal/models/terminal.py:114 #: terminal/models/storage.py:29 terminal/models/terminal.py:114
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288 #: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
#: users/models/group.py:16 users/models/user.py:698 #: users/models/group.py:16 users/models/user.py:702
#: xpack/plugins/change_auth_plan/models/base.py:44 #: xpack/plugins/change_auth_plan/models/base.py:44
#: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:116 #: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:116
#: xpack/plugins/gathered_user/models.py:26 #: xpack/plugins/gathered_user/models.py:26
@ -94,7 +94,7 @@ msgstr "ログイン確認"
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:44 #: terminal/backends/command/serializers.py:13 terminal/models/session.py:44
#: terminal/models/sharing.py:33 terminal/notifications.py:91 #: terminal/models/sharing.py:33 terminal/notifications.py:91
#: terminal/notifications.py:139 tickets/models/comment.py:21 users/const.py:14 #: terminal/notifications.py:139 tickets/models/comment.py:21 users/const.py:14
#: users/models/user.py:890 users/models/user.py:921 #: users/models/user.py:894 users/models/user.py:925
#: users/serializers/group.py:19 #: users/serializers/group.py:19
msgid "User" msgid "User"
msgstr "ユーザー" msgstr "ユーザー"
@ -160,7 +160,7 @@ msgstr "コンマ区切り文字列の形式。* はすべて一致すること
#: authentication/models.py:260 #: authentication/models.py:260
#: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_different_city.html:9
#: authentication/templates/authentication/_msg_oauth_bind.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9
#: ops/models/adhoc.py:159 users/forms/profile.py:32 users/models/user.py:659 #: ops/models/adhoc.py:159 users/forms/profile.py:32 users/models/user.py:663
#: users/templates/users/_msg_user_created.html:12 #: users/templates/users/_msg_user_created.html:12
#: xpack/plugins/change_auth_plan/models/asset.py:34 #: xpack/plugins/change_auth_plan/models/asset.py:34
#: xpack/plugins/change_auth_plan/models/asset.py:195 #: xpack/plugins/change_auth_plan/models/asset.py:195
@ -364,7 +364,7 @@ msgstr "タイプ表示"
#: assets/serializers/cmd_filter.py:48 common/db/models.py:114 #: assets/serializers/cmd_filter.py:48 common/db/models.py:114
#: common/mixins/models.py:50 ops/models/adhoc.py:39 ops/models/command.py:30 #: common/mixins/models.py:50 ops/models/adhoc.py:39 ops/models/command.py:30
#: orgs/models.py:72 orgs/models.py:223 perms/models/base.py:92 #: orgs/models.py:72 orgs/models.py:223 perms/models/base.py:92
#: users/models/group.py:18 users/models/user.py:922 #: users/models/group.py:18 users/models/user.py:926
#: xpack/plugins/cloud/models.py:125 #: xpack/plugins/cloud/models.py:125
msgid "Date created" msgid "Date created"
msgstr "作成された日付" msgstr "作成された日付"
@ -628,7 +628,7 @@ msgstr "ラベル"
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:52 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:52
#: assets/models/cmd_filter.py:99 assets/models/group.py:21 #: assets/models/cmd_filter.py:99 assets/models/group.py:21
#: common/db/models.py:112 common/mixins/models.py:49 orgs/models.py:71 #: common/db/models.py:112 common/mixins/models.py:49 orgs/models.py:71
#: orgs/models.py:225 perms/models/base.py:91 users/models/user.py:706 #: orgs/models.py:225 perms/models/base.py:91 users/models/user.py:710
#: users/serializers/group.py:33 #: users/serializers/group.py:33
#: xpack/plugins/change_auth_plan/models/base.py:48 #: xpack/plugins/change_auth_plan/models/base.py:48
#: xpack/plugins/cloud/models.py:122 xpack/plugins/gathered_user/models.py:30 #: xpack/plugins/cloud/models.py:122 xpack/plugins/gathered_user/models.py:30
@ -824,7 +824,7 @@ msgstr "帯域幅"
msgid "Contact" msgid "Contact"
msgstr "連絡先" msgstr "連絡先"
#: assets/models/cluster.py:22 users/models/user.py:681 #: assets/models/cluster.py:22 users/models/user.py:685
msgid "Phone" msgid "Phone"
msgstr "電話" msgstr "電話"
@ -850,7 +850,7 @@ msgid "Default"
msgstr "デフォルト" msgstr "デフォルト"
#: assets/models/cluster.py:36 assets/models/label.py:14 rbac/const.py:6 #: assets/models/cluster.py:36 assets/models/label.py:14 rbac/const.py:6
#: users/models/user.py:907 #: users/models/user.py:911
msgid "System" msgid "System"
msgstr "システム" msgstr "システム"
@ -859,7 +859,7 @@ msgid "Default Cluster"
msgstr "デフォルトクラスター" msgstr "デフォルトクラスター"
#: assets/models/cmd_filter.py:34 perms/models/base.py:86 #: assets/models/cmd_filter.py:34 perms/models/base.py:86
#: users/models/group.py:31 users/models/user.py:667 #: users/models/group.py:31 users/models/user.py:671
msgid "User group" msgid "User group"
msgstr "ユーザーグループ" msgstr "ユーザーグループ"
@ -960,7 +960,7 @@ msgstr "資産グループ"
msgid "Default asset group" msgid "Default asset group"
msgstr "デフォルトアセットグループ" msgstr "デフォルトアセットグループ"
#: assets/models/label.py:19 assets/models/node.py:546 settings/models.py:30 #: assets/models/label.py:19 assets/models/node.py:546 settings/models.py:34
msgid "Value" msgid "Value"
msgstr "値" msgstr "値"
@ -1157,7 +1157,7 @@ msgstr "定期的なパフォーマンス"
msgid "Currently only mail sending is supported" msgid "Currently only mail sending is supported"
msgstr "現在、メール送信のみがサポートされています" msgstr "現在、メール送信のみがサポートされています"
#: assets/serializers/base.py:16 users/models/user.py:689 #: assets/serializers/base.py:16 users/models/user.py:693
msgid "Private key" msgid "Private key"
msgstr "ssh秘密鍵" msgstr "ssh秘密鍵"
@ -1507,7 +1507,7 @@ msgstr "パスワード変更ログ"
msgid "Disabled" msgid "Disabled"
msgstr "無効" msgstr "無効"
#: audits/models.py:112 settings/models.py:33 #: audits/models.py:112 settings/models.py:37
msgid "Enabled" msgid "Enabled"
msgstr "有効化" msgstr "有効化"
@ -1535,7 +1535,7 @@ msgstr "ユーザーエージェント"
#: audits/models.py:126 #: audits/models.py:126
#: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms/profile.py:65 users/models/user.py:684 #: users/forms/profile.py:65 users/models/user.py:688
#: users/serializers/profile.py:126 #: users/serializers/profile.py:126
msgid "MFA" msgid "MFA"
msgstr "MFA" msgstr "MFA"
@ -1613,20 +1613,20 @@ msgid "Auth Token"
msgstr "認証トークン" msgstr "認証トークン"
#: audits/signal_handlers.py:53 authentication/notifications.py:73 #: audits/signal_handlers.py:53 authentication/notifications.py:73
#: authentication/views/login.py:67 authentication/views/wecom.py:178 #: authentication/views/login.py:73 authentication/views/wecom.py:178
#: notifications/backends/__init__.py:11 users/models/user.py:720 #: notifications/backends/__init__.py:11 users/models/user.py:724
msgid "WeCom" msgid "WeCom"
msgstr "企業微信" msgstr "企業微信"
#: audits/signal_handlers.py:54 authentication/views/feishu.py:144 #: audits/signal_handlers.py:54 authentication/views/feishu.py:144
#: authentication/views/login.py:79 notifications/backends/__init__.py:14 #: authentication/views/login.py:85 notifications/backends/__init__.py:14
#: users/models/user.py:722 #: users/models/user.py:726
msgid "FeiShu" msgid "FeiShu"
msgstr "本を飛ばす" msgstr "本を飛ばす"
#: audits/signal_handlers.py:55 authentication/views/dingtalk.py:179 #: audits/signal_handlers.py:55 authentication/views/dingtalk.py:179
#: authentication/views/login.py:73 notifications/backends/__init__.py:12 #: authentication/views/login.py:79 notifications/backends/__init__.py:12
#: users/models/user.py:721 #: users/models/user.py:725
msgid "DingTalk" msgid "DingTalk"
msgstr "DingTalk" msgstr "DingTalk"
@ -1867,6 +1867,10 @@ msgstr ""
msgid "Invalid token or cache refreshed." msgid "Invalid token or cache refreshed."
msgstr "無効なトークンまたはキャッシュの更新。" msgstr "無効なトークンまたはキャッシュの更新。"
#: authentication/backends/oauth2/backends.py:155 authentication/models.py:158
msgid "User invalid, disabled or expired"
msgstr "ユーザーが無効、無効、または期限切れです"
#: authentication/confirm/password.py:16 #: authentication/confirm/password.py:16
msgid "Authentication failed password incorrect" msgid "Authentication failed password incorrect"
msgstr "認証に失敗しました (ユーザー名またはパスワードが正しくありません)" msgstr "認証に失敗しました (ユーザー名またはパスワードが正しくありません)"
@ -2142,7 +2146,7 @@ msgstr "ひみつ"
#: authentication/models.py:74 authentication/models.py:264 #: authentication/models.py:74 authentication/models.py:264
#: perms/models/base.py:90 tickets/models/ticket/apply_application.py:30 #: perms/models/base.py:90 tickets/models/ticket/apply_application.py:30
#: tickets/models/ticket/apply_asset.py:24 users/models/user.py:703 #: tickets/models/ticket/apply_asset.py:24 users/models/user.py:707
msgid "Date expired" msgid "Date expired"
msgstr "期限切れの日付" msgstr "期限切れの日付"
@ -2166,10 +2170,6 @@ msgstr "接続トークンの有効期限: {}"
msgid "User not exists" msgid "User not exists"
msgstr "ユーザーは存在しません" msgstr "ユーザーは存在しません"
#: authentication/models.py:158
msgid "User invalid, disabled or expired"
msgstr "ユーザーが無効、無効、または期限切れです"
#: authentication/models.py:163 #: authentication/models.py:163
msgid "System user not exists" msgid "System user not exists"
msgstr "システムユーザーが存在しません" msgstr "システムユーザーが存在しません"
@ -2317,7 +2317,7 @@ msgstr "コードエラー"
#: authentication/templates/authentication/_msg_reset_password.html:3 #: authentication/templates/authentication/_msg_reset_password.html:3
#: authentication/templates/authentication/_msg_rest_password_success.html:2 #: authentication/templates/authentication/_msg_rest_password_success.html:2
#: authentication/templates/authentication/_msg_rest_public_key_success.html:2 #: authentication/templates/authentication/_msg_rest_public_key_success.html:2
#: jumpserver/conf.py:307 ops/tasks.py:145 ops/tasks.py:148 #: jumpserver/conf.py:323 ops/tasks.py:145 ops/tasks.py:148
#: perms/templates/perms/_msg_item_permissions_expire.html:3 #: perms/templates/perms/_msg_item_permissions_expire.html:3
#: perms/templates/perms/_msg_permed_items_expire.html:3 #: perms/templates/perms/_msg_permed_items_expire.html:3
#: tickets/templates/tickets/approve_check_password.html:33 #: tickets/templates/tickets/approve_check_password.html:33
@ -2530,19 +2530,19 @@ msgstr "本を飛ばすからユーザーを取得できませんでした"
msgid "Please login with a password and then bind the FeiShu" msgid "Please login with a password and then bind the FeiShu"
msgstr "パスワードでログインしてから本を飛ばすをバインドしてください" msgstr "パスワードでログインしてから本を飛ばすをバインドしてください"
#: authentication/views/login.py:175 #: authentication/views/login.py:181
msgid "Redirecting" msgid "Redirecting"
msgstr "リダイレクト" msgstr "リダイレクト"
#: authentication/views/login.py:176 #: authentication/views/login.py:182
msgid "Redirecting to {} authentication" msgid "Redirecting to {} authentication"
msgstr "{} 認証へのリダイレクト" msgstr "{} 認証へのリダイレクト"
#: authentication/views/login.py:199 #: authentication/views/login.py:205
msgid "Please enable cookies and try again." msgid "Please enable cookies and try again."
msgstr "クッキーを有効にして、もう一度お試しください。" msgstr "クッキーを有効にして、もう一度お試しください。"
#: authentication/views/login.py:301 #: authentication/views/login.py:307
msgid "" msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n" "Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page" " Don't close this page"
@ -2550,15 +2550,15 @@ msgstr ""
"<b>{}</b> 確認を待ちます。彼女/彼へのリンクをコピーすることもできます <br/>\n" "<b>{}</b> 確認を待ちます。彼女/彼へのリンクをコピーすることもできます <br/>\n"
" このページを閉じないでください" " このページを閉じないでください"
#: authentication/views/login.py:306 #: authentication/views/login.py:312
msgid "No ticket found" msgid "No ticket found"
msgstr "チケットが見つかりません" msgstr "チケットが見つかりません"
#: authentication/views/login.py:340 #: authentication/views/login.py:346
msgid "Logout success" msgid "Logout success"
msgstr "ログアウト成功" msgstr "ログアウト成功"
#: authentication/views/login.py:341 #: authentication/views/login.py:347
msgid "Logout success, return login page" msgid "Logout success, return login page"
msgstr "ログアウト成功、ログインページを返す" msgstr "ログアウト成功、ログインページを返す"
@ -2762,11 +2762,11 @@ msgstr "特殊文字を含むべきではない"
msgid "The mobile phone number format is incorrect" msgid "The mobile phone number format is incorrect"
msgstr "携帯電話番号の形式が正しくありません" msgstr "携帯電話番号の形式が正しくありません"
#: jumpserver/conf.py:306 #: jumpserver/conf.py:322
msgid "Create account successfully" msgid "Create account successfully"
msgstr "アカウントを正常に作成" msgstr "アカウントを正常に作成"
#: jumpserver/conf.py:308 #: jumpserver/conf.py:324
msgid "Your account has been created successfully" msgid "Your account has been created successfully"
msgstr "アカウントが正常に作成されました" msgstr "アカウントが正常に作成されました"
@ -2811,7 +2811,7 @@ msgid "Notifications"
msgstr "通知" msgstr "通知"
#: notifications/backends/__init__.py:10 users/forms/profile.py:102 #: notifications/backends/__init__.py:10 users/forms/profile.py:102
#: users/models/user.py:663 #: users/models/user.py:667
msgid "Email" msgid "Email"
msgstr "メール" msgstr "メール"
@ -3045,7 +3045,7 @@ msgid "Can view all joined org"
msgstr "参加しているすべての組織を表示できます" msgstr "参加しているすべての組織を表示できます"
#: orgs/models.py:222 rbac/models/role.py:46 rbac/models/rolebinding.py:44 #: orgs/models.py:222 rbac/models/role.py:46 rbac/models/rolebinding.py:44
#: users/models/user.py:671 #: users/models/user.py:675
msgid "Role" msgid "Role"
msgstr "ロール" msgstr "ロール"
@ -3323,6 +3323,7 @@ msgid "Permission"
msgstr "権限" msgstr "権限"
#: rbac/models/role.py:31 rbac/models/rolebinding.py:38 #: rbac/models/role.py:31 rbac/models/rolebinding.py:38
#: settings/serializers/auth/oauth2.py:35
msgid "Scope" msgid "Scope"
msgstr "スコープ" msgstr "スコープ"
@ -3401,7 +3402,7 @@ msgstr "ワークスペースビュー"
msgid "Audit view" msgid "Audit view"
msgstr "監査ビュー" msgstr "監査ビュー"
#: rbac/tree.py:28 settings/models.py:140 #: rbac/tree.py:28 settings/models.py:156
msgid "System setting" msgid "System setting"
msgstr "システム設定" msgstr "システム設定"
@ -3499,43 +3500,43 @@ msgstr "{} 人のユーザーを正常にインポートしました (組織: {}
msgid "Settings" msgid "Settings"
msgstr "設定" msgstr "設定"
#: settings/models.py:142 #: settings/models.py:158
msgid "Can change email setting" msgid "Can change email setting"
msgstr "メール設定を変更できます" msgstr "メール設定を変更できます"
#: settings/models.py:143 #: settings/models.py:159
msgid "Can change auth setting" msgid "Can change auth setting"
msgstr "資格認定の設定" msgstr "資格認定の設定"
#: settings/models.py:144 #: settings/models.py:160
msgid "Can change system msg sub setting" msgid "Can change system msg sub setting"
msgstr "システムmsgサブ设定を変更できます" msgstr "システムmsgサブ设定を変更できます"
#: settings/models.py:145 #: settings/models.py:161
msgid "Can change sms setting" msgid "Can change sms setting"
msgstr "Smsの設定を変えることができます" msgstr "Smsの設定を変えることができます"
#: settings/models.py:146 #: settings/models.py:162
msgid "Can change security setting" msgid "Can change security setting"
msgstr "セキュリティ設定を変更できます" msgstr "セキュリティ設定を変更できます"
#: settings/models.py:147 #: settings/models.py:163
msgid "Can change clean setting" msgid "Can change clean setting"
msgstr "きれいな設定を変えることができます" msgstr "きれいな設定を変えることができます"
#: settings/models.py:148 #: settings/models.py:164
msgid "Can change interface setting" msgid "Can change interface setting"
msgstr "インターフェイスの設定を変えることができます" msgstr "インターフェイスの設定を変えることができます"
#: settings/models.py:149 #: settings/models.py:165
msgid "Can change license setting" msgid "Can change license setting"
msgstr "ライセンス設定を変更できます" msgstr "ライセンス設定を変更できます"
#: settings/models.py:150 #: settings/models.py:166
msgid "Can change terminal setting" msgid "Can change terminal setting"
msgstr "ターミナルの設定を変えることができます" msgstr "ターミナルの設定を変えることができます"
#: settings/models.py:151 #: settings/models.py:167
msgid "Can change other setting" msgid "Can change other setting"
msgstr "他の設定を変えることができます" msgstr "他の設定を変えることができます"
@ -3648,7 +3649,8 @@ msgstr "ユーザー検索フィルター"
msgid "Choice may be (cn|uid|sAMAccountName)=%(user)s)" msgid "Choice may be (cn|uid|sAMAccountName)=%(user)s)"
msgstr "選択は (cnまたはuidまたはsAMAccountName)=%(user)s)" msgstr "選択は (cnまたはuidまたはsAMAccountName)=%(user)s)"
#: settings/serializers/auth/ldap.py:57 settings/serializers/auth/oidc.py:36 #: settings/serializers/auth/ldap.py:57 settings/serializers/auth/oauth2.py:51
#: settings/serializers/auth/oidc.py:36
msgid "User attr map" msgid "User attr map"
msgstr "ユーザー属性マッピング" msgstr "ユーザー属性マッピング"
@ -3672,23 +3674,52 @@ msgstr "ページサイズを検索"
msgid "Enable LDAP auth" msgid "Enable LDAP auth"
msgstr "LDAP認証の有効化" msgstr "LDAP認証の有効化"
#: settings/serializers/auth/oidc.py:15 #: settings/serializers/auth/oauth2.py:20
msgid "Base site url" msgid "Enable OAuth2 Auth"
msgstr "ベースサイトのアドレス" msgstr "OAuth2認証の有効化"
#: settings/serializers/auth/oidc.py:18 #: settings/serializers/auth/oauth2.py:23
msgid "Logo"
msgstr "アイコン"
#: settings/serializers/auth/oauth2.py:26
msgid "Service provider"
msgstr "サービスプロバイダー"
#: settings/serializers/auth/oauth2.py:29 settings/serializers/auth/oidc.py:18
msgid "Client Id" msgid "Client Id"
msgstr "クライアントID" msgstr "クライアントID"
#: settings/serializers/auth/oidc.py:21 #: settings/serializers/auth/oauth2.py:32 settings/serializers/auth/oidc.py:21
#: xpack/plugins/cloud/serializers/account_attrs.py:36 #: xpack/plugins/cloud/serializers/account_attrs.py:36
msgid "Client Secret" msgid "Client Secret"
msgstr "クライアント秘密" msgstr "クライアント秘密"
#: settings/serializers/auth/oidc.py:29 #: settings/serializers/auth/oauth2.py:38 settings/serializers/auth/oidc.py:62
msgid "Provider auth endpoint"
msgstr "認証エンドポイントアドレス"
#: settings/serializers/auth/oauth2.py:41 settings/serializers/auth/oidc.py:65
msgid "Provider token endpoint"
msgstr "プロバイダートークンエンドポイント"
#: settings/serializers/auth/oauth2.py:44 settings/serializers/auth/oidc.py:29
msgid "Client authentication method" msgid "Client authentication method"
msgstr "クライアント認証方式" msgstr "クライアント認証方式"
#: settings/serializers/auth/oauth2.py:48 settings/serializers/auth/oidc.py:71
msgid "Provider userinfo endpoint"
msgstr "プロバイダーuserinfoエンドポイント"
#: settings/serializers/auth/oauth2.py:54 settings/serializers/auth/oidc.py:92
#: settings/serializers/auth/saml2.py:33
msgid "Always update user"
msgstr "常にユーザーを更新"
#: settings/serializers/auth/oidc.py:15
msgid "Base site url"
msgstr "ベースサイトのアドレス"
#: settings/serializers/auth/oidc.py:31 #: settings/serializers/auth/oidc.py:31
msgid "Share session" msgid "Share session"
msgstr "セッションの共有" msgstr "セッションの共有"
@ -3721,22 +3752,10 @@ msgstr "OIDC認証の有効化"
msgid "Provider endpoint" msgid "Provider endpoint"
msgstr "プロバイダーエンドポイント" msgstr "プロバイダーエンドポイント"
#: settings/serializers/auth/oidc.py:62
msgid "Provider auth endpoint"
msgstr "認証エンドポイントアドレス"
#: settings/serializers/auth/oidc.py:65
msgid "Provider token endpoint"
msgstr "プロバイダートークンエンドポイント"
#: settings/serializers/auth/oidc.py:68 #: settings/serializers/auth/oidc.py:68
msgid "Provider jwks endpoint" msgid "Provider jwks endpoint"
msgstr "プロバイダーjwksエンドポイント" msgstr "プロバイダーjwksエンドポイント"
#: settings/serializers/auth/oidc.py:71
msgid "Provider userinfo endpoint"
msgstr "プロバイダーuserinfoエンドポイント"
#: settings/serializers/auth/oidc.py:74 #: settings/serializers/auth/oidc.py:74
msgid "Provider end session endpoint" msgid "Provider end session endpoint"
msgstr "プロバイダーのセッション終了エンドポイント" msgstr "プロバイダーのセッション終了エンドポイント"
@ -3769,10 +3788,6 @@ msgstr "使用状態"
msgid "Use nonce" msgid "Use nonce"
msgstr "Nonceを使用" msgstr "Nonceを使用"
#: settings/serializers/auth/oidc.py:92 settings/serializers/auth/saml2.py:33
msgid "Always update user"
msgstr "常にユーザーを更新"
#: settings/serializers/auth/radius.py:13 #: settings/serializers/auth/radius.py:13
msgid "Enable Radius Auth" msgid "Enable Radius Auth"
msgstr "Radius認証の有効化" msgstr "Radius認証の有効化"
@ -5686,7 +5701,7 @@ msgstr "公開鍵は古いものと同じであってはなりません。"
msgid "Not a valid ssh public key" msgid "Not a valid ssh public key"
msgstr "有効なssh公開鍵ではありません" msgstr "有効なssh公開鍵ではありません"
#: users/forms/profile.py:161 users/models/user.py:692 #: users/forms/profile.py:161 users/models/user.py:696
msgid "Public key" msgid "Public key"
msgstr "公開キー" msgstr "公開キー"
@ -5698,55 +5713,55 @@ msgstr "強制有効"
msgid "Local" msgid "Local"
msgstr "ローカル" msgstr "ローカル"
#: users/models/user.py:673 users/serializers/user.py:149 #: users/models/user.py:677 users/serializers/user.py:149
msgid "Is service account" msgid "Is service account"
msgstr "サービスアカウントです" msgstr "サービスアカウントです"
#: users/models/user.py:675 #: users/models/user.py:679
msgid "Avatar" msgid "Avatar"
msgstr "アバター" msgstr "アバター"
#: users/models/user.py:678 #: users/models/user.py:682
msgid "Wechat" msgid "Wechat"
msgstr "微信" msgstr "微信"
#: users/models/user.py:695 #: users/models/user.py:699
msgid "Secret key" msgid "Secret key"
msgstr "秘密キー" msgstr "秘密キー"
#: users/models/user.py:711 #: users/models/user.py:715
msgid "Source" msgid "Source"
msgstr "ソース" msgstr "ソース"
#: users/models/user.py:715 #: users/models/user.py:719
msgid "Date password last updated" msgid "Date password last updated"
msgstr "最終更新日パスワード" msgstr "最終更新日パスワード"
#: users/models/user.py:718 #: users/models/user.py:722
msgid "Need update password" msgid "Need update password"
msgstr "更新パスワードが必要" msgstr "更新パスワードが必要"
#: users/models/user.py:892 #: users/models/user.py:896
msgid "Can invite user" msgid "Can invite user"
msgstr "ユーザーを招待できます" msgstr "ユーザーを招待できます"
#: users/models/user.py:893 #: users/models/user.py:897
msgid "Can remove user" msgid "Can remove user"
msgstr "ユーザーを削除できます" msgstr "ユーザーを削除できます"
#: users/models/user.py:894 #: users/models/user.py:898
msgid "Can match user" msgid "Can match user"
msgstr "ユーザーに一致できます" msgstr "ユーザーに一致できます"
#: users/models/user.py:903 #: users/models/user.py:907
msgid "Administrator" msgid "Administrator"
msgstr "管理者" msgstr "管理者"
#: users/models/user.py:906 #: users/models/user.py:910
msgid "Administrator is the super user of system" msgid "Administrator is the super user of system"
msgstr "管理者はシステムのスーパーユーザーです" msgstr "管理者はシステムのスーパーユーザーです"
#: users/models/user.py:931 #: users/models/user.py:935
msgid "User password history" msgid "User password history"
msgstr "ユーザーパスワード履歴" msgstr "ユーザーパスワード履歴"
@ -6859,6 +6874,9 @@ msgstr "究極のエディション"
msgid "Community edition" msgid "Community edition"
msgstr "コミュニティ版" msgstr "コミュニティ版"
#~ msgid "Logo title"
#~ msgstr "アイコンタイトル"
#~ msgid "IP is not allowed" #~ msgid "IP is not allowed"
#~ msgstr "IPは許可されていません" #~ msgstr "IPは許可されていません"

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:9ed12e275e241284573d49c752cf01bafddb912dfe38ae2888a62e62cdb30ebd oid sha256:4e8c2c0a8a9b7d9de0bd11c1fba8073bbe44fe7274ff6bb3537d5fa19b083baa
size 106084 size 106370

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-08-02 11:39+0800\n" "POT-Creation-Date: 2022-08-04 14:17+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -27,11 +27,11 @@ msgstr "访问控制"
#: assets/models/cmd_filter.py:27 assets/models/domain.py:23 #: assets/models/cmd_filter.py:27 assets/models/domain.py:23
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24 #: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29 #: orgs/models.py:70 perms/models/base.py:83 rbac/models/role.py:29
#: settings/models.py:29 settings/serializers/sms.py:6 #: settings/models.py:33 settings/serializers/sms.py:6
#: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86 #: terminal/models/endpoint.py:10 terminal/models/endpoint.py:86
#: terminal/models/storage.py:26 terminal/models/task.py:16 #: terminal/models/storage.py:26 terminal/models/task.py:16
#: terminal/models/terminal.py:100 users/forms/profile.py:33 #: terminal/models/terminal.py:100 users/forms/profile.py:33
#: users/models/group.py:15 users/models/user.py:661 #: users/models/group.py:15 users/models/user.py:665
#: xpack/plugins/cloud/models.py:28 #: xpack/plugins/cloud/models.py:28
msgid "Name" msgid "Name"
msgstr "名称" msgstr "名称"
@ -59,11 +59,11 @@ msgstr "激活中"
#: assets/models/cmd_filter.py:96 assets/models/domain.py:24 #: assets/models/cmd_filter.py:96 assets/models/domain.py:24
#: assets/models/domain.py:65 assets/models/group.py:23 #: assets/models/domain.py:65 assets/models/group.py:23
#: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73 #: assets/models/label.py:23 ops/models/adhoc.py:38 orgs/models.py:73
#: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:34 #: perms/models/base.py:93 rbac/models/role.py:37 settings/models.py:38
#: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96 #: terminal/models/endpoint.py:23 terminal/models/endpoint.py:96
#: terminal/models/storage.py:29 terminal/models/terminal.py:114 #: terminal/models/storage.py:29 terminal/models/terminal.py:114
#: tickets/models/comment.py:32 tickets/models/ticket/general.py:288 #: tickets/models/comment.py:32 tickets/models/ticket/general.py:288
#: users/models/group.py:16 users/models/user.py:698 #: users/models/group.py:16 users/models/user.py:702
#: xpack/plugins/change_auth_plan/models/base.py:44 #: xpack/plugins/change_auth_plan/models/base.py:44
#: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:116 #: xpack/plugins/cloud/models.py:35 xpack/plugins/cloud/models.py:116
#: xpack/plugins/gathered_user/models.py:26 #: xpack/plugins/gathered_user/models.py:26
@ -93,7 +93,7 @@ msgstr "登录复核"
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:44 #: terminal/backends/command/serializers.py:13 terminal/models/session.py:44
#: terminal/models/sharing.py:33 terminal/notifications.py:91 #: terminal/models/sharing.py:33 terminal/notifications.py:91
#: terminal/notifications.py:139 tickets/models/comment.py:21 users/const.py:14 #: terminal/notifications.py:139 tickets/models/comment.py:21 users/const.py:14
#: users/models/user.py:890 users/models/user.py:921 #: users/models/user.py:894 users/models/user.py:925
#: users/serializers/group.py:19 #: users/serializers/group.py:19
msgid "User" msgid "User"
msgstr "用户" msgstr "用户"
@ -159,7 +159,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
#: authentication/models.py:260 #: authentication/models.py:260
#: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_different_city.html:9
#: authentication/templates/authentication/_msg_oauth_bind.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9
#: ops/models/adhoc.py:159 users/forms/profile.py:32 users/models/user.py:659 #: ops/models/adhoc.py:159 users/forms/profile.py:32 users/models/user.py:663
#: users/templates/users/_msg_user_created.html:12 #: users/templates/users/_msg_user_created.html:12
#: xpack/plugins/change_auth_plan/models/asset.py:34 #: xpack/plugins/change_auth_plan/models/asset.py:34
#: xpack/plugins/change_auth_plan/models/asset.py:195 #: xpack/plugins/change_auth_plan/models/asset.py:195
@ -359,7 +359,7 @@ msgstr "类型名称"
#: assets/serializers/cmd_filter.py:48 common/db/models.py:114 #: assets/serializers/cmd_filter.py:48 common/db/models.py:114
#: common/mixins/models.py:50 ops/models/adhoc.py:39 ops/models/command.py:30 #: common/mixins/models.py:50 ops/models/adhoc.py:39 ops/models/command.py:30
#: orgs/models.py:72 orgs/models.py:223 perms/models/base.py:92 #: orgs/models.py:72 orgs/models.py:223 perms/models/base.py:92
#: users/models/group.py:18 users/models/user.py:922 #: users/models/group.py:18 users/models/user.py:926
#: xpack/plugins/cloud/models.py:125 #: xpack/plugins/cloud/models.py:125
msgid "Date created" msgid "Date created"
msgstr "创建日期" msgstr "创建日期"
@ -623,7 +623,7 @@ msgstr "标签管理"
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:52 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:52
#: assets/models/cmd_filter.py:99 assets/models/group.py:21 #: assets/models/cmd_filter.py:99 assets/models/group.py:21
#: common/db/models.py:112 common/mixins/models.py:49 orgs/models.py:71 #: common/db/models.py:112 common/mixins/models.py:49 orgs/models.py:71
#: orgs/models.py:225 perms/models/base.py:91 users/models/user.py:706 #: orgs/models.py:225 perms/models/base.py:91 users/models/user.py:710
#: users/serializers/group.py:33 #: users/serializers/group.py:33
#: xpack/plugins/change_auth_plan/models/base.py:48 #: xpack/plugins/change_auth_plan/models/base.py:48
#: xpack/plugins/cloud/models.py:122 xpack/plugins/gathered_user/models.py:30 #: xpack/plugins/cloud/models.py:122 xpack/plugins/gathered_user/models.py:30
@ -819,7 +819,7 @@ msgstr "带宽"
msgid "Contact" msgid "Contact"
msgstr "联系人" msgstr "联系人"
#: assets/models/cluster.py:22 users/models/user.py:681 #: assets/models/cluster.py:22 users/models/user.py:685
msgid "Phone" msgid "Phone"
msgstr "手机" msgstr "手机"
@ -845,7 +845,7 @@ msgid "Default"
msgstr "默认" msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:14 rbac/const.py:6 #: assets/models/cluster.py:36 assets/models/label.py:14 rbac/const.py:6
#: users/models/user.py:907 #: users/models/user.py:911
msgid "System" msgid "System"
msgstr "系统" msgstr "系统"
@ -854,7 +854,7 @@ msgid "Default Cluster"
msgstr "默认Cluster" msgstr "默认Cluster"
#: assets/models/cmd_filter.py:34 perms/models/base.py:86 #: assets/models/cmd_filter.py:34 perms/models/base.py:86
#: users/models/group.py:31 users/models/user.py:667 #: users/models/group.py:31 users/models/user.py:671
msgid "User group" msgid "User group"
msgstr "用户组" msgstr "用户组"
@ -955,7 +955,7 @@ msgstr "资产组"
msgid "Default asset group" msgid "Default asset group"
msgstr "默认资产组" msgstr "默认资产组"
#: assets/models/label.py:19 assets/models/node.py:546 settings/models.py:30 #: assets/models/label.py:19 assets/models/node.py:546 settings/models.py:34
msgid "Value" msgid "Value"
msgstr "值" msgstr "值"
@ -1149,7 +1149,7 @@ msgstr "定时执行"
msgid "Currently only mail sending is supported" msgid "Currently only mail sending is supported"
msgstr "当前只支持邮件发送" msgstr "当前只支持邮件发送"
#: assets/serializers/base.py:16 users/models/user.py:689 #: assets/serializers/base.py:16 users/models/user.py:693
msgid "Private key" msgid "Private key"
msgstr "ssh私钥" msgstr "ssh私钥"
@ -1495,7 +1495,7 @@ msgstr "改密日志"
msgid "Disabled" msgid "Disabled"
msgstr "禁用" msgstr "禁用"
#: audits/models.py:112 settings/models.py:33 #: audits/models.py:112 settings/models.py:37
msgid "Enabled" msgid "Enabled"
msgstr "启用" msgstr "启用"
@ -1523,7 +1523,7 @@ msgstr "用户代理"
#: audits/models.py:126 #: audits/models.py:126
#: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms/profile.py:65 users/models/user.py:684 #: users/forms/profile.py:65 users/models/user.py:688
#: users/serializers/profile.py:126 #: users/serializers/profile.py:126
msgid "MFA" msgid "MFA"
msgstr "MFA" msgstr "MFA"
@ -1601,20 +1601,20 @@ msgid "Auth Token"
msgstr "认证令牌" msgstr "认证令牌"
#: audits/signal_handlers.py:53 authentication/notifications.py:73 #: audits/signal_handlers.py:53 authentication/notifications.py:73
#: authentication/views/login.py:67 authentication/views/wecom.py:178 #: authentication/views/login.py:73 authentication/views/wecom.py:178
#: notifications/backends/__init__.py:11 users/models/user.py:720 #: notifications/backends/__init__.py:11 users/models/user.py:724
msgid "WeCom" msgid "WeCom"
msgstr "企业微信" msgstr "企业微信"
#: audits/signal_handlers.py:54 authentication/views/feishu.py:144 #: audits/signal_handlers.py:54 authentication/views/feishu.py:144
#: authentication/views/login.py:79 notifications/backends/__init__.py:14 #: authentication/views/login.py:85 notifications/backends/__init__.py:14
#: users/models/user.py:722 #: users/models/user.py:726
msgid "FeiShu" msgid "FeiShu"
msgstr "飞书" msgstr "飞书"
#: audits/signal_handlers.py:55 authentication/views/dingtalk.py:179 #: audits/signal_handlers.py:55 authentication/views/dingtalk.py:179
#: authentication/views/login.py:73 notifications/backends/__init__.py:12 #: authentication/views/login.py:79 notifications/backends/__init__.py:12
#: users/models/user.py:721 #: users/models/user.py:725
msgid "DingTalk" msgid "DingTalk"
msgstr "钉钉" msgstr "钉钉"
@ -1853,6 +1853,10 @@ msgstr "无效的令牌头。符号字符串不应包含无效字符。"
msgid "Invalid token or cache refreshed." msgid "Invalid token or cache refreshed."
msgstr "刷新的令牌或缓存无效。" msgstr "刷新的令牌或缓存无效。"
#: authentication/backends/oauth2/backends.py:155 authentication/models.py:158
msgid "User invalid, disabled or expired"
msgstr "用户无效,已禁用或已过期"
#: authentication/confirm/password.py:16 #: authentication/confirm/password.py:16
msgid "Authentication failed password incorrect" msgid "Authentication failed password incorrect"
msgstr "认证失败 (用户名或密码不正确)" msgstr "认证失败 (用户名或密码不正确)"
@ -2121,7 +2125,7 @@ msgstr "密钥"
#: authentication/models.py:74 authentication/models.py:264 #: authentication/models.py:74 authentication/models.py:264
#: perms/models/base.py:90 tickets/models/ticket/apply_application.py:30 #: perms/models/base.py:90 tickets/models/ticket/apply_application.py:30
#: tickets/models/ticket/apply_asset.py:24 users/models/user.py:703 #: tickets/models/ticket/apply_asset.py:24 users/models/user.py:707
msgid "Date expired" msgid "Date expired"
msgstr "失效日期" msgstr "失效日期"
@ -2145,10 +2149,6 @@ msgstr "连接令牌过期: {}"
msgid "User not exists" msgid "User not exists"
msgstr "用户不存在" msgstr "用户不存在"
#: authentication/models.py:158
msgid "User invalid, disabled or expired"
msgstr "用户无效,已禁用或已过期"
#: authentication/models.py:163 #: authentication/models.py:163
msgid "System user not exists" msgid "System user not exists"
msgstr "系统用户不存在" msgstr "系统用户不存在"
@ -2292,7 +2292,7 @@ msgstr "代码错误"
#: authentication/templates/authentication/_msg_reset_password.html:3 #: authentication/templates/authentication/_msg_reset_password.html:3
#: authentication/templates/authentication/_msg_rest_password_success.html:2 #: authentication/templates/authentication/_msg_rest_password_success.html:2
#: authentication/templates/authentication/_msg_rest_public_key_success.html:2 #: authentication/templates/authentication/_msg_rest_public_key_success.html:2
#: jumpserver/conf.py:307 ops/tasks.py:145 ops/tasks.py:148 #: jumpserver/conf.py:323 ops/tasks.py:145 ops/tasks.py:148
#: perms/templates/perms/_msg_item_permissions_expire.html:3 #: perms/templates/perms/_msg_item_permissions_expire.html:3
#: perms/templates/perms/_msg_permed_items_expire.html:3 #: perms/templates/perms/_msg_permed_items_expire.html:3
#: tickets/templates/tickets/approve_check_password.html:33 #: tickets/templates/tickets/approve_check_password.html:33
@ -2496,19 +2496,19 @@ msgstr "从飞书获取用户失败"
msgid "Please login with a password and then bind the FeiShu" msgid "Please login with a password and then bind the FeiShu"
msgstr "请使用密码登录,然后绑定飞书" msgstr "请使用密码登录,然后绑定飞书"
#: authentication/views/login.py:175 #: authentication/views/login.py:181
msgid "Redirecting" msgid "Redirecting"
msgstr "跳转中" msgstr "跳转中"
#: authentication/views/login.py:176 #: authentication/views/login.py:182
msgid "Redirecting to {} authentication" msgid "Redirecting to {} authentication"
msgstr "正在跳转到 {} 认证" msgstr "正在跳转到 {} 认证"
#: authentication/views/login.py:199 #: authentication/views/login.py:205
msgid "Please enable cookies and try again." msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie" msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:301 #: authentication/views/login.py:307
msgid "" msgid ""
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n" "Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
" Don't close this page" " Don't close this page"
@ -2516,15 +2516,15 @@ msgstr ""
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n" "等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
" 不要关闭本页面" " 不要关闭本页面"
#: authentication/views/login.py:306 #: authentication/views/login.py:312
msgid "No ticket found" msgid "No ticket found"
msgstr "没有发现工单" msgstr "没有发现工单"
#: authentication/views/login.py:340 #: authentication/views/login.py:346
msgid "Logout success" msgid "Logout success"
msgstr "退出登录成功" msgstr "退出登录成功"
#: authentication/views/login.py:341 #: authentication/views/login.py:347
msgid "Logout success, return login page" msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面" msgstr "退出登录成功,返回到登录页面"
@ -2728,11 +2728,11 @@ msgstr "不能包含特殊字符"
msgid "The mobile phone number format is incorrect" msgid "The mobile phone number format is incorrect"
msgstr "手机号格式不正确" msgstr "手机号格式不正确"
#: jumpserver/conf.py:306 #: jumpserver/conf.py:322
msgid "Create account successfully" msgid "Create account successfully"
msgstr "创建账号成功" msgstr "创建账号成功"
#: jumpserver/conf.py:308 #: jumpserver/conf.py:324
msgid "Your account has been created successfully" msgid "Your account has been created successfully"
msgstr "你的账号已创建成功" msgstr "你的账号已创建成功"
@ -2772,7 +2772,7 @@ msgid "Notifications"
msgstr "通知" msgstr "通知"
#: notifications/backends/__init__.py:10 users/forms/profile.py:102 #: notifications/backends/__init__.py:10 users/forms/profile.py:102
#: users/models/user.py:663 #: users/models/user.py:667
msgid "Email" msgid "Email"
msgstr "邮件" msgstr "邮件"
@ -3005,7 +3005,7 @@ msgid "Can view all joined org"
msgstr "可以查看所有加入的组织" msgstr "可以查看所有加入的组织"
#: orgs/models.py:222 rbac/models/role.py:46 rbac/models/rolebinding.py:44 #: orgs/models.py:222 rbac/models/role.py:46 rbac/models/rolebinding.py:44
#: users/models/user.py:671 #: users/models/user.py:675
msgid "Role" msgid "Role"
msgstr "角色" msgstr "角色"
@ -3281,6 +3281,7 @@ msgid "Permission"
msgstr "权限" msgstr "权限"
#: rbac/models/role.py:31 rbac/models/rolebinding.py:38 #: rbac/models/role.py:31 rbac/models/rolebinding.py:38
#: settings/serializers/auth/oauth2.py:35
msgid "Scope" msgid "Scope"
msgstr "范围" msgstr "范围"
@ -3358,7 +3359,7 @@ msgstr "工作台"
msgid "Audit view" msgid "Audit view"
msgstr "审计台" msgstr "审计台"
#: rbac/tree.py:28 settings/models.py:140 #: rbac/tree.py:28 settings/models.py:156
msgid "System setting" msgid "System setting"
msgstr "系统设置" msgstr "系统设置"
@ -3456,43 +3457,43 @@ msgstr "成功导入 {} 个用户 ( 组织: {} )"
msgid "Settings" msgid "Settings"
msgstr "系统设置" msgstr "系统设置"
#: settings/models.py:142 #: settings/models.py:158
msgid "Can change email setting" msgid "Can change email setting"
msgstr "邮件设置" msgstr "邮件设置"
#: settings/models.py:143 #: settings/models.py:159
msgid "Can change auth setting" msgid "Can change auth setting"
msgstr "认证设置" msgstr "认证设置"
#: settings/models.py:144 #: settings/models.py:160
msgid "Can change system msg sub setting" msgid "Can change system msg sub setting"
msgstr "消息订阅设置" msgstr "消息订阅设置"
#: settings/models.py:145 #: settings/models.py:161
msgid "Can change sms setting" msgid "Can change sms setting"
msgstr "短信设置" msgstr "短信设置"
#: settings/models.py:146 #: settings/models.py:162
msgid "Can change security setting" msgid "Can change security setting"
msgstr "安全设置" msgstr "安全设置"
#: settings/models.py:147 #: settings/models.py:163
msgid "Can change clean setting" msgid "Can change clean setting"
msgstr "定期清理" msgstr "定期清理"
#: settings/models.py:148 #: settings/models.py:164
msgid "Can change interface setting" msgid "Can change interface setting"
msgstr "界面设置" msgstr "界面设置"
#: settings/models.py:149 #: settings/models.py:165
msgid "Can change license setting" msgid "Can change license setting"
msgstr "许可证设置" msgstr "许可证设置"
#: settings/models.py:150 #: settings/models.py:166
msgid "Can change terminal setting" msgid "Can change terminal setting"
msgstr "终端设置" msgstr "终端设置"
#: settings/models.py:151 #: settings/models.py:167
msgid "Can change other setting" msgid "Can change other setting"
msgstr "其它设置" msgstr "其它设置"
@ -3605,7 +3606,8 @@ msgstr "用户过滤器"
msgid "Choice may be (cn|uid|sAMAccountName)=%(user)s)" msgid "Choice may be (cn|uid|sAMAccountName)=%(user)s)"
msgstr "可能的选项是(cn或uid或sAMAccountName=%(user)s)" msgstr "可能的选项是(cn或uid或sAMAccountName=%(user)s)"
#: settings/serializers/auth/ldap.py:57 settings/serializers/auth/oidc.py:36 #: settings/serializers/auth/ldap.py:57 settings/serializers/auth/oauth2.py:51
#: settings/serializers/auth/oidc.py:36
msgid "User attr map" msgid "User attr map"
msgstr "用户属性映射" msgstr "用户属性映射"
@ -3629,23 +3631,52 @@ msgstr "搜索分页数量"
msgid "Enable LDAP auth" msgid "Enable LDAP auth"
msgstr "启用 LDAP 认证" msgstr "启用 LDAP 认证"
#: settings/serializers/auth/oidc.py:15 #: settings/serializers/auth/oauth2.py:20
msgid "Base site url" msgid "Enable OAuth2 Auth"
msgstr "JumpServer 地址" msgstr "启用 OAuth2 认证"
#: settings/serializers/auth/oidc.py:18 #: settings/serializers/auth/oauth2.py:23
msgid "Logo"
msgstr "图标"
#: settings/serializers/auth/oauth2.py:26
msgid "Service provider"
msgstr "服务提供商"
#: settings/serializers/auth/oauth2.py:29 settings/serializers/auth/oidc.py:18
msgid "Client Id" msgid "Client Id"
msgstr "客户端 ID" msgstr "客户端 ID"
#: settings/serializers/auth/oidc.py:21 #: settings/serializers/auth/oauth2.py:32 settings/serializers/auth/oidc.py:21
#: xpack/plugins/cloud/serializers/account_attrs.py:36 #: xpack/plugins/cloud/serializers/account_attrs.py:36
msgid "Client Secret" msgid "Client Secret"
msgstr "客户端密钥" msgstr "客户端密钥"
#: settings/serializers/auth/oidc.py:29 #: settings/serializers/auth/oauth2.py:38 settings/serializers/auth/oidc.py:62
msgid "Provider auth endpoint"
msgstr "授权端点地址"
#: settings/serializers/auth/oauth2.py:41 settings/serializers/auth/oidc.py:65
msgid "Provider token endpoint"
msgstr "token 端点地址"
#: settings/serializers/auth/oauth2.py:44 settings/serializers/auth/oidc.py:29
msgid "Client authentication method" msgid "Client authentication method"
msgstr "客户端认证方式" msgstr "客户端认证方式"
#: settings/serializers/auth/oauth2.py:48 settings/serializers/auth/oidc.py:71
msgid "Provider userinfo endpoint"
msgstr "用户信息端点地址"
#: settings/serializers/auth/oauth2.py:54 settings/serializers/auth/oidc.py:92
#: settings/serializers/auth/saml2.py:33
msgid "Always update user"
msgstr "总是更新用户信息"
#: settings/serializers/auth/oidc.py:15
msgid "Base site url"
msgstr "JumpServer 地址"
#: settings/serializers/auth/oidc.py:31 #: settings/serializers/auth/oidc.py:31
msgid "Share session" msgid "Share session"
msgstr "共享会话" msgstr "共享会话"
@ -3678,22 +3709,10 @@ msgstr "启用 OIDC 认证"
msgid "Provider endpoint" msgid "Provider endpoint"
msgstr "端点地址" msgstr "端点地址"
#: settings/serializers/auth/oidc.py:62
msgid "Provider auth endpoint"
msgstr "授权端点地址"
#: settings/serializers/auth/oidc.py:65
msgid "Provider token endpoint"
msgstr "token 端点地址"
#: settings/serializers/auth/oidc.py:68 #: settings/serializers/auth/oidc.py:68
msgid "Provider jwks endpoint" msgid "Provider jwks endpoint"
msgstr "jwks 端点地址" msgstr "jwks 端点地址"
#: settings/serializers/auth/oidc.py:71
msgid "Provider userinfo endpoint"
msgstr "用户信息端点地址"
#: settings/serializers/auth/oidc.py:74 #: settings/serializers/auth/oidc.py:74
msgid "Provider end session endpoint" msgid "Provider end session endpoint"
msgstr "注销会话端点地址" msgstr "注销会话端点地址"
@ -3726,10 +3745,6 @@ msgstr "使用状态"
msgid "Use nonce" msgid "Use nonce"
msgstr "临时使用" msgstr "临时使用"
#: settings/serializers/auth/oidc.py:92 settings/serializers/auth/saml2.py:33
msgid "Always update user"
msgstr "总是更新用户信息"
#: settings/serializers/auth/radius.py:13 #: settings/serializers/auth/radius.py:13
msgid "Enable Radius Auth" msgid "Enable Radius Auth"
msgstr "启用 Radius 认证" msgstr "启用 Radius 认证"
@ -5604,7 +5619,7 @@ msgstr "不能和原来的密钥相同"
msgid "Not a valid ssh public key" msgid "Not a valid ssh public key"
msgstr "SSH密钥不合法" msgstr "SSH密钥不合法"
#: users/forms/profile.py:161 users/models/user.py:692 #: users/forms/profile.py:161 users/models/user.py:696
msgid "Public key" msgid "Public key"
msgstr "SSH公钥" msgstr "SSH公钥"
@ -5616,55 +5631,55 @@ msgstr "强制启用"
msgid "Local" msgid "Local"
msgstr "数据库" msgstr "数据库"
#: users/models/user.py:673 users/serializers/user.py:149 #: users/models/user.py:677 users/serializers/user.py:149
msgid "Is service account" msgid "Is service account"
msgstr "服务账号" msgstr "服务账号"
#: users/models/user.py:675 #: users/models/user.py:679
msgid "Avatar" msgid "Avatar"
msgstr "头像" msgstr "头像"
#: users/models/user.py:678 #: users/models/user.py:682
msgid "Wechat" msgid "Wechat"
msgstr "微信" msgstr "微信"
#: users/models/user.py:695 #: users/models/user.py:699
msgid "Secret key" msgid "Secret key"
msgstr "Secret key" msgstr "Secret key"
#: users/models/user.py:711 #: users/models/user.py:715
msgid "Source" msgid "Source"
msgstr "来源" msgstr "来源"
#: users/models/user.py:715 #: users/models/user.py:719
msgid "Date password last updated" msgid "Date password last updated"
msgstr "最后更新密码日期" msgstr "最后更新密码日期"
#: users/models/user.py:718 #: users/models/user.py:722
msgid "Need update password" msgid "Need update password"
msgstr "需要更新密码" msgstr "需要更新密码"
#: users/models/user.py:892 #: users/models/user.py:896
msgid "Can invite user" msgid "Can invite user"
msgstr "可以邀请用户" msgstr "可以邀请用户"
#: users/models/user.py:893 #: users/models/user.py:897
msgid "Can remove user" msgid "Can remove user"
msgstr "可以移除用户" msgstr "可以移除用户"
#: users/models/user.py:894 #: users/models/user.py:898
msgid "Can match user" msgid "Can match user"
msgstr "可以匹配用户" msgstr "可以匹配用户"
#: users/models/user.py:903 #: users/models/user.py:907
msgid "Administrator" msgid "Administrator"
msgstr "管理员" msgstr "管理员"
#: users/models/user.py:906 #: users/models/user.py:910
msgid "Administrator is the super user of system" msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员" msgstr "Administrator是初始的超级管理员"
#: users/models/user.py:931 #: users/models/user.py:935
msgid "User password history" msgid "User password history"
msgstr "用户密码历史" msgstr "用户密码历史"
@ -6762,6 +6777,9 @@ msgstr "旗舰版"
msgid "Community edition" msgid "Community edition"
msgstr "社区版" msgstr "社区版"
#~ msgid "Logo title"
#~ msgstr "图标标题"
#~ msgid "IP is not allowed" #~ msgid "IP is not allowed"
#~ msgstr "来源 IP 不被允许登录" #~ msgstr "来源 IP 不被允许登录"

View File

@ -34,6 +34,7 @@ class SettingsApi(generics.RetrieveUpdateAPIView):
'cas': serializers.CASSettingSerializer, 'cas': serializers.CASSettingSerializer,
'sso': serializers.SSOSettingSerializer, 'sso': serializers.SSOSettingSerializer,
'saml2': serializers.SAML2SettingSerializer, 'saml2': serializers.SAML2SettingSerializer,
'oauth2': serializers.OAuth2SettingSerializer,
'clean': serializers.CleaningSerializer, 'clean': serializers.CleaningSerializer,
'other': serializers.OtherSettingSerializer, 'other': serializers.OtherSettingSerializer,
'sms': serializers.SMSSettingSerializer, 'sms': serializers.SMSSettingSerializer,
@ -113,9 +114,12 @@ class SettingsApi(generics.RetrieveUpdateAPIView):
return data return data
def perform_update(self, serializer): def perform_update(self, serializer):
post_data_names = list(self.request.data.keys())
settings_items = self.parse_serializer_data(serializer) settings_items = self.parse_serializer_data(serializer)
serializer_data = getattr(serializer, 'data', {}) serializer_data = getattr(serializer, 'data', {})
for item in settings_items: for item in settings_items:
if item['name'] not in post_data_names:
continue
changed, setting = Setting.update_or_create(**item) changed, setting = Setting.update_or_create(**item)
if not changed: if not changed:
continue continue

View File

@ -1,9 +1,13 @@
import os
import json import json
from django.db import models from django.db import models
from django.db.utils import ProgrammingError, OperationalError from django.db.utils import ProgrammingError, OperationalError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.core.files.uploadedfile import InMemoryUploadedFile
from common.utils import signer, get_logger from common.utils import signer, get_logger
@ -118,6 +122,14 @@ class Setting(models.Model):
setattr(settings, key, value) setattr(settings, key, value)
self.__class__.update_or_create(key, value, encrypted=False, category=self.category) self.__class__.update_or_create(key, value, encrypted=False, category=self.category)
@classmethod
def save_to_file(cls, value: InMemoryUploadedFile):
filename = value.name
filepath = f'settings/{filename}'
path = default_storage.save(filepath, ContentFile(value.read()))
url = default_storage.url(path)
return url
@classmethod @classmethod
def update_or_create(cls, name='', value='', encrypted=False, category=''): def update_or_create(cls, name='', value='', encrypted=False, category=''):
""" """
@ -128,6 +140,10 @@ class Setting(models.Model):
changed = False changed = False
if not setting: if not setting:
setting = Setting(name=name, encrypted=encrypted, category=category) setting = Setting(name=name, encrypted=encrypted, category=category)
if isinstance(value, InMemoryUploadedFile):
value = cls.save_to_file(value)
if setting.cleaned_value != value: if setting.cleaned_value != value:
setting.encrypted = encrypted setting.encrypted = encrypted
setting.cleaned_value = value setting.cleaned_value = value

View File

@ -9,3 +9,4 @@ from .sso import *
from .base import * from .base import *
from .sms import * from .sms import *
from .saml2 import * from .saml2 import *
from .oauth2 import *

View File

@ -0,0 +1,55 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from common.utils import static_or_direct
__all__ = [
'OAuth2SettingSerializer',
]
class SettingImageField(serializers.ImageField):
def to_representation(self, value):
return static_or_direct(value)
class OAuth2SettingSerializer(serializers.Serializer):
AUTH_OAUTH2 = serializers.BooleanField(
default=False, required=False, label=_('Enable OAuth2 Auth')
)
AUTH_OAUTH2_LOGO_PATH = SettingImageField(
allow_null=True, required=False, label=_('Logo')
)
AUTH_OAUTH2_PROVIDER = serializers.CharField(
required=False, max_length=16, label=_('Service provider')
)
AUTH_OAUTH2_CLIENT_ID = serializers.CharField(
required=False, max_length=1024, label=_('Client Id')
)
AUTH_OAUTH2_CLIENT_SECRET = EncryptedField(
required=False, max_length=1024, label=_('Client Secret')
)
AUTH_OAUTH2_SCOPE = serializers.CharField(
required=False, max_length=1024, label=_('Scope'), allow_blank=True
)
AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT = serializers.CharField(
required=False, max_length=1024, label=_('Provider auth endpoint')
)
AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT = serializers.CharField(
required=False, max_length=1024, label=_('Provider token endpoint')
)
AUTH_OAUTH2_ACCESS_TOKEN_METHOD = serializers.ChoiceField(
default='GET', label=_('Client authentication method'),
choices=(('GET', 'GET'), ('POST', 'POST'))
)
AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT = serializers.CharField(
required=False, max_length=1024, label=_('Provider userinfo endpoint')
)
AUTH_OAUTH2_USER_ATTR_MAP = serializers.DictField(
required=False, label=_('User attr map')
)
AUTH_OAUTH2_ALWAYS_UPDATE_USER = serializers.BooleanField(
required=False, label=_('Always update user')
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -628,6 +628,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
radius = 'radius', 'Radius' radius = 'radius', 'Radius'
cas = 'cas', 'CAS' cas = 'cas', 'CAS'
saml2 = 'saml2', 'SAML2' saml2 = 'saml2', 'SAML2'
oauth2 = 'oauth2', 'OAuth2'
SOURCE_BACKEND_MAPPING = { SOURCE_BACKEND_MAPPING = {
Source.local: [ Source.local: [
@ -652,6 +653,9 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
Source.saml2: [ Source.saml2: [
settings.AUTH_BACKEND_SAML2 settings.AUTH_BACKEND_SAML2
], ],
Source.oauth2: [
settings.AUTH_BACKEND_OAUTH2
],
} }
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)

View File

@ -9,6 +9,7 @@ from django.db.models.signals import post_save
from authentication.backends.oidc.signals import openid_create_or_update_user from authentication.backends.oidc.signals import openid_create_or_update_user
from authentication.backends.saml2.signals import saml2_create_or_update_user from authentication.backends.saml2.signals import saml2_create_or_update_user
from authentication.backends.oauth2.signals import oauth2_create_or_update_user
from common.utils import get_logger from common.utils import get_logger
from common.decorator import on_transaction_commit from common.decorator import on_transaction_commit
from .signals import post_user_create from .signals import post_user_create
@ -26,16 +27,18 @@ def user_authenticated_handle(user, created, source, attrs=None, **kwargs):
user.source = source user.source = source
user.save() user.save()
if not created and settings.AUTH_SAML2_ALWAYS_UPDATE_USER: if not attrs:
return
always_update = getattr(settings, 'AUTH_%s_ALWAYS_UPDATE_USER' % source.upper(), False)
if not created and always_update:
attr_whitelist = ('user', 'username', 'email', 'phone', 'comment') attr_whitelist = ('user', 'username', 'email', 'phone', 'comment')
logger.debug( logger.debug(
"Receive saml2 user updated signal: {}, " "Receive {} user updated signal: {}, "
"Update user info: {}," "Update user info: {},"
"(Update only properties in the whitelist. [{}])" "(Update only properties in the whitelist. [{}])"
"".format(user, str(attrs), ','.join(attr_whitelist)) "".format(source, user, str(attrs), ','.join(attr_whitelist))
) )
if not attrs:
return
for key, value in attrs.items(): for key, value in attrs.items():
if key in attr_whitelist and value: if key in attr_whitelist and value:
setattr(user, key, value) setattr(user, key, value)
@ -103,6 +106,12 @@ def on_saml2_create_or_update_user(sender, user, created, attrs, **kwargs):
user_authenticated_handle(user, created, source, attrs, **kwargs) user_authenticated_handle(user, created, source, attrs, **kwargs)
@receiver(oauth2_create_or_update_user)
def on_oauth2_create_or_update_user(sender, user, created, attrs, **kwargs):
source = user.Source.oauth2.value
user_authenticated_handle(user, created, source, attrs, **kwargs)
@receiver(populate_user) @receiver(populate_user)
def on_ldap_create_user(sender, user, ldap_user, **kwargs): def on_ldap_create_user(sender, user, ldap_user, **kwargs):
if user and user.username not in ['admin']: if user and user.username not in ['admin']:

View File

@ -230,6 +230,8 @@ class LoginIpBlockUtil(BlockGlobalIpUtilBase):
def construct_user_email(username, email, email_suffix=''): def construct_user_email(username, email, email_suffix=''):
if email is None:
email = ''
if '@' in email: if '@' in email:
return email return email
if '@' in username: if '@' in username: