perf: update zip and agreement

This commit is contained in:
ibuler
2026-02-02 18:49:12 +08:00
parent c847caabaa
commit f529be12d8
11 changed files with 330 additions and 20 deletions

View File

@@ -0,0 +1,129 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>User Agreement & Privacy Policy</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
body {
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
line-height: 1.7;
color: #333;
max-width: 960px;
margin: 0 auto;
padding: 24px;
background: #fff;
}
h1,
h2,
h3 {
color: #111;
margin-top: 1.6em;
}
h1 {
font-size: 26px;
border-bottom: 2px solid #eee;
padding-bottom: 8px;
}
h2 {
font-size: 20px;
}
p,
li {
font-size: 14px;
}
ul {
padding-left: 20px;
}
.lang {
margin-top: 48px;
padding-top: 24px;
border-top: 2px dashed #ddd;
}
.note {
background: #f8f9fa;
border-left: 4px solid #409eff;
padding: 12px 16px;
margin: 16px 0;
font-size: 13px;
}
</style>
</head>
<body>
<!-- ================= ENGLISH ================= -->
<div>
<h1>User Agreement & Privacy Policy</h1>
<p>
<strong>Version:</strong> 2026-01<br />
<strong>Effective Date:</strong> Feb 1, 2026
</p>
<h2>1. User Agreement</h2>
<h3>1.1 Product Nature</h3>
<p>
This system is an enterprise-grade system deployed in a private environment. All
deployment environments and data are fully controlled by the customer.
</p>
<h3>1.2 User Obligations</h3>
<ul>
<li>Use the system only within authorized scope;</li>
<li>Do not perform illegal or unauthorized activities;</li>
<li>Do not bypass or interfere with security controls and audit mechanisms.</li>
</ul>
<h3>1.3 Account Security</h3>
<p>
Users are responsible for safeguarding their credentials. Risks caused by improper
credential management shall be borne by the user or customer.
</p>
<h2>2. Privacy Policy</h2>
<h3>2.1 Scope</h3>
<p>
This policy applies to the privately deployed version of this system. All personal
data is stored within the customer's own environment. The software provider does not
access, store, or transmit such data.
</p>
<h3>2.2 Personal Information We Process</h3>
<ul>
<li>Username</li>
<li>Phone number</li>
<li>Email address</li>
</ul>
<h3>2.3 Authentication Information</h3>
<p>
Passwords are never stored in plaintext. Only irreversible encrypted password hashes
are stored for authentication.
</p>
<h3>2.4 Facial Recognition Data</h3>
<p>
Facial recognition is an optional premium feature and is disabled by default. If
enabled, facial image or feature data will be processed solely for identity
verification.
</p>
<p>
Such data is considered sensitive personal information and is stored only within the
customer's local environment.
</p>
<h3>2.5 Cookies and Similar Technologies</h3>
<p>
Necessary cookies and session identifiers are used to maintain login sessions and
system security. These technologies are not used for advertising or cross-site
tracking.
</p>
<h3>2.6 Policy Updates</h3>
<p>Material changes to this policy will require renewed user consent.</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,165 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>用户协议与隐私政策</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
body {
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
line-height: 1.7;
color: #333;
max-width: 960px;
margin: 0 auto;
padding: 24px;
background: #fff;
}
h1,
h2,
h3 {
color: #111;
margin-top: 1.6em;
}
h1 {
font-size: 26px;
border-bottom: 2px solid #eee;
padding-bottom: 8px;
}
h2 {
font-size: 20px;
}
p,
li {
font-size: 14px;
}
ul {
padding-left: 20px;
}
.lang {
margin-top: 48px;
padding-top: 24px;
border-top: 2px dashed #ddd;
}
.note {
background: #f8f9fa;
border-left: 4px solid #409eff;
padding: 12px 16px;
margin: 16px 0;
font-size: 13px;
}
</style>
</head>
<body>
<div>
<h1>用户协议与隐私政策</h1>
<p>
<strong>版本号</strong>2026-01<br />
<strong>生效日期</strong>2026-02-01
</p>
<div class="note">本软件为企业级私有化部署系统仅供客户内部授权人员使用</div>
<h2>用户协议</h2>
<h3>1. 产品性质</h3>
<p>
本系统为企业级运维与安全管理系统
采用私有化部署方式运行系统的部署环境运行环境及数据均由客户自行控制
</p>
<h3>2. 用户行为规范</h3>
<ul>
<li>用户应在客户授权范围内使用本系统</li>
<li>不得利用本系统从事违法违规或未授权行为</li>
<li>不得破坏规避或干扰系统的安全审计与控制机制</li>
</ul>
<h3>3. 账号与凭证安全</h3>
<p>
用户应妥善保管自身账号及认证凭证
因用户个人原因导致的账号安全风险由用户或客户自行承担
</p>
<h3>4. 协议变更</h3>
<p>如本协议内容发生实质性变更系统将提示用户重新确认</p>
<hr />
<h2>隐私政策</h2>
<h3>1. 适用范围</h3>
<p>
本隐私政策适用于本系统私有化部署版本 系统所产生的数据均存储于客户自有环境中
软件提供方不接触不存储不回传客户业务数据或个人信息
</p>
<h3>2. 我们收集和使用的个人信息</h3>
<h4>2.1 基本账号信息</h4>
<ul>
<li>用户名</li>
<li>手机号码</li>
<li>电子邮箱</li>
</ul>
<p>用于账号管理身份识别与安全通知</p>
<h4>2.2 身份认证信息</h4>
<p>系统不会以明文形式存储用户密码 仅以不可逆加密方式保存密码摘要用于身份校验</p>
<h4>2.3 日志与审计信息</h4>
<ul>
<li>登录时间登录 IP</li>
<li>用户操作审计日志</li>
</ul>
<p>用于安全审计合规留痕与问题追溯</p>
<h3>3. 敏感个人信息的处理人脸识别</h3>
<p>人脸识别功能为可选增值服务默认不开启 仅在客户或用户主动启用后生效</p>
<p>
如启用该功能系统将采集并处理用户的人脸图像或特征信息
用于身份认证与防止账号冒用
</p>
<p>
上述信息属于敏感个人信息 仅存储和处理于客户自有部署环境中
不会传输至软件提供方服务器
</p>
<p>用户或客户管理员可随时关闭人脸识别功能 并删除已采集的人脸信息</p>
<h3>4. Cookie 与同类技术</h3>
<p>
为保障系统正常运行登录状态维持及安全防护 系统会在用户设备中存储必要的 Cookie
或会话标识 SessionCSRF Token
</p>
<p>上述技术仅用于系统功能实现 不用于广告投放或跨站追踪</p>
<h3>5. 数据存储位置与期限</h3>
<p>系统中的个人信息均存储于客户自有环境中 具体保存期限由客户根据自身管理要求决定</p>
<h3>6. 信息共享转让与披露</h3>
<p>
软件提供方不会共享转让或披露系统中的个人信息
但法律法规或司法机关另有要求的除外
</p>
<h3>7. 用户权利</h3>
<ul>
<li>查询更正个人信息</li>
<li>删除个人信息在权限允许范围内</li>
<li>关闭或删除人脸识别信息</li>
</ul>
<h3>8. 安全措施</h3>
<ul>
<li>加密存储与传输</li>
<li>访问控制与权限管理</li>
<li>操作审计与日志留存</li>
</ul>
<h3>9. 隐私政策更新</h3>
<p>如本隐私政策发生实质性变更 系统将要求用户重新确认后方可继续使用</p>
</div>
</body>
</html>

