perf: 修改翻译

pull/12928/head
ibuler 2024-03-11 14:33:14 +08:00
parent 6f37cc4d01
commit 327cdc8604
17 changed files with 138 additions and 66 deletions

View File

@ -28,7 +28,7 @@ class Migration(migrations.Migration):
('org_id',
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')),
('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')),
('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')),
('types', models.JSONField(default=list)),

View File

@ -20,7 +20,7 @@ class Migration(migrations.Migration):
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')),
('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')),
('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),

View File

@ -24,7 +24,7 @@ class Migration(migrations.Migration):
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')),
('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')),
('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')),
('accounts', models.JSONField(default=list, verbose_name='Accounts')),

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:16cedef767e949250b792d7f5921071b001545fc17cff4be093cd5faf5a20176
size 2453
oid sha256:d25379bd2019d09bc9608414a5516b3d19fac2f5a829f3dd845ffeb194599669
size 2535

View File

@ -2021,11 +2021,11 @@ msgstr ""
#: assets/models/platform.py:107 assets/serializers/platform.py:166
msgid "Su enabled"
msgstr ""
msgstr "Switch enabled"
#: assets/models/platform.py:108 assets/serializers/platform.py:144
msgid "Su method"
msgstr ""
msgstr "Switch method"
#: assets/models/platform.py:109 assets/serializers/platform.py:147
msgid "Custom fields"

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:aa826a4ec69f3ea3fb9250affe699f452857201b8e48065038479b9b88d9bfc1
size 167853

View File

@ -4247,7 +4247,7 @@ msgstr "利用可能なプログラムポータルがありません"
#: ops/mixin.py:23 ops/mixin.py:104 settings/serializers/auth/ldap.py:66
#, fuzzy
#| msgid "Periodic perform"
#| msgid "Periodic run"
msgid "Periodic run"
msgstr "定期的なパフォーマンス"

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:da4f0f84c01c061cbab1d2fff69f4cd84edb4bbb5088c83a83789a3149575b8f
size 138961

View File

@ -440,8 +440,8 @@
"Expired": "Expiration Date",
"Export": "Export",
"ExportAll": "Export All",
"ExportOnlyFiltered": "Export Search Results Only",
"ExportOnlySelectedItems": "Export selected Items Only",
"ExportOnlyFiltered": "Export filtered items",
"ExportOnlySelectedItems": "Export selected items",
"ExportRange": "Export Range",
"FC": "Fusion Compute",
"Failed": "Failed",
@ -826,7 +826,7 @@
"Receivers": "Receiver",
"RecentLogin": "Recent Login",
"RecentSession": "Recent sessions",
"RecentlyUsed": "Recently Used",
"RecentlyUsed": "Recently",
"RecipientHelpText": "If both recipient A and B are set, the account's key will be split into two parts",
"RecipientServer": "Receiving Server",
"Reconnect": "Reconnect",

View File

@ -59,7 +59,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('interval', models.IntegerField(blank=True, help_text='Units: seconds', null=True, verbose_name='Interval')),
('crontab', models.CharField(blank=True, help_text='5 * * * *', max_length=128, null=True, verbose_name='Crontab')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')),
('callback', models.CharField(blank=True, max_length=128, null=True, verbose_name='Callback')),
('is_deleted', models.BooleanField(default=False)),
('comment', models.TextField(blank=True, verbose_name='Comment')),

View File

@ -60,7 +60,7 @@ class Migration(migrations.Migration):
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('org_id',
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')),
('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')),
('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')),
('name', models.CharField(max_length=128, null=True, verbose_name='Name')),
@ -164,7 +164,7 @@ class Migration(migrations.Migration):
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
('org_id',
models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic perform')),
('is_periodic', models.BooleanField(default=False, verbose_name='Periodic run')),
('interval', models.IntegerField(blank=True, default=24, null=True, verbose_name='Interval')),
('crontab', models.CharField(blank=True, max_length=128, null=True, verbose_name='Crontab')),
('name', models.CharField(max_length=128, null=True, verbose_name='Name')),

View File

@ -22,12 +22,10 @@ class PeriodTaskModelMixin(models.Model):
)
is_periodic = models.BooleanField(default=False, verbose_name=_("Periodic run"))
interval = models.IntegerField(
default=24, null=True, blank=True,
verbose_name=_("Interval"),
default=24, null=True, blank=True, verbose_name=_("Interval"),
)
crontab = models.CharField(
blank=True, max_length=128,
verbose_name=_("Crontab"),
blank=True, max_length=128, null=True, verbose_name=_("Crontab"),
)
@abc.abstractmethod

View File

