mirror of https://github.com/jumpserver/jumpserver
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
parent
b22aed0cc3
commit
2099baaaff
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from .backends import *
|
|
@ -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
|
|
@ -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'])
|
||||
|
|
@ -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')
|
||||
]
|
|
@ -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)
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
import base64
|
||||
import requests
|
||||
|
||||
from rest_framework.exceptions import ParseError
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
|
@ -18,10 +19,11 @@ from django.urls import reverse
|
|||
from django.conf import settings
|
||||
|
||||
from common.utils import get_logger
|
||||
from authentication.utils import build_absolute_uri_for_oidc
|
||||
from users.utils import construct_user_email
|
||||
|
||||
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 .signals import (
|
||||
openid_create_or_update_user, openid_user_login_failed, openid_user_login_success
|
||||
|
@ -127,7 +129,7 @@ class OIDCAuthCodeBackend(OIDCBaseBackend):
|
|||
token_payload = {
|
||||
'grant_type': 'authorization_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)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import datetime as dt
|
||||
from calendar import timegm
|
||||
from urllib.parse import urlparse, urljoin
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
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')
|
||||
|
||||
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
|
||||
|
|
|
@ -20,7 +20,8 @@ from django.utils.crypto import get_random_string
|
|||
from django.utils.http import is_safe_url, urlencode
|
||||
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__)
|
||||
|
@ -50,7 +51,7 @@ class OIDCAuthRequestView(View):
|
|||
'scope': settings.AUTH_OPENID_SCOPES,
|
||||
'response_type': 'code',
|
||||
'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)
|
||||
)
|
||||
})
|
||||
|
@ -216,7 +217,7 @@ class OIDCEndSessionView(View):
|
|||
""" Returns the end-session URL. """
|
||||
q = QueryDict(mutable=True)
|
||||
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] = \
|
||||
self.request.session['oidc_auth_id_token']
|
||||
return '{}?{}'.format(settings.AUTH_OPENID_PROVIDER_END_SESSION_ENDPOINT, q.urlencode())
|
||||
|
|
|
@ -39,7 +39,7 @@ class SAML2Backend(JMSModelBackend):
|
|||
return user, created
|
||||
|
||||
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'))
|
||||
if saml_user_data is None:
|
||||
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)))
|
||||
username = saml_user_data.get('username')
|
||||
if not username:
|
||||
logger.debug(log_prompt.format('username is missing'))
|
||||
logger.warning(log_prompt.format('username is missing'))
|
||||
return None
|
||||
|
||||
user, created = self.get_or_create_from_saml_data(request, **saml_user_data)
|
||||
|
|
|
@ -12,6 +12,9 @@ from authentication.backends.oidc.signals import (
|
|||
from authentication.backends.saml2.signals import (
|
||||
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
|
||||
|
||||
|
||||
|
@ -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):
|
||||
request.session['auth_backend'] = settings.AUTH_BACKEND_SAML2
|
||||
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)
|
||||
|
|
|
@ -56,9 +56,11 @@ urlpatterns = [
|
|||
path('profile/otp/disable/', users_view.UserOtpDisableView.as_view(),
|
||||
name='user-otp-disable'),
|
||||
|
||||
# openid
|
||||
# other authentication protocol
|
||||
path('cas/', include(('authentication.backends.cas.urls', 'authentication'), namespace='cas')),
|
||||
path('openid/', include(('authentication.backends.oidc.urls', 'authentication'), namespace='openid')),
|
||||
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')),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from urllib.parse import urljoin
|
||||
|
||||
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:
|
||||
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
|
||||
|
|
|
@ -21,7 +21,7 @@ from django.conf import settings
|
|||
from django.urls import reverse_lazy
|
||||
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 (
|
||||
redirect_user_first_login_or_index
|
||||
)
|
||||
|
@ -39,8 +39,7 @@ class UserLoginContextMixin:
|
|||
get_user_mfa_context: Callable
|
||||
request: HttpRequest
|
||||
|
||||
@staticmethod
|
||||
def get_support_auth_methods():
|
||||
def get_support_auth_methods(self):
|
||||
auth_methods = [
|
||||
{
|
||||
'name': 'OpenID',
|
||||
|
@ -63,6 +62,13 @@ class UserLoginContextMixin:
|
|||
'logo': static('img/login_saml2_logo.png'),
|
||||
'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'),
|
||||
'enabled': settings.AUTH_WECOM,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import re
|
||||
from django.templatetags.static import static
|
||||
from collections import OrderedDict
|
||||
from itertools import chain
|
||||
import logging
|
||||
|
@ -365,3 +366,10 @@ def pretty_string(data: str, max_length=128, ellipsis_str='...'):
|
|||
|
||||
def group_by_count(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
|
||||
|
|
|
@ -265,6 +265,22 @@ class Config(dict):
|
|||
'AUTH_SAML2_PROVIDER_AUTHORIZATION_ENDPOINT': '/',
|
||||
'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,
|
||||
|
||||
# 企业微信
|
||||
|
|
|
@ -143,6 +143,23 @@ SAML2_SP_ADVANCED_SETTINGS = CONFIG.SAML2_SP_ADVANCED_SETTINGS
|
|||
SAML2_LOGIN_URL_NAME = "authentication:saml2:saml2-login"
|
||||
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
|
||||
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_AUTH_TOKEN = 'authentication.backends.sso.AuthorizationTokenAuthentication'
|
||||
AUTH_BACKEND_SAML2 = 'authentication.backends.saml2.SAML2Backend'
|
||||
AUTH_BACKEND_OAUTH2 = 'authentication.backends.oauth2.OAuth2Backend'
|
||||
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_CAS, AUTH_BACKEND_OIDC_PASSWORD, AUTH_BACKEND_OIDC_CODE, AUTH_BACKEND_SAML2,
|
||||
AUTH_BACKEND_OAUTH2,
|
||||
# 扫码模式
|
||||
AUTH_BACKEND_WECOM, AUTH_BACKEND_DINGTALK, AUTH_BACKEND_FEISHU,
|
||||
# Token模式
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:322701b975fe90b4b187c4a99ddd1837291150502c82accf0a4c6e32dddf91be
|
||||
size 128721
|
||||
oid sha256:73ea6289c22c329752330fae1fef6d174573c7f46355137ffbc864407b2b8270
|
||||
size 129073
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\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"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\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/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
|
||||
#: 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/storage.py:26 terminal/models/task.py:16
|
||||
#: 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
|
||||
msgid "Name"
|
||||
msgstr "名前"
|
||||
|
@ -60,11 +60,11 @@ msgstr "アクティブ"
|
|||
#: assets/models/cmd_filter.py:96 assets/models/domain.py:24
|
||||
#: assets/models/domain.py:65 assets/models/group.py:23
|
||||
#: 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/storage.py:29 terminal/models/terminal.py:114
|
||||
#: 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/cloud/models.py:35 xpack/plugins/cloud/models.py:116
|
||||
#: xpack/plugins/gathered_user/models.py:26
|
||||
|
@ -94,7 +94,7 @@ msgstr "ログイン確認"
|
|||
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:44
|
||||
#: terminal/models/sharing.py:33 terminal/notifications.py:91
|
||||
#: 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
|
||||
msgid "User"
|
||||
msgstr "ユーザー"
|
||||
|
@ -160,7 +160,7 @@ msgstr "コンマ区切り文字列の形式。* はすべて一致すること
|
|||
#: authentication/models.py:260
|
||||
#: authentication/templates/authentication/_msg_different_city.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
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:34
|
||||
#: 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
|
||||
#: 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
|
||||
#: 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
|
||||
msgid "Date created"
|
||||
msgstr "作成された日付"
|
||||
|
@ -628,7 +628,7 @@ msgstr "ラベル"
|
|||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:52
|
||||
#: 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
|
||||
#: 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
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:48
|
||||
#: xpack/plugins/cloud/models.py:122 xpack/plugins/gathered_user/models.py:30
|
||||
|
@ -824,7 +824,7 @@ msgstr "帯域幅"
|
|||
msgid "Contact"
|
||||
msgstr "連絡先"
|
||||
|
||||
#: assets/models/cluster.py:22 users/models/user.py:681
|
||||
#: assets/models/cluster.py:22 users/models/user.py:685
|
||||
msgid "Phone"
|
||||
msgstr "電話"
|
||||
|
||||
|
@ -850,7 +850,7 @@ msgid "Default"
|
|||
msgstr "デフォルト"
|
||||
|
||||
#: 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"
|
||||
msgstr "システム"
|
||||
|
||||
|
@ -859,7 +859,7 @@ msgid "Default Cluster"
|
|||
msgstr "デフォルトクラスター"
|
||||
|
||||
#: 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"
|
||||
msgstr "ユーザーグループ"
|
||||
|
||||
|
@ -960,7 +960,7 @@ msgstr "資産グループ"
|
|||
msgid "Default asset group"
|
||||
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"
|
||||
msgstr "値"
|
||||
|
||||
|
@ -1157,7 +1157,7 @@ msgstr "定期的なパフォーマンス"
|
|||
msgid "Currently only mail sending is supported"
|
||||
msgstr "現在、メール送信のみがサポートされています"
|
||||
|
||||
#: assets/serializers/base.py:16 users/models/user.py:689
|
||||
#: assets/serializers/base.py:16 users/models/user.py:693
|
||||
msgid "Private key"
|
||||
msgstr "ssh秘密鍵"
|
||||
|
||||
|
@ -1507,7 +1507,7 @@ msgstr "パスワード変更ログ"
|
|||
msgid "Disabled"
|
||||
msgstr "無効"
|
||||
|
||||
#: audits/models.py:112 settings/models.py:33
|
||||
#: audits/models.py:112 settings/models.py:37
|
||||
msgid "Enabled"
|
||||
msgstr "有効化"
|
||||
|
||||
|
@ -1535,7 +1535,7 @@ msgstr "ユーザーエージェント"
|
|||
|
||||
#: audits/models.py:126
|
||||
#: 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
|
||||
msgid "MFA"
|
||||
msgstr "MFA"
|
||||
|
@ -1613,20 +1613,20 @@ msgid "Auth Token"
|
|||
msgstr "認証トークン"
|
||||
|
||||
#: audits/signal_handlers.py:53 authentication/notifications.py:73
|
||||
#: authentication/views/login.py:67 authentication/views/wecom.py:178
|
||||
#: notifications/backends/__init__.py:11 users/models/user.py:720
|
||||
#: authentication/views/login.py:73 authentication/views/wecom.py:178
|
||||
#: notifications/backends/__init__.py:11 users/models/user.py:724
|
||||
msgid "WeCom"
|
||||
msgstr "企業微信"
|
||||
|
||||
#: audits/signal_handlers.py:54 authentication/views/feishu.py:144
|
||||
#: authentication/views/login.py:79 notifications/backends/__init__.py:14
|
||||
#: users/models/user.py:722
|
||||
#: authentication/views/login.py:85 notifications/backends/__init__.py:14
|
||||
#: users/models/user.py:726
|
||||
msgid "FeiShu"
|
||||
msgstr "本を飛ばす"
|
||||
|
||||
#: audits/signal_handlers.py:55 authentication/views/dingtalk.py:179
|
||||
#: authentication/views/login.py:73 notifications/backends/__init__.py:12
|
||||
#: users/models/user.py:721
|
||||
#: authentication/views/login.py:79 notifications/backends/__init__.py:12
|
||||
#: users/models/user.py:725
|
||||
msgid "DingTalk"
|
||||
msgstr "DingTalk"
|
||||
|
||||
|
@ -1867,6 +1867,10 @@ msgstr ""
|
|||
msgid "Invalid token or cache refreshed."
|
||||
msgstr "無効なトークンまたはキャッシュの更新。"
|
||||
|
||||
#: authentication/backends/oauth2/backends.py:155 authentication/models.py:158
|
||||
msgid "User invalid, disabled or expired"
|
||||
msgstr "ユーザーが無効、無効、または期限切れです"
|
||||
|
||||
#: authentication/confirm/password.py:16
|
||||
msgid "Authentication failed password incorrect"
|
||||
msgstr "認証に失敗しました (ユーザー名またはパスワードが正しくありません)"
|
||||
|
@ -2142,7 +2146,7 @@ msgstr "ひみつ"
|
|||
|
||||
#: authentication/models.py:74 authentication/models.py:264
|
||||
#: 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"
|
||||
msgstr "期限切れの日付"
|
||||
|
||||
|
@ -2166,10 +2170,6 @@ msgstr "接続トークンの有効期限: {}"
|
|||
msgid "User not exists"
|
||||
msgstr "ユーザーは存在しません"
|
||||
|
||||
#: authentication/models.py:158
|
||||
msgid "User invalid, disabled or expired"
|
||||
msgstr "ユーザーが無効、無効、または期限切れです"
|
||||
|
||||
#: authentication/models.py:163
|
||||
msgid "System user not exists"
|
||||
msgstr "システムユーザーが存在しません"
|
||||
|
@ -2317,7 +2317,7 @@ msgstr "コードエラー"
|
|||
#: authentication/templates/authentication/_msg_reset_password.html:3
|
||||
#: authentication/templates/authentication/_msg_rest_password_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_permed_items_expire.html:3
|
||||
#: tickets/templates/tickets/approve_check_password.html:33
|
||||
|
@ -2530,19 +2530,19 @@ msgstr "本を飛ばすからユーザーを取得できませんでした"
|
|||
msgid "Please login with a password and then bind the FeiShu"
|
||||
msgstr "パスワードでログインしてから本を飛ばすをバインドしてください"
|
||||
|
||||
#: authentication/views/login.py:175
|
||||
#: authentication/views/login.py:181
|
||||
msgid "Redirecting"
|
||||
msgstr "リダイレクト"
|
||||
|
||||
#: authentication/views/login.py:176
|
||||
#: authentication/views/login.py:182
|
||||
msgid "Redirecting to {} authentication"
|
||||
msgstr "{} 認証へのリダイレクト"
|
||||
|
||||
#: authentication/views/login.py:199
|
||||
#: authentication/views/login.py:205
|
||||
msgid "Please enable cookies and try again."
|
||||
msgstr "クッキーを有効にして、もう一度お試しください。"
|
||||
|
||||
#: authentication/views/login.py:301
|
||||
#: authentication/views/login.py:307
|
||||
msgid ""
|
||||
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
|
||||
" Don't close this page"
|
||||
|
@ -2550,15 +2550,15 @@ msgstr ""
|
|||
"<b>{}</b> 確認を待ちます。彼女/彼へのリンクをコピーすることもできます <br/>\n"
|
||||
" このページを閉じないでください"
|
||||
|
||||
#: authentication/views/login.py:306
|
||||
#: authentication/views/login.py:312
|
||||
msgid "No ticket found"
|
||||
msgstr "チケットが見つかりません"
|
||||
|
||||
#: authentication/views/login.py:340
|
||||
#: authentication/views/login.py:346
|
||||
msgid "Logout success"
|
||||
msgstr "ログアウト成功"
|
||||
|
||||
#: authentication/views/login.py:341
|
||||
#: authentication/views/login.py:347
|
||||
msgid "Logout success, return login page"
|
||||
msgstr "ログアウト成功、ログインページを返す"
|
||||
|
||||
|
@ -2762,11 +2762,11 @@ msgstr "特殊文字を含むべきではない"
|
|||
msgid "The mobile phone number format is incorrect"
|
||||
msgstr "携帯電話番号の形式が正しくありません"
|
||||
|
||||
#: jumpserver/conf.py:306
|
||||
#: jumpserver/conf.py:322
|
||||
msgid "Create account successfully"
|
||||
msgstr "アカウントを正常に作成"
|
||||
|
||||
#: jumpserver/conf.py:308
|
||||
#: jumpserver/conf.py:324
|
||||
msgid "Your account has been created successfully"
|
||||
msgstr "アカウントが正常に作成されました"
|
||||
|
||||
|
@ -2811,7 +2811,7 @@ msgid "Notifications"
|
|||
msgstr "通知"
|
||||
|
||||
#: notifications/backends/__init__.py:10 users/forms/profile.py:102
|
||||
#: users/models/user.py:663
|
||||
#: users/models/user.py:667
|
||||
msgid "Email"
|
||||
msgstr "メール"
|
||||
|
||||
|
@ -3045,7 +3045,7 @@ msgid "Can view all joined org"
|
|||
msgstr "参加しているすべての組織を表示できます"
|
||||
|
||||
#: 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"
|
||||
msgstr "ロール"
|
||||
|
||||
|
@ -3323,6 +3323,7 @@ msgid "Permission"
|
|||
msgstr "権限"
|
||||
|
||||
#: rbac/models/role.py:31 rbac/models/rolebinding.py:38
|
||||
#: settings/serializers/auth/oauth2.py:35
|
||||
msgid "Scope"
|
||||
msgstr "スコープ"
|
||||
|
||||
|
@ -3401,7 +3402,7 @@ msgstr "ワークスペースビュー"
|
|||
msgid "Audit view"
|
||||
msgstr "監査ビュー"
|
||||
|
||||
#: rbac/tree.py:28 settings/models.py:140
|
||||
#: rbac/tree.py:28 settings/models.py:156
|
||||
msgid "System setting"
|
||||
msgstr "システム設定"
|
||||
|
||||
|
@ -3499,43 +3500,43 @@ msgstr "{} 人のユーザーを正常にインポートしました (組織: {}
|
|||
msgid "Settings"
|
||||
msgstr "設定"
|
||||
|
||||
#: settings/models.py:142
|
||||
#: settings/models.py:158
|
||||
msgid "Can change email setting"
|
||||
msgstr "メール設定を変更できます"
|
||||
|
||||
#: settings/models.py:143
|
||||
#: settings/models.py:159
|
||||
msgid "Can change auth setting"
|
||||
msgstr "資格認定の設定"
|
||||
|
||||
#: settings/models.py:144
|
||||
#: settings/models.py:160
|
||||
msgid "Can change system msg sub setting"
|
||||
msgstr "システムmsgサブ设定を変更できます"
|
||||
|
||||
#: settings/models.py:145
|
||||
#: settings/models.py:161
|
||||
msgid "Can change sms setting"
|
||||
msgstr "Smsの設定を変えることができます"
|
||||
|
||||
#: settings/models.py:146
|
||||
#: settings/models.py:162
|
||||
msgid "Can change security setting"
|
||||
msgstr "セキュリティ設定を変更できます"
|
||||
|
||||
#: settings/models.py:147
|
||||
#: settings/models.py:163
|
||||
msgid "Can change clean setting"
|
||||
msgstr "きれいな設定を変えることができます"
|
||||
|
||||
#: settings/models.py:148
|
||||
#: settings/models.py:164
|
||||
msgid "Can change interface setting"
|
||||
msgstr "インターフェイスの設定を変えることができます"
|
||||
|
||||
#: settings/models.py:149
|
||||
#: settings/models.py:165
|
||||
msgid "Can change license setting"
|
||||
msgstr "ライセンス設定を変更できます"
|
||||
|
||||
#: settings/models.py:150
|
||||
#: settings/models.py:166
|
||||
msgid "Can change terminal setting"
|
||||
msgstr "ターミナルの設定を変えることができます"
|
||||
|
||||
#: settings/models.py:151
|
||||
#: settings/models.py:167
|
||||
msgid "Can change other setting"
|
||||
msgstr "他の設定を変えることができます"
|
||||
|
||||
|
@ -3648,7 +3649,8 @@ msgstr "ユーザー検索フィルター"
|
|||
msgid "Choice may be (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"
|
||||
msgstr "ユーザー属性マッピング"
|
||||
|
||||
|
@ -3672,23 +3674,52 @@ msgstr "ページサイズを検索"
|
|||
msgid "Enable LDAP auth"
|
||||
msgstr "LDAP認証の有効化"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:15
|
||||
msgid "Base site url"
|
||||
msgstr "ベースサイトのアドレス"
|
||||
#: settings/serializers/auth/oauth2.py:20
|
||||
msgid "Enable OAuth2 Auth"
|
||||
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"
|
||||
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
|
||||
msgid "Client Secret"
|
||||
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"
|
||||
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
|
||||
msgid "Share session"
|
||||
msgstr "セッションの共有"
|
||||
|
@ -3721,22 +3752,10 @@ msgstr "OIDC認証の有効化"
|
|||
msgid "Provider endpoint"
|
||||
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
|
||||
msgid "Provider jwks endpoint"
|
||||
msgstr "プロバイダーjwksエンドポイント"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:71
|
||||
msgid "Provider userinfo endpoint"
|
||||
msgstr "プロバイダーuserinfoエンドポイント"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:74
|
||||
msgid "Provider end session endpoint"
|
||||
msgstr "プロバイダーのセッション終了エンドポイント"
|
||||
|
@ -3769,10 +3788,6 @@ msgstr "使用状態"
|
|||
msgid "Use 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
|
||||
msgid "Enable Radius Auth"
|
||||
msgstr "Radius認証の有効化"
|
||||
|
@ -5686,7 +5701,7 @@ msgstr "公開鍵は古いものと同じであってはなりません。"
|
|||
msgid "Not a valid ssh public key"
|
||||
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"
|
||||
msgstr "公開キー"
|
||||
|
||||
|
@ -5698,55 +5713,55 @@ msgstr "強制有効"
|
|||
msgid "Local"
|
||||
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"
|
||||
msgstr "サービスアカウントです"
|
||||
|
||||
#: users/models/user.py:675
|
||||
#: users/models/user.py:679
|
||||
msgid "Avatar"
|
||||
msgstr "アバター"
|
||||
|
||||
#: users/models/user.py:678
|
||||
#: users/models/user.py:682
|
||||
msgid "Wechat"
|
||||
msgstr "微信"
|
||||
|
||||
#: users/models/user.py:695
|
||||
#: users/models/user.py:699
|
||||
msgid "Secret key"
|
||||
msgstr "秘密キー"
|
||||
|
||||
#: users/models/user.py:711
|
||||
#: users/models/user.py:715
|
||||
msgid "Source"
|
||||
msgstr "ソース"
|
||||
|
||||
#: users/models/user.py:715
|
||||
#: users/models/user.py:719
|
||||
msgid "Date password last updated"
|
||||
msgstr "最終更新日パスワード"
|
||||
|
||||
#: users/models/user.py:718
|
||||
#: users/models/user.py:722
|
||||
msgid "Need update password"
|
||||
msgstr "更新パスワードが必要"
|
||||
|
||||
#: users/models/user.py:892
|
||||
#: users/models/user.py:896
|
||||
msgid "Can invite user"
|
||||
msgstr "ユーザーを招待できます"
|
||||
|
||||
#: users/models/user.py:893
|
||||
#: users/models/user.py:897
|
||||
msgid "Can remove user"
|
||||
msgstr "ユーザーを削除できます"
|
||||
|
||||
#: users/models/user.py:894
|
||||
#: users/models/user.py:898
|
||||
msgid "Can match user"
|
||||
msgstr "ユーザーに一致できます"
|
||||
|
||||
#: users/models/user.py:903
|
||||
#: users/models/user.py:907
|
||||
msgid "Administrator"
|
||||
msgstr "管理者"
|
||||
|
||||
#: users/models/user.py:906
|
||||
#: users/models/user.py:910
|
||||
msgid "Administrator is the super user of system"
|
||||
msgstr "管理者はシステムのスーパーユーザーです"
|
||||
|
||||
#: users/models/user.py:931
|
||||
#: users/models/user.py:935
|
||||
msgid "User password history"
|
||||
msgstr "ユーザーパスワード履歴"
|
||||
|
||||
|
@ -6859,6 +6874,9 @@ msgstr "究極のエディション"
|
|||
msgid "Community edition"
|
||||
msgstr "コミュニティ版"
|
||||
|
||||
#~ msgid "Logo title"
|
||||
#~ msgstr "アイコンタイトル"
|
||||
|
||||
#~ msgid "IP is not allowed"
|
||||
#~ msgstr "IPは許可されていません"
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9ed12e275e241284573d49c752cf01bafddb912dfe38ae2888a62e62cdb30ebd
|
||||
size 106084
|
||||
oid sha256:4e8c2c0a8a9b7d9de0bd11c1fba8073bbe44fe7274ff6bb3537d5fa19b083baa
|
||||
size 106370
|
||||
|
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\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"
|
||||
"Last-Translator: ibuler <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/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
|
||||
#: 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/storage.py:26 terminal/models/task.py:16
|
||||
#: 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
|
||||
msgid "Name"
|
||||
msgstr "名称"
|
||||
|
@ -59,11 +59,11 @@ msgstr "激活中"
|
|||
#: assets/models/cmd_filter.py:96 assets/models/domain.py:24
|
||||
#: assets/models/domain.py:65 assets/models/group.py:23
|
||||
#: 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/storage.py:29 terminal/models/terminal.py:114
|
||||
#: 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/cloud/models.py:35 xpack/plugins/cloud/models.py:116
|
||||
#: xpack/plugins/gathered_user/models.py:26
|
||||
|
@ -93,7 +93,7 @@ msgstr "登录复核"
|
|||
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:44
|
||||
#: terminal/models/sharing.py:33 terminal/notifications.py:91
|
||||
#: 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
|
||||
msgid "User"
|
||||
msgstr "用户"
|
||||
|
@ -159,7 +159,7 @@ msgstr "格式为逗号分隔的字符串, * 表示匹配所有. "
|
|||
#: authentication/models.py:260
|
||||
#: authentication/templates/authentication/_msg_different_city.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
|
||||
#: xpack/plugins/change_auth_plan/models/asset.py:34
|
||||
#: 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
|
||||
#: 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
|
||||
#: 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
|
||||
msgid "Date created"
|
||||
msgstr "创建日期"
|
||||
|
@ -623,7 +623,7 @@ msgstr "标签管理"
|
|||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:52
|
||||
#: 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
|
||||
#: 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
|
||||
#: xpack/plugins/change_auth_plan/models/base.py:48
|
||||
#: xpack/plugins/cloud/models.py:122 xpack/plugins/gathered_user/models.py:30
|
||||
|
@ -819,7 +819,7 @@ msgstr "带宽"
|
|||
msgid "Contact"
|
||||
msgstr "联系人"
|
||||
|
||||
#: assets/models/cluster.py:22 users/models/user.py:681
|
||||
#: assets/models/cluster.py:22 users/models/user.py:685
|
||||
msgid "Phone"
|
||||
msgstr "手机"
|
||||
|
||||
|
@ -845,7 +845,7 @@ msgid "Default"
|
|||
msgstr "默认"
|
||||
|
||||
#: 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"
|
||||
msgstr "系统"
|
||||
|
||||
|
@ -854,7 +854,7 @@ msgid "Default Cluster"
|
|||
msgstr "默认Cluster"
|
||||
|
||||
#: 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"
|
||||
msgstr "用户组"
|
||||
|
||||
|
@ -955,7 +955,7 @@ msgstr "资产组"
|
|||
msgid "Default asset group"
|
||||
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"
|
||||
msgstr "值"
|
||||
|
||||
|
@ -1149,7 +1149,7 @@ msgstr "定时执行"
|
|||
msgid "Currently only mail sending is supported"
|
||||
msgstr "当前只支持邮件发送"
|
||||
|
||||
#: assets/serializers/base.py:16 users/models/user.py:689
|
||||
#: assets/serializers/base.py:16 users/models/user.py:693
|
||||
msgid "Private key"
|
||||
msgstr "ssh私钥"
|
||||
|
||||
|
@ -1495,7 +1495,7 @@ msgstr "改密日志"
|
|||
msgid "Disabled"
|
||||
msgstr "禁用"
|
||||
|
||||
#: audits/models.py:112 settings/models.py:33
|
||||
#: audits/models.py:112 settings/models.py:37
|
||||
msgid "Enabled"
|
||||
msgstr "启用"
|
||||
|
||||
|
@ -1523,7 +1523,7 @@ msgstr "用户代理"
|
|||
|
||||
#: audits/models.py:126
|
||||
#: 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
|
||||
msgid "MFA"
|
||||
msgstr "MFA"
|
||||
|
@ -1601,20 +1601,20 @@ msgid "Auth Token"
|
|||
msgstr "认证令牌"
|
||||
|
||||
#: audits/signal_handlers.py:53 authentication/notifications.py:73
|
||||
#: authentication/views/login.py:67 authentication/views/wecom.py:178
|
||||
#: notifications/backends/__init__.py:11 users/models/user.py:720
|
||||
#: authentication/views/login.py:73 authentication/views/wecom.py:178
|
||||
#: notifications/backends/__init__.py:11 users/models/user.py:724
|
||||
msgid "WeCom"
|
||||
msgstr "企业微信"
|
||||
|
||||
#: audits/signal_handlers.py:54 authentication/views/feishu.py:144
|
||||
#: authentication/views/login.py:79 notifications/backends/__init__.py:14
|
||||
#: users/models/user.py:722
|
||||
#: authentication/views/login.py:85 notifications/backends/__init__.py:14
|
||||
#: users/models/user.py:726
|
||||
msgid "FeiShu"
|
||||
msgstr "飞书"
|
||||
|
||||
#: audits/signal_handlers.py:55 authentication/views/dingtalk.py:179
|
||||
#: authentication/views/login.py:73 notifications/backends/__init__.py:12
|
||||
#: users/models/user.py:721
|
||||
#: authentication/views/login.py:79 notifications/backends/__init__.py:12
|
||||
#: users/models/user.py:725
|
||||
msgid "DingTalk"
|
||||
msgstr "钉钉"
|
||||
|
||||
|
@ -1853,6 +1853,10 @@ msgstr "无效的令牌头。符号字符串不应包含无效字符。"
|
|||
msgid "Invalid token or cache refreshed."
|
||||
msgstr "刷新的令牌或缓存无效。"
|
||||
|
||||
#: authentication/backends/oauth2/backends.py:155 authentication/models.py:158
|
||||
msgid "User invalid, disabled or expired"
|
||||
msgstr "用户无效,已禁用或已过期"
|
||||
|
||||
#: authentication/confirm/password.py:16
|
||||
msgid "Authentication failed password incorrect"
|
||||
msgstr "认证失败 (用户名或密码不正确)"
|
||||
|
@ -2121,7 +2125,7 @@ msgstr "密钥"
|
|||
|
||||
#: authentication/models.py:74 authentication/models.py:264
|
||||
#: 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"
|
||||
msgstr "失效日期"
|
||||
|
||||
|
@ -2145,10 +2149,6 @@ msgstr "连接令牌过期: {}"
|
|||
msgid "User not exists"
|
||||
msgstr "用户不存在"
|
||||
|
||||
#: authentication/models.py:158
|
||||
msgid "User invalid, disabled or expired"
|
||||
msgstr "用户无效,已禁用或已过期"
|
||||
|
||||
#: authentication/models.py:163
|
||||
msgid "System user not exists"
|
||||
msgstr "系统用户不存在"
|
||||
|
@ -2292,7 +2292,7 @@ msgstr "代码错误"
|
|||
#: authentication/templates/authentication/_msg_reset_password.html:3
|
||||
#: authentication/templates/authentication/_msg_rest_password_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_permed_items_expire.html:3
|
||||
#: tickets/templates/tickets/approve_check_password.html:33
|
||||
|
@ -2496,19 +2496,19 @@ msgstr "从飞书获取用户失败"
|
|||
msgid "Please login with a password and then bind the FeiShu"
|
||||
msgstr "请使用密码登录,然后绑定飞书"
|
||||
|
||||
#: authentication/views/login.py:175
|
||||
#: authentication/views/login.py:181
|
||||
msgid "Redirecting"
|
||||
msgstr "跳转中"
|
||||
|
||||
#: authentication/views/login.py:176
|
||||
#: authentication/views/login.py:182
|
||||
msgid "Redirecting to {} authentication"
|
||||
msgstr "正在跳转到 {} 认证"
|
||||
|
||||
#: authentication/views/login.py:199
|
||||
#: authentication/views/login.py:205
|
||||
msgid "Please enable cookies and try again."
|
||||
msgstr "设置你的浏览器支持cookie"
|
||||
|
||||
#: authentication/views/login.py:301
|
||||
#: authentication/views/login.py:307
|
||||
msgid ""
|
||||
"Wait for <b>{}</b> confirm, You also can copy link to her/him <br/>\n"
|
||||
" Don't close this page"
|
||||
|
@ -2516,15 +2516,15 @@ msgstr ""
|
|||
"等待 <b>{}</b> 确认, 你也可以复制链接发给他/她 <br/>\n"
|
||||
" 不要关闭本页面"
|
||||
|
||||
#: authentication/views/login.py:306
|
||||
#: authentication/views/login.py:312
|
||||
msgid "No ticket found"
|
||||
msgstr "没有发现工单"
|
||||
|
||||
#: authentication/views/login.py:340
|
||||
#: authentication/views/login.py:346
|
||||
msgid "Logout success"
|
||||
msgstr "退出登录成功"
|
||||
|
||||
#: authentication/views/login.py:341
|
||||
#: authentication/views/login.py:347
|
||||
msgid "Logout success, return login page"
|
||||
msgstr "退出登录成功,返回到登录页面"
|
||||
|
||||
|
@ -2728,11 +2728,11 @@ msgstr "不能包含特殊字符"
|
|||
msgid "The mobile phone number format is incorrect"
|
||||
msgstr "手机号格式不正确"
|
||||
|
||||
#: jumpserver/conf.py:306
|
||||
#: jumpserver/conf.py:322
|
||||
msgid "Create account successfully"
|
||||
msgstr "创建账号成功"
|
||||
|
||||
#: jumpserver/conf.py:308
|
||||
#: jumpserver/conf.py:324
|
||||
msgid "Your account has been created successfully"
|
||||
msgstr "你的账号已创建成功"
|
||||
|
||||
|
@ -2772,7 +2772,7 @@ msgid "Notifications"
|
|||
msgstr "通知"
|
||||
|
||||
#: notifications/backends/__init__.py:10 users/forms/profile.py:102
|
||||
#: users/models/user.py:663
|
||||
#: users/models/user.py:667
|
||||
msgid "Email"
|
||||
msgstr "邮件"
|
||||
|
||||
|
@ -3005,7 +3005,7 @@ msgid "Can view all joined org"
|
|||
msgstr "可以查看所有加入的组织"
|
||||
|
||||
#: 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"
|
||||
msgstr "角色"
|
||||
|
||||
|
@ -3281,6 +3281,7 @@ msgid "Permission"
|
|||
msgstr "权限"
|
||||
|
||||
#: rbac/models/role.py:31 rbac/models/rolebinding.py:38
|
||||
#: settings/serializers/auth/oauth2.py:35
|
||||
msgid "Scope"
|
||||
msgstr "范围"
|
||||
|
||||
|
@ -3358,7 +3359,7 @@ msgstr "工作台"
|
|||
msgid "Audit view"
|
||||
msgstr "审计台"
|
||||
|
||||
#: rbac/tree.py:28 settings/models.py:140
|
||||
#: rbac/tree.py:28 settings/models.py:156
|
||||
msgid "System setting"
|
||||
msgstr "系统设置"
|
||||
|
||||
|
@ -3456,43 +3457,43 @@ msgstr "成功导入 {} 个用户 ( 组织: {} )"
|
|||
msgid "Settings"
|
||||
msgstr "系统设置"
|
||||
|
||||
#: settings/models.py:142
|
||||
#: settings/models.py:158
|
||||
msgid "Can change email setting"
|
||||
msgstr "邮件设置"
|
||||
|
||||
#: settings/models.py:143
|
||||
#: settings/models.py:159
|
||||
msgid "Can change auth setting"
|
||||
msgstr "认证设置"
|
||||
|
||||
#: settings/models.py:144
|
||||
#: settings/models.py:160
|
||||
msgid "Can change system msg sub setting"
|
||||
msgstr "消息订阅设置"
|
||||
|
||||
#: settings/models.py:145
|
||||
#: settings/models.py:161
|
||||
msgid "Can change sms setting"
|
||||
msgstr "短信设置"
|
||||
|
||||
#: settings/models.py:146
|
||||
#: settings/models.py:162
|
||||
msgid "Can change security setting"
|
||||
msgstr "安全设置"
|
||||
|
||||
#: settings/models.py:147
|
||||
#: settings/models.py:163
|
||||
msgid "Can change clean setting"
|
||||
msgstr "定期清理"
|
||||
|
||||
#: settings/models.py:148
|
||||
#: settings/models.py:164
|
||||
msgid "Can change interface setting"
|
||||
msgstr "界面设置"
|
||||
|
||||
#: settings/models.py:149
|
||||
#: settings/models.py:165
|
||||
msgid "Can change license setting"
|
||||
msgstr "许可证设置"
|
||||
|
||||
#: settings/models.py:150
|
||||
#: settings/models.py:166
|
||||
msgid "Can change terminal setting"
|
||||
msgstr "终端设置"
|
||||
|
||||
#: settings/models.py:151
|
||||
#: settings/models.py:167
|
||||
msgid "Can change other setting"
|
||||
msgstr "其它设置"
|
||||
|
||||
|
@ -3605,7 +3606,8 @@ msgstr "用户过滤器"
|
|||
msgid "Choice may be (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"
|
||||
msgstr "用户属性映射"
|
||||
|
||||
|
@ -3629,23 +3631,52 @@ msgstr "搜索分页数量"
|
|||
msgid "Enable LDAP auth"
|
||||
msgstr "启用 LDAP 认证"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:15
|
||||
msgid "Base site url"
|
||||
msgstr "JumpServer 地址"
|
||||
#: settings/serializers/auth/oauth2.py:20
|
||||
msgid "Enable OAuth2 Auth"
|
||||
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"
|
||||
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
|
||||
msgid "Client Secret"
|
||||
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"
|
||||
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
|
||||
msgid "Share session"
|
||||
msgstr "共享会话"
|
||||
|
@ -3678,22 +3709,10 @@ msgstr "启用 OIDC 认证"
|
|||
msgid "Provider endpoint"
|
||||
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
|
||||
msgid "Provider jwks endpoint"
|
||||
msgstr "jwks 端点地址"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:71
|
||||
msgid "Provider userinfo endpoint"
|
||||
msgstr "用户信息端点地址"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:74
|
||||
msgid "Provider end session endpoint"
|
||||
msgstr "注销会话端点地址"
|
||||
|
@ -3726,10 +3745,6 @@ msgstr "使用状态"
|
|||
msgid "Use nonce"
|
||||
msgstr "临时使用"
|
||||
|
||||
#: settings/serializers/auth/oidc.py:92 settings/serializers/auth/saml2.py:33
|
||||
msgid "Always update user"
|
||||
msgstr "总是更新用户信息"
|
||||
|
||||
#: settings/serializers/auth/radius.py:13
|
||||
msgid "Enable Radius Auth"
|
||||
msgstr "启用 Radius 认证"
|
||||
|
@ -5604,7 +5619,7 @@ msgstr "不能和原来的密钥相同"
|
|||
msgid "Not a valid ssh public key"
|
||||
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"
|
||||
msgstr "SSH公钥"
|
||||
|
||||
|
@ -5616,55 +5631,55 @@ msgstr "强制启用"
|
|||
msgid "Local"
|
||||
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"
|
||||
msgstr "服务账号"
|
||||
|
||||
#: users/models/user.py:675
|
||||
#: users/models/user.py:679
|
||||
msgid "Avatar"
|
||||
msgstr "头像"
|
||||
|
||||
#: users/models/user.py:678
|
||||
#: users/models/user.py:682
|
||||
msgid "Wechat"
|
||||
msgstr "微信"
|
||||
|
||||
#: users/models/user.py:695
|
||||
#: users/models/user.py:699
|
||||
msgid "Secret key"
|
||||
msgstr "Secret key"
|
||||
|
||||
#: users/models/user.py:711
|
||||
#: users/models/user.py:715
|
||||
msgid "Source"
|
||||
msgstr "来源"
|
||||
|
||||
#: users/models/user.py:715
|
||||
#: users/models/user.py:719
|
||||
msgid "Date password last updated"
|
||||
msgstr "最后更新密码日期"
|
||||
|
||||
#: users/models/user.py:718
|
||||
#: users/models/user.py:722
|
||||
msgid "Need update password"
|
||||
msgstr "需要更新密码"
|
||||
|
||||
#: users/models/user.py:892
|
||||
#: users/models/user.py:896
|
||||
msgid "Can invite user"
|
||||
msgstr "可以邀请用户"
|
||||
|
||||
#: users/models/user.py:893
|
||||
#: users/models/user.py:897
|
||||
msgid "Can remove user"
|
||||
msgstr "可以移除用户"
|
||||
|
||||
#: users/models/user.py:894
|
||||
#: users/models/user.py:898
|
||||
msgid "Can match user"
|
||||
msgstr "可以匹配用户"
|
||||
|
||||
#: users/models/user.py:903
|
||||
#: users/models/user.py:907
|
||||
msgid "Administrator"
|
||||
msgstr "管理员"
|
||||
|
||||
#: users/models/user.py:906
|
||||
#: users/models/user.py:910
|
||||
msgid "Administrator is the super user of system"
|
||||
msgstr "Administrator是初始的超级管理员"
|
||||
|
||||
#: users/models/user.py:931
|
||||
#: users/models/user.py:935
|
||||
msgid "User password history"
|
||||
msgstr "用户密码历史"
|
||||
|
||||
|
@ -6762,6 +6777,9 @@ msgstr "旗舰版"
|
|||
msgid "Community edition"
|
||||
msgstr "社区版"
|
||||
|
||||
#~ msgid "Logo title"
|
||||
#~ msgstr "图标标题"
|
||||
|
||||
#~ msgid "IP is not allowed"
|
||||
#~ msgstr "来源 IP 不被允许登录"
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ class SettingsApi(generics.RetrieveUpdateAPIView):
|
|||
'cas': serializers.CASSettingSerializer,
|
||||
'sso': serializers.SSOSettingSerializer,
|
||||
'saml2': serializers.SAML2SettingSerializer,
|
||||
'oauth2': serializers.OAuth2SettingSerializer,
|
||||
'clean': serializers.CleaningSerializer,
|
||||
'other': serializers.OtherSettingSerializer,
|
||||
'sms': serializers.SMSSettingSerializer,
|
||||
|
@ -113,9 +114,12 @@ class SettingsApi(generics.RetrieveUpdateAPIView):
|
|||
return data
|
||||
|
||||
def perform_update(self, serializer):
|
||||
post_data_names = list(self.request.data.keys())
|
||||
settings_items = self.parse_serializer_data(serializer)
|
||||
serializer_data = getattr(serializer, 'data', {})
|
||||
for item in settings_items:
|
||||
if item['name'] not in post_data_names:
|
||||
continue
|
||||
changed, setting = Setting.update_or_create(**item)
|
||||
if not changed:
|
||||
continue
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import os
|
||||
import json
|
||||
|
||||
from django.db import models
|
||||
from django.db.utils import ProgrammingError, OperationalError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django.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
|
||||
|
||||
|
@ -118,6 +122,14 @@ class Setting(models.Model):
|
|||
setattr(settings, key, value)
|
||||
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
|
||||
def update_or_create(cls, name='', value='', encrypted=False, category=''):
|
||||
"""
|
||||
|
@ -128,6 +140,10 @@ class Setting(models.Model):
|
|||
changed = False
|
||||
if not setting:
|
||||
setting = Setting(name=name, encrypted=encrypted, category=category)
|
||||
|
||||
if isinstance(value, InMemoryUploadedFile):
|
||||
value = cls.save_to_file(value)
|
||||
|
||||
if setting.cleaned_value != value:
|
||||
setting.encrypted = encrypted
|
||||
setting.cleaned_value = value
|
||||
|
|
|
@ -9,3 +9,4 @@ from .sso import *
|
|||
from .base import *
|
||||
from .sms import *
|
||||
from .saml2 import *
|
||||
from .oauth2 import *
|
||||
|
|
|
@ -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 |
|
@ -628,6 +628,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
|
|||
radius = 'radius', 'Radius'
|
||||
cas = 'cas', 'CAS'
|
||||
saml2 = 'saml2', 'SAML2'
|
||||
oauth2 = 'oauth2', 'OAuth2'
|
||||
|
||||
SOURCE_BACKEND_MAPPING = {
|
||||
Source.local: [
|
||||
|
@ -652,6 +653,9 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, AbstractUser):
|
|||
Source.saml2: [
|
||||
settings.AUTH_BACKEND_SAML2
|
||||
],
|
||||
Source.oauth2: [
|
||||
settings.AUTH_BACKEND_OAUTH2
|
||||
],
|
||||
}
|
||||
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
|
|
|
@ -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.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.decorator import on_transaction_commit
|
||||
from .signals import post_user_create
|
||||
|
@ -26,16 +27,18 @@ def user_authenticated_handle(user, created, source, attrs=None, **kwargs):
|
|||
user.source = source
|
||||
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')
|
||||
logger.debug(
|
||||
"Receive saml2 user updated signal: {}, "
|
||||
"Receive {} user updated signal: {}, "
|
||||
"Update user info: {},"
|
||||
"(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():
|
||||
if key in attr_whitelist and 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)
|
||||
|
||||
|
||||
@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)
|
||||
def on_ldap_create_user(sender, user, ldap_user, **kwargs):
|
||||
if user and user.username not in ['admin']:
|
||||
|
|
|
@ -230,6 +230,8 @@ class LoginIpBlockUtil(BlockGlobalIpUtilBase):
|
|||
|
||||
|
||||
def construct_user_email(username, email, email_suffix=''):
|
||||
if email is None:
|
||||
email = ''
|
||||
if '@' in email:
|
||||
return email
|
||||
if '@' in username:
|
||||
|
|
Loading…
Reference in New Issue