View File

@@ -84,5 +84,6 @@ urlpatterns = [
path('captcha/', include('captcha.urls')),
path('oauth2-provider/', include(('authentication.backends.oauth2_provider.urls', 'authentication'), namespace='oauth2-provider'))
path('oauth2-provider/', include(('authentication.backends.oauth2_provider.urls', 'authentication'), namespace='oauth2-provider')),
path('agreement/', views.AgreementView.as_view(), name='agreement'),
]

View File

@@ -7,3 +7,4 @@ from .login import *
from .mfa import *
from .slack import *
from .wecom import *
from .agreement import *

View File

@@ -0,0 +1,13 @@
from django.views.generic import TemplateView
from django.utils.translation import get_language
__all__ = ['AgreementView']
class AgreementView(TemplateView):
def get_template_names(self):
current_lang = get_language() or 'zh-cn'
if current_lang.startswith('zh'):
return 'authentication/agreement_zh.html'
else:
return 'authentication/agreement.html'

View File

@@ -2,6 +2,7 @@ import os
import stat
import hashlib
from pathlib import Path
from rest_framework.exceptions import ValidationError
from zipfile import ZipFile, BadZipFile
@@ -14,7 +15,7 @@ MAX_TOTAL_SIZE = 1 * 1024 * 1024 * 1024 # 1GB
MAX_COMPRESSION_RATIO = 100 # 解压 / 压缩
class ZipSecurityError(Exception):
class ZipSecurityError(ValidationError):
pass