@ -10,6 +10,7 @@ from django.utils.translation import gettext_lazy as _
from common.db.models import JMSBaseModel
from common.utils import signer, get_logger
from .signals import setting_changed
logger = get_logger(__name__)
@ -84,6 +85,7 @@ class Setting(models.Model):
if not item:
return
item.refresh_setting()
setting_changed.send(sender=cls, name=name, item=item)
def refresh_setting(self):
setattr(settings, self.name, self.cleaned_value)

View File

@ -1,3 +1,4 @@
from django.dispatch import Signal
category_setting_updated = Signal()
setting_changed = Signal()

View File

@ -31,7 +31,7 @@ from ..signals import (
post_user_change_password, post_user_leave_org, pre_user_leave_org
)
__all__ = ['User', 'UserPasswordHistory']
__all__ = ['User', 'UserPasswordHistory', ]
logger = get_logger(__file__)
@ -737,20 +737,25 @@ class JSONFilterMixin:
return models.Q(id__in=user_id)
class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterMixin, AbstractUser):
class Source(models.TextChoices):
local = 'local', _('Local')
ldap = 'ldap', 'LDAP/AD'
openid = 'openid', 'OpenID'
radius = 'radius', 'Radius'
cas = 'cas', 'CAS'
saml2 = 'saml2', 'SAML2'
oauth2 = 'oauth2', 'OAuth2'
wecom = 'wecom', _('WeCom')
dingtalk = 'dingtalk', _('DingTalk')
feishu = 'feishu', _('FeiShu')
slack = 'slack', _('Slack')
custom = 'custom', 'Custom'
class Source(models.TextChoices):
local = 'local', _('Local')
ldap = 'ldap', 'LDAP/AD'
openid = 'openid', 'OpenID'
radius = 'radius', 'Radius'
cas = 'cas', 'CAS'
saml2 = 'saml2', 'SAML2'
oauth2 = 'oauth2', 'OAuth2'
wecom = 'wecom', _('WeCom')
dingtalk = 'dingtalk', _('DingTalk')
feishu = 'feishu', _('FeiShu')
slack = 'slack', _('Slack')
custom = 'custom', 'Custom'
class SourceMixin:
source: str
_source_choices = []
Source = Source
SOURCE_BACKEND_MAPPING = {
Source.local: [
@ -793,6 +798,61 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM
]
}
@classmethod
def get_sources_enabled(cls):
mapper = {
cls.Source.local: True,
cls.Source.ldap: settings.AUTH_LDAP,
cls.Source.openid: settings.AUTH_OPENID,
cls.Source.radius: settings.AUTH_RADIUS,
cls.Source.cas: settings.AUTH_CAS,
cls.Source.saml2: settings.AUTH_SAML2,
cls.Source.oauth2: settings.AUTH_OAUTH2,
cls.Source.wecom: settings.AUTH_WECOM,
cls.Source.feishu: settings.AUTH_FEISHU,
cls.Source.slack: settings.AUTH_SLACK,
cls.Source.dingtalk: settings.AUTH_DINGTALK,
cls.Source.custom: settings.AUTH_CUSTOM
}
return [str(k) for k, v in mapper.items() if v]
@property
def source_display(self):
return self.get_source_display()
@property
def is_local(self):
return self.source == self.Source.local.value
@classmethod
def get_source_choices(cls):
if cls._source_choices:
return cls._source_choices
used = cls.objects.values_list('source', flat=True).order_by('source').distinct()
enabled_sources = cls.get_sources_enabled()
_choices = []
for k, v in cls.Source.choices:
if k in enabled_sources or k in used:
_choices.append((k, v))
cls._source_choices = _choices
return cls._source_choices
@classmethod
def get_user_allowed_auth_backend_paths(cls, username):
if not settings.ONLY_ALLOW_AUTH_FROM_SOURCE or not username:
return None
user = cls.objects.filter(username=username).first()
if not user:
return None
return user.get_allowed_auth_backend_paths()
def get_allowed_auth_backend_paths(self):
if not settings.ONLY_ALLOW_AUTH_FROM_SOURCE:
return None
return self.SOURCE_BACKEND_MAPPING.get(self.source, [])
class User(AuthMixin, SourceMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterMixin, AbstractUser):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
username = models.CharField(
max_length=128, unique=True, verbose_name=_('Username')
@ -842,7 +902,6 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM
)
created_by = models.CharField(max_length=30, default='', blank=True, verbose_name=_('Created by'))
updated_by = models.CharField(max_length=30, default='', blank=True, verbose_name=_('Updated by'))
source = models.CharField(max_length=30, default=Source.local, choices=Source.choices, verbose_name=_('Source'))
date_password_last_updated = models.DateTimeField(
auto_now_add=True, blank=True, null=True,
verbose_name=_('Date password last updated')
@ -850,13 +909,16 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM
need_update_password = models.BooleanField(
default=False, verbose_name=_('Need update password')
)
date_api_key_last_used = models.DateTimeField(null=True, blank=True, verbose_name=_('Date api key used'))
date_updated = models.DateTimeField(auto_now=True, verbose_name=_('Date updated'))
source = models.CharField(
max_length=30, default=Source.local,
choices=Source.choices, verbose_name=_('Source')
)
wecom_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('WeCom'))
dingtalk_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('DingTalk'))
feishu_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('FeiShu'))
slack_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('Slack'))
date_api_key_last_used = models.DateTimeField(null=True, blank=True, verbose_name=_('Date api key used'))
date_updated = models.DateTimeField(auto_now=True, verbose_name=_('Date updated'))
DATE_EXPIRED_WARNING_DAYS = 5
def __str__(self):
@ -888,10 +950,6 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM
def get_absolute_url(self):
return reverse('users:user-detail', args=(self.id,))
@property
def source_display(self):
return self.get_source_display()
@property
def is_expired(self):
if self.date_expired and self.date_expired < timezone.now():
@ -899,6 +957,12 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM
else:
return False
def is_password_authenticate(self):
cas = self.Source.cas
saml2 = self.Source.saml2
oauth2 = self.Source.oauth2
return self.source not in [cas, saml2, oauth2]
@property
def expired_remain_days(self):
date_remain = self.date_expired - timezone.now()
@ -917,16 +981,6 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM
return True
return False
@property
def is_local(self):
return self.source == self.Source.local.value
def is_password_authenticate(self):
cas = self.Source.cas
saml2 = self.Source.saml2
oauth2 = self.Source.oauth2
return self.source not in [cas, saml2, oauth2]
def set_required_attr_if_need(self):
if not self.name:
self.name = self.username
@ -985,20 +1039,6 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM
raise PermissionDenied(_('Can not delete admin user'))
return super(User, self).delete(using=using, keep_parents=keep_parents)
@classmethod
def get_user_allowed_auth_backend_paths(cls, username):
if not settings.ONLY_ALLOW_AUTH_FROM_SOURCE or not username:
return None
user = cls.objects.filter(username=username).first()
if not user:
return None
return user.get_allowed_auth_backend_paths()
def get_allowed_auth_backend_paths(self):
if not settings.ONLY_ALLOW_AUTH_FROM_SOURCE:
return None
return self.SOURCE_BACKEND_MAPPING.get(self.source, [])
class Meta:
ordering = ['username']
verbose_name = _("User")

