diff --git a/Dockerfile-ee b/Dockerfile-ee index 6ac258b5d..5f4baf4c4 100644 --- a/Dockerfile-ee +++ b/Dockerfile-ee @@ -1,27 +1,5 @@ ARG VERSION=dev -FROM python:3.14-slim-trixie AS gmssl-builder -WORKDIR /app -ARG GMSSL_VERSION=3.1.1 -RUN set -ex \ - && apt-get update \ - && apt-get install -y --no-install-recommends \ - git \ - cmake \ - make \ - gcc \ - g++ \ - ca-certificates \ - && rm -rf /var/lib/apt/lists/* - -RUN set -ex \ - && git clone --branch v${GMSSL_VERSION} https://github.com/guanzhi/GmSSL.git \ - && cd GmSSL \ - && mkdir build \ - && cd build \ - && cmake .. \ - && make \ - && make -j"$(nproc)" \ - && make install +FROM jumpserver/gmssl:3.1.1-trixie AS gmssl-builder FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} AS build-xpack FROM jumpserver/core:${VERSION}-ce @@ -44,8 +22,6 @@ WORKDIR /opt/jumpserver ARG PIP_MIRROR=https://pypi.org/simple -ARG GMSSL_VERSION=3.1.1 - RUN set -ex \ && uv pip install -i${PIP_MIRROR} --group xpack \ && rm -rf /root/.cache/ diff --git a/apps/accounts/serializers/automations/change_secret.py b/apps/accounts/serializers/automations/change_secret.py index e5510b7cc..288be098f 100644 --- a/apps/accounts/serializers/automations/change_secret.py +++ b/apps/accounts/serializers/automations/change_secret.py @@ -13,6 +13,7 @@ from accounts.models import ( ) from accounts.serializers import AuthValidateMixin, PasswordRulesSerializer from assets.models import Asset +from common.serializers import SecretReadableCheckMixin from common.serializers.fields import LabeledChoiceField, ObjectRelatedField from common.utils import get_logger from .base import BaseAutomationSerializer @@ -134,12 +135,13 @@ class ChangeSecretRecordSerializer(serializers.ModelSerializer): return obj.status == ChangeSecretRecordStatusChoice.success -class ChangeSecretRecordViewSecretSerializer(serializers.ModelSerializer): +class ChangeSecretRecordViewSecretSerializer(SecretReadableCheckMixin, serializers.ModelSerializer): class Meta: model = ChangeSecretRecord fields = [ 'id', 'old_secret', 'new_secret', ] + secret_fields = ['old_secret', 'new_secret'] read_only_fields = fields diff --git a/apps/authentication/backends/ukey/backends.py b/apps/authentication/backends/ukey/backends.py index c937951e5..cde2438b4 100644 --- a/apps/authentication/backends/ukey/backends.py +++ b/apps/authentication/backends/ukey/backends.py @@ -7,6 +7,7 @@ import tempfile from django.conf import settings from django.core.exceptions import PermissionDenied +from django.utils.translation import gettext_lazy as _ from users.models import User from common.utils import get_logger @@ -24,6 +25,7 @@ from .exceptions import ( UKeyCertUnsupportedAlgorithmError, ) from .utils import is_sm2_pem +from authentication.errors.const import reason_user_inactive, reason_choices __all__ = ['UKeyBackend'] @@ -45,9 +47,14 @@ class UKeyBackend(JMSBaseAuthBackend): user = self._check_user_and_ukey_sn(username, ukey_sn) cert_pem = self._load_cert_pem(cert) if self._is_sm2_cert(cert_pem): - return self._authenticate_sm2(cert_pem, username, signature, challenge, user) + user = self._authenticate_sm2(cert_pem, username, signature, challenge, user) else: - return self._authenticate_other(cert_pem, username, signature, challenge, user) + user = self._authenticate_other(cert_pem, username, signature, challenge, user) + if self.user_can_authenticate(user): + return user + else: + error = reason_choices[reason_user_inactive] + raise PermissionDenied(error) except Exception as e: if request: request.error_message = str(e) diff --git a/apps/authentication/backends/ukey/vendors/ji_da/sdk_config.yaml b/apps/authentication/backends/ukey/vendors/ji_da/sdk_config.yaml index 77d5e5da8..2815f7ec1 100644 --- a/apps/authentication/backends/ukey/vendors/ji_da/sdk_config.yaml +++ b/apps/authentication/backends/ukey/vendors/ji_da/sdk_config.yaml @@ -297,7 +297,7 @@ operations: url_format: user_id: "{{ user.id }}" body: - ukey_sn: null + ukey_sn: "" register: user - key: issueCert diff --git a/apps/authentication/backends/ukey/vendors/long_mai/sdk_config.yaml b/apps/authentication/backends/ukey/vendors/long_mai/sdk_config.yaml index 39028feff..3cb850247 100644 --- a/apps/authentication/backends/ukey/vendors/long_mai/sdk_config.yaml +++ b/apps/authentication/backends/ukey/vendors/long_mai/sdk_config.yaml @@ -359,7 +359,7 @@ operations: url_format: user_id: "{{ user.id }}" body: - ukey_sn: null + ukey_sn: "" register: user - key: issueCert diff --git a/apps/authentication/backends/ukey/views.py b/apps/authentication/backends/ukey/views.py index 23048f70c..e85ba9271 100644 --- a/apps/authentication/backends/ukey/views.py +++ b/apps/authentication/backends/ukey/views.py @@ -5,7 +5,6 @@ from urllib.parse import urlencode from django.conf import settings from django.contrib.auth import authenticate -from django.contrib import messages from django.core.cache import cache from django.utils.decorators import method_decorator from django.utils.translation import gettext as _ @@ -27,6 +26,7 @@ from .sdk import ukey_sdk_config __all__ = ['UKeyLoginView'] _CHALLENGE_CACHE_KEY_PREFIX = 'ukey_login_challenge' +_UKEY_ERROR_SESSION_KEY = 'ukey_login_error' @method_decorator(sensitive_post_parameters(), name='dispatch') @method_decorator(csrf_protect, name='dispatch') @@ -79,6 +79,7 @@ class UKeyLoginView(AuthMixin, FormView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['challenge'] = self._generate_and_store_challenge() + context['error_msg'] = self.request.session.pop(_UKEY_ERROR_SESSION_KEY, '') return context def form_valid(self, form): @@ -155,7 +156,7 @@ class UKeyLoginView(AuthMixin, FormView): return field_name def get_failed_response(self, form, username, error_msg): - messages.error(self.request, error_msg) + self.request.session[_UKEY_ERROR_SESSION_KEY] = str(error_msg or _('Unknown')) self.send_auth_signal(success=False, reason=error_msg, username=username) return redirect(self._build_login_redirect_url()) diff --git a/apps/authentication/templates/authentication/auth_fail_flash_message_standalone.html b/apps/authentication/templates/authentication/auth_fail_flash_message_standalone.html index 1ac57cd92..c3ab04d87 100644 --- a/apps/authentication/templates/authentication/auth_fail_flash_message_standalone.html +++ b/apps/authentication/templates/authentication/auth_fail_flash_message_standalone.html @@ -56,7 +56,7 @@ } function redirect_page() { - if (time >= 0) { + if (time > 0) { var msg = message + ', ' + time + ' ...'; $('#messages').html(msg); time--; diff --git a/apps/authentication/templates/authentication/login_ukey.html b/apps/authentication/templates/authentication/login_ukey.html index bb21be15e..c26758a74 100644 --- a/apps/authentication/templates/authentication/login_ukey.html +++ b/apps/authentication/templates/authentication/login_ukey.html @@ -202,11 +202,9 @@
{% csrf_token %} - {% if messages %} + {% if error_msg %}
- {% for message in messages %} -

{% trans "Error" %}: {{ message }}

- {% endfor %} +

{% trans "Error" %}: {{ error_msg }}

{% endif %} diff --git a/apps/i18n/lina/en.json b/apps/i18n/lina/en.json index fef6ea2f7..2f900a488 100644 --- a/apps/i18n/lina/en.json +++ b/apps/i18n/lina/en.json @@ -198,6 +198,7 @@ "Audits": "Audits", "AuditsDashboard": "Audits dashboard", "Auth": "Authentication", + "Author": "Author", "AuthConfig": "Authentication", "AuthIntegration": "Auth Integration", "AuthLimit": "Login restriction", diff --git a/apps/i18n/lina/es.json b/apps/i18n/lina/es.json index f471fb273..5c8a748c9 100644 --- a/apps/i18n/lina/es.json +++ b/apps/i18n/lina/es.json @@ -200,6 +200,7 @@ "Audits": "panel de auditoría", "AuditsDashboard": "Panel de auditoría", "Auth": "Configuración de autenticación", + "Author": "Autor", "AuthConfig": "Configuración de autenticación", "AuthIntegration": "Integración de certificación", "AuthLimit": "Restricción de inicio de sesión", diff --git a/apps/i18n/lina/ja.json b/apps/i18n/lina/ja.json index 7d62f466e..b3666d17b 100644 --- a/apps/i18n/lina/ja.json +++ b/apps/i18n/lina/ja.json @@ -202,6 +202,7 @@ "Audits": "監査台", "AuditsDashboard": "監査ダッシュボード", "Auth": "認証設定", + "Author": "作成者", "AuthConfig": "資格認定構成", "AuthIntegration": "認証統合", "AuthLimit": "ログイン制限", diff --git a/apps/i18n/lina/ko.json b/apps/i18n/lina/ko.json index b614d70b7..c32fad9e1 100644 --- a/apps/i18n/lina/ko.json +++ b/apps/i18n/lina/ko.json @@ -200,6 +200,7 @@ "Audits": "감사 대시보드", "AuditsDashboard": "감사 대시보드", "Auth": "인증 설정", + "Author": "작성자", "AuthConfig": "인증 구성", "AuthIntegration": "인증 통합", "AuthLimit": "로그인 제한", diff --git a/apps/i18n/lina/pt_br.json b/apps/i18n/lina/pt_br.json index 1c5b1bcac..dc25cabf4 100644 --- a/apps/i18n/lina/pt_br.json +++ b/apps/i18n/lina/pt_br.json @@ -200,6 +200,7 @@ "Audits": "Auditório", "AuditsDashboard": "Painel de Auditoria", "Auth": "Configurações de autenticação", + "Author": "Autor", "AuthConfig": "Configurar Autenticação", "AuthIntegration": "Integração de Certificação", "AuthLimit": "Restrições de login", diff --git a/apps/i18n/lina/ru.json b/apps/i18n/lina/ru.json index 181ee9729..d995f53f9 100644 --- a/apps/i18n/lina/ru.json +++ b/apps/i18n/lina/ru.json @@ -200,6 +200,7 @@ "Audits": "Аудит", "AuditsDashboard": "Панель аудита", "Auth": "Аутентификация", + "Author": "Автор", "AuthConfig": "Настройка аутентификации", "AuthIntegration": "Интеграция аутентификации", "AuthLimit": "Ограничение входа", diff --git a/apps/i18n/lina/vi.json b/apps/i18n/lina/vi.json index 4f8a5b417..89fd66176 100644 --- a/apps/i18n/lina/vi.json +++ b/apps/i18n/lina/vi.json @@ -200,6 +200,7 @@ "Audits": "Bảng kiểm tra", "AuditsDashboard": "Bảng điều khiển kiểm toán", "Auth": "Cài đặt xác thực", + "Author": "Tác giả", "AuthConfig": "Cấu hình xác thực", "AuthIntegration": "Tích hợp xác thực", "AuthLimit": "Giới hạn đăng nhập", diff --git a/apps/i18n/lina/zh.json b/apps/i18n/lina/zh.json index 5c68fd079..29a55a0af 100644 --- a/apps/i18n/lina/zh.json +++ b/apps/i18n/lina/zh.json @@ -197,6 +197,7 @@ "Audits": "审计台", "AuditsDashboard": "审计台仪表盘", "Auth": "认证设置", + "Author": "作者", "AuthConfig": "配置认证", "AuthIntegration": "认证集成", "AuthLimit": "登录限制", diff --git a/apps/i18n/lina/zh_hant.json b/apps/i18n/lina/zh_hant.json index 11acf923e..7e905de6b 100644 --- a/apps/i18n/lina/zh_hant.json +++ b/apps/i18n/lina/zh_hant.json @@ -202,6 +202,7 @@ "Audits": "審計台", "AuditsDashboard": "審計台儀表盤", "Auth": "認證設置", + "Author": "作者", "AuthConfig": "配寘認證資訊", "AuthIntegration": "認證整合", "AuthLimit": "登入限制", diff --git a/apps/notifications/api/site_msgs.py b/apps/notifications/api/site_msgs.py index 48f9068fc..3f5b45257 100644 --- a/apps/notifications/api/site_msgs.py +++ b/apps/notifications/api/site_msgs.py @@ -1,6 +1,7 @@ from rest_framework.decorators import action from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.response import Response +from django.shortcuts import get_object_or_404 from common.api import JMSGenericViewSet from common.const.http import GET, PATCH, POST @@ -11,6 +12,8 @@ from ..serializers import ( SiteMessageSendSerializer, ) from ..site_msg import SiteMessageUtil +from ..models import MessageContent + __all__ = ('SiteMessageViewSet',) @@ -59,5 +62,14 @@ class SiteMessageViewSet(ListModelMixin, RetrieveModelMixin, JMSGenericViewSet): def send(self, request, **kwargs): s = self.get_serializer(data=request.data) s.is_valid(raise_exception=True) - SiteMessageUtil.send_msg(**s.validated_data, sender=request.user) + site_msg = SiteMessageUtil.send_msg(**s.validated_data, sender=request.user) + if site_msg: + return Response({'detail': 'ok', 'site_msg_id': str(site_msg.id)}) + else: + return Response({'detail': 'error'}) + + @action(methods=[PATCH], detail=True, permission_classes=[OnlySuperUser,]) + def revoke(self, request, **kwargs): + msg = get_object_or_404(MessageContent, id=kwargs['pk']) + msg.revoke_msg() return Response({'detail': 'ok'}) diff --git a/apps/notifications/models/site_msg.py b/apps/notifications/models/site_msg.py index 5de243339..04e56fd6e 100644 --- a/apps/notifications/models/site_msg.py +++ b/apps/notifications/models/site_msg.py @@ -52,8 +52,15 @@ class MessageContent(JMSBaseModel): return { 'id': str(self.id), 'subject': self.subject, + 'is_broadcast': self.is_broadcast, 'message': self.message, 'display_mode': self.display_mode, 'date_created': str(self.date_created), 'sender': str(self.sender) if self.sender else '' } + + def revoke_msg(self): + if not self.is_broadcast: + return + self.is_broadcast = False + self.save() diff --git a/apps/notifications/site_msg.py b/apps/notifications/site_msg.py index 4318b24cc..ae7da92ff 100644 --- a/apps/notifications/site_msg.py +++ b/apps/notifications/site_msg.py @@ -41,6 +41,7 @@ class SiteMessageUtil: site_msg.users.add(*user_ids) # 只有调用 save 才能触发 post_save 信号 site_msg.save() + return site_msg @classmethod def get_user_all_msgs(cls, user_id): diff --git a/apps/templates/flash_message_standalone.html b/apps/templates/flash_message_standalone.html index 97a75e67a..4269f71e7 100644 --- a/apps/templates/flash_message_standalone.html +++ b/apps/templates/flash_message_standalone.html @@ -44,7 +44,7 @@ {% block custom_foot_js %}