View File

@@ -1639,5 +1639,9 @@
"AccountSecretReadDisabled": "Account secret reading has been disabled by administrator",
"AccessToken": "Access tokens",
"AccessTokenTip": "Access Token is a temporary credential generated through the OAuth2 (Authorization Code Grant) flow using the JumpServer client, which is used to access protected resources.",
"Revoke": "Revoke"
}
"Revoke": "Revoke",
"ReadAgreeTo": "I have read and agree to the",
"TermsOfService": "Terms of Service",
"PrivacyPolicy": "Privacy Policy",
"and": "and"
}

View File

@@ -1650,5 +1650,9 @@
"AccessTokenTip": "访问令牌是通过 JumpServer 客户端使用 OAuth2授权码授权流程生成的临时凭证用于访问受保护的资源。",
"Revoke": "撤销",
"AccountSecretReadDisabled": "账号密码读取功能已被管理员禁用",
"Automations": "自动化"
}
"Automations": "自动化",
"ReadAgreeTo": "我已阅读并同意",
"TermsOfService": "服务条款",
"PrivacyPolicy": "隐私政策",
"and": "和"
}

View File

@@ -1,6 +1,5 @@
import os
import shutil
import zipfile
from django.conf import settings
from django.core.exceptions import SuspiciousFileOperation
@@ -13,6 +12,7 @@ from common.api.generic import JMSBulkModelViewSet
from common.exceptions import JMSException
from common.permissions import IsOwnerOrAdminWritable
from common.utils.http import is_true
from common.utils.zip import safe_extract_zip
from rbac.permissions import RBACPermission
from ..const import Scope
from ..exception import PlaybookNoValidEntry
@@ -27,9 +27,7 @@ from django.utils._os import safe_join
def unzip_playbook(src, dest):
fz = zipfile.ZipFile(src, 'r')
for file in fz.namelist():
fz.extract(file, dest)
safe_extract_zip(src, dest)
class PlaybookViewSet(JMSBulkModelViewSet):

View File

@@ -2,7 +2,6 @@ import os
import os.path
import re
import shutil
import zipfile
from typing import Callable
from django.conf import settings
@@ -22,6 +21,7 @@ from common.utils import is_uuid
from common.utils.http import is_true
from terminal import serializers
from terminal.models import AppletPublication, Applet
from common.utils.zip import safe_extract_zip
__all__ = ['AppletViewSet', 'AppletPublicationViewSet']
@@ -47,10 +47,7 @@ class DownloadUploadMixin:
shutil.rmtree(extract_to)
try:
with zipfile.ZipFile(path) as zp:
if zp.testzip() is not None:
raise ValidationError({'error': _('Invalid zip file')})
zp.extractall(extract_to)
safe_extract_zip(path, extract_to)
except RuntimeError as e:
raise ValidationError({'error': _('Invalid zip file') + ': {}'.format(e)})

View File

@@ -1,6 +1,5 @@
import os.path
import shutil
import zipfile
from typing import Callable
from django.core.files.storage import default_storage
@@ -16,6 +15,7 @@ from common.api import JMSBulkModelViewSet
from common.serializers import FileSerializer
from terminal import serializers
from terminal.models import VirtualAppPublication, VirtualApp
from common.utils.zip import safe_extract_zip
__all__ = ['VirtualAppViewSet', 'VirtualAppPublicationViewSet']
@@ -38,10 +38,7 @@ class UploadMixin:
if os.path.exists(extract_to):
shutil.rmtree(extract_to)
try:
with zipfile.ZipFile(path) as zp:
if zp.testzip() is not None:
raise ValidationError({'error': _('Invalid zip file')})
zp.extractall(extract_to)
safe_extract_zip(path, extract_to)
except RuntimeError as e:
raise ValidationError({'error': _('Invalid zip file') + ': {}'.format(e)})
tmp_dir = safe_join(extract_to, file.name.replace('.zip', ''))