View File

@ -87,7 +87,7 @@ class UserSerializer(RolesSerializerMixin, ResourceLabelsMixin, CommonBulkModelS
default=PasswordStrategy.email,
allow_null=True,
required=False,
label=_("Password strategy"),
label=_("Password option"),
)
mfa_enabled = serializers.BooleanField(read_only=True, label=_("MFA enabled"))
mfa_force_enabled = serializers.BooleanField(
@ -182,6 +182,16 @@ class UserSerializer(RolesSerializerMixin, ResourceLabelsMixin, CommonBulkModelS
'mfa_level': {'label': _("MFA level")},
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_source_options()
def set_source_options(self):
field = self.fields.get("source")
if not field:
return
field.choices = User.get_source_choices()
def validate_password(self, password):
password_strategy = self.initial_data.get("password_strategy")
if self.instance is None and password_strategy != PasswordStrategy.custom:

View File

@ -15,9 +15,11 @@ from authentication.backends.oidc.signals import openid_create_or_update_user
from authentication.backends.saml2.signals import saml2_create_or_update_user
from common.const.crontab import CRONTAB_AT_PM_TWO
from common.decorators import on_transaction_commit
from common.signals import django_ready
from common.utils import get_logger
from jumpserver.utils import get_current_request
from ops.celery.decorator import register_as_period_task
from settings.signals import setting_changed
from .models import User, UserPasswordHistory
from .signals import post_user_create
@ -173,3 +175,16 @@ def clean_expired_user_session_period():
def user_logged_out_callback(sender, request, user, **kwargs):
session_key = request.session.session_key
UserSession.objects.filter(key=session_key).delete()
@receiver(setting_changed)
@on_transaction_commit
def on_auth_setting_changed_clear_source_choice(sender, name='', **kwargs):
print("Receive setting changed signal: {}".format(name))
if name.startswith('AUTH_'):
User._source_choices = []
@receiver(django_ready)
def on_django_ready_refresh_source(sender, **kwargs):
User._source_choices = []