Skip to content
Merged

Beta #10

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
80bea25
Merge branch 'alpha' into beta
trustedinster May 16, 2026
194ee8c
feat(deploy): 新增交互式.env环境变量配置功能
trustedinster May 16, 2026
95fbbd2
Merge branch 'alpha' into beta
trustedinster May 16, 2026
f3d9be3
refactor(deploy): 重构交互式部署脚本的配置流程
trustedinster May 16, 2026
32bf6e1
refactor: 完成多维度代码优化与功能增强
trustedinster May 20, 2026
fa6bf92
feat: H端初始化命令生成与证书自动配置
trustedinster May 23, 2026
baeca4e
fix: auto-create host when uploading cert with unassociated token
trustedinster May 23, 2026
8678c8c
refactor: store cert data on token instead of auto-creating host
trustedinster May 23, 2026
ac3eb96
fix: move @csrf_exempt and @require_http_methods from helper to uploa…
trustedinster May 23, 2026
8e03cec
fix: add cert_uploaded SSE status for when host not yet created
trustedinster May 23, 2026
6a63465
fix: SSE inner loop also checks cert_data for host=None case
trustedinster May 23, 2026
8e50a96
fix: ensure init_token value is set in hidden input before form submit
trustedinster May 23, 2026
7a3e283
fix: add warning log when cert files missing in test_connection
trustedinster May 23, 2026
46c7473
fix: move init_token hidden input outside template x-if, use :value b…
trustedinster May 23, 2026
4c22041
simplify: remove init_token form dependency, auto-associate pending c…
trustedinster May 23, 2026
c4b52f3
fix: dynamically create init_token input in onSubmit instead of Alpin…
trustedinster May 23, 2026
839868c
feat: 完成证书管理与验证码系统重构
trustedinster May 30, 2026
8a0a09a
Merge pull request #7 from 2c2a/alpha
trustedinster May 30, 2026
338a48a
Update collectstatic.yml
trustedinster May 30, 2026
8089b50
Update collectstatic.yml
trustedinster May 30, 2026
6cb7ad6
Update security_middleware.py
trustedinster May 30, 2026
40b90b1
Remove unnecessary imports and fix CSP syntax
trustedinster May 30, 2026
682f3c6
Update security_middleware.py
trustedinster May 30, 2026
822ed51
fix(hosts): use host port instead of fixed 5986 for winrm connection
trustedinster May 30, 2026
377a1d3
Merge pull request #8 from 2c2a/alpha
trustedinster May 30, 2026
182c939
fix(hosts): use host port instead of fixed 5986 for winrm connection
trustedinster May 30, 2026
8206bc1
Merge pull request #9 from 2c2a/alpha
trustedinster May 30, 2026
8cd80a1
Merge branch 'master' into beta
trustedinster May 30, 2026
5dba5c4
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
1c6d243
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
ddd0698
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
1a38326
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
0c14369
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
1cf50b6
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
cc81337
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
412d1d5
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
914123b
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
1977e14
Potential fix for pull request finding 'Empty except'
trustedinster May 30, 2026
5736d2c
Potential fix for pull request finding 'CodeQL / Information exposure…
trustedinster May 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,18 @@ WINRM_RETRY_COUNT=3

# ========== Gateway 配置 ==========
GATEWAY_ENABLED=False
GATEWAY_CONTROL_SOCKET=/run/zasca/control.sock
GATEWAY_CONTROL_SOCKET=/run/2c2a/control.sock

# ========== Beta数据库配置(Beta推送插件) ==========
# 配置后可将生产数据推送到Beta版本数据库,支持MySQL和PostgreSQL架构
# 配置后可将生产数据推送到Beta版本数据库,仅支持PostgreSQL架构
#BETA_DB_NAME=zasca_beta
#BETA_DB_USER=root
#BETA_DB_USER=postgres
#BETA_DB_PASSWORD=your_beta_database_password_here
#BETA_DB_HOST=127.0.0.1
#BETA_DB_PORT=3306
#BETA_DB_PORT=5432
# Beta环境的SECRET_KEY(用于重加密:生产密钥解密 → Beta密钥加密)
# 若不配置则直接复制密文,Beta端需使用与生产相同的SECRET_KEY才能解密
#BETA_SECRET_KEY=

# ========== Bootstrap 认证配置 ==========
BOOTSTRAP_SHARED_SALT=
40 changes: 20 additions & 20 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -661,12 +661,12 @@ For more information, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.


ZASCA PLUGIN EXCEPTION TO THE
2c2a PLUGIN EXCEPTION TO THE
GNU AFFERO GENERAL PUBLIC LICENSE VERSION 3

This Plugin Exception ("Exception") is an additional permission under
Section 7 of the GNU Affero General Public License, Version 3 ("AGPLv3").
It applies to the software known as ZASCA (the "Program"). This Exception
It applies to the software known as 2c2a (the "Program"). This Exception
modifies the AGPLv3 as it applies to the Program by providing an exception
for qualifying Plugins, as defined below.

Expand Down Expand Up @@ -876,35 +876,35 @@ the following notices:
a prominent comment at the beginning of the file containing:

(i) A statement identifying the work as a Plugin for the
ZASCA program;
2c2a program;
(ii) The Plugin's own license terms; and
(iii) The following declaration:

"This is a plugin for the ZASCA program. ZASCA is licensed
"This is a plugin for the 2c2a program. 2c2a is licensed
under the GNU Affero General Public License, Version 3
(AGPLv3). This plugin is NOT part of ZASCA and is NOT
licensed under the AGPLv3. The AGPLv3 applies to ZASCA
itself, not to this plugin. This plugin uses the ZASCA
(AGPLv3). This plugin is NOT part of 2c2a and is NOT
licensed under the AGPLv3. The AGPLv3 applies to 2c2a
itself, not to this plugin. This plugin uses the 2c2a
Plugin Exception to the AGPLv3 for its interaction with
ZASCA."
2c2a."

(b) Documentation Notice. Any documentation, README, or
distribution materials for the Plugin must prominently state:

(i) That the work is a Plugin for the ZASCA program;
(ii) That ZASCA is licensed under the AGPLv3;
(iii) That the Plugin is NOT part of ZASCA and is NOT
subject to the AGPLv3 by virtue of the ZASCA Plugin
(i) That the work is a Plugin for the 2c2a program;
(ii) That 2c2a is licensed under the AGPLv3;
(iii) That the Plugin is NOT part of 2c2a and is NOT
subject to the AGPLv3 by virtue of the 2c2a Plugin
Exception;
(iv) The Plugin's own license terms; and
(v) That the AGPLv3 applies to ZASCA itself, not to the
(v) That the AGPLv3 applies to 2c2a itself, not to the
Plugin.

(c) Runtime Notice. If the Plugin provides any user-facing
interface (including but not limited to: web pages, API
responses, CLI output, or log messages), it must display or
include a notice indicating that it is a Plugin for ZASCA and
that ZASCA's license (AGPLv3) does not apply to the Plugin.
include a notice indicating that it is a Plugin for 2c2a and
that 2c2a's license (AGPLv3) does not apply to the Plugin.

(d) Visual Interface Attribution. In addition to the source code
and documentation notices required by subsections (a) and (b),
Expand All @@ -926,7 +926,7 @@ the following notices:
HTML source code, comments, or developer tools. The
label must include at minimum the Plugin's name and a
statement that the content is provided by a third-party
Plugin (e.g., "Provided by [Plugin Name] (ZASCA
Plugin (e.g., "Provided by [Plugin Name] (2c2a
Plugin)").

(ii) Injected UI Panels and Widgets. Any panel, widget,
Expand All @@ -951,14 +951,14 @@ the following notices:
mechanism must include a visible notice (such as a
footer, header badge, or sidebar indicator) stating
that the page is provided by the Plugin and is not part
of ZASCA's core interface.
of 2c2a's core interface.

(v) General Principle. The overarching requirement is that
no UI content injected by a Plugin may appear to the
end user as though it is a native part of the ZASCA
end user as though it is a native part of the 2c2a
program. The attribution must be sufficient for a
typical user to distinguish Plugin-provided UI elements
from ZASCA's core interface elements. Minimal or
from 2c2a's core interface elements. Minimal or
hard-to-notice attributions (such as light-colored
small text, hidden metadata, or code comments) do not
satisfy this requirement. The attribution must be
Expand Down Expand Up @@ -1012,7 +1012,7 @@ the following notices:

5. SCOPE AND LIMITATIONS

(a) This Exception applies only to the Program known as ZASCA, as
(a) This Exception applies only to the Program known as 2c2a, as
identified by its copyright holders. It does not apply to any
other software licensed under the AGPLv3.

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

![2c2a Logo](./docs/images/logo.svg)

<h1>2c2a - Zero Agent Security Control Architecture</h1>
<h1>2c2a -
Cloudy Computer Account Activation Integration Platform</h1>

<p>
<strong>基于 Django 的企业级 Windows 主机远程管理平台</strong><br>
Expand Down
75 changes: 14 additions & 61 deletions apps/accounts/captcha_service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import logging
from typing import Tuple, Optional
from django.http import HttpRequest
from . import geetest_utils, captcha_utils

logger = logging.getLogger(__name__)

Expand All @@ -22,15 +21,11 @@ def validate_captcha(
) -> Tuple[bool, Optional[str]]:
from apps.dashboard.models import SystemConfig

provider, _, _ = SystemConfig.get_config().get_captcha_config(scene=scene)
provider = SystemConfig.get_config().get_captcha_config(scene=scene)

try:
if provider == 'geetest':
return CaptchaService._validate_geetest(request)
elif provider == 'turnstile':
return CaptchaService._validate_turnstile(request)
elif provider == 'local':
return CaptchaService._validate_local_captcha(request)
if provider == 'tianai':
return CaptchaService._validate_tianai(request)
else:
logger.debug(f"No captcha validation required for scene '{scene}', provider: {provider}")
return True, None
Expand All @@ -40,65 +35,23 @@ def validate_captcha(
return False, e.message

@staticmethod
def _validate_geetest(request: HttpRequest) -> Tuple[bool, Optional[str]]:
lot_number = request.POST.get('lot_number')
captcha_output = request.POST.get('captcha_output')
pass_token = request.POST.get('pass_token')
gen_time = request.POST.get('gen_time')
captcha_id = request.POST.get('captcha_id')

if not all([lot_number, captcha_output, pass_token, gen_time]):
logger.warning(f"Geetest validation failed: missing parameters - lot_number={lot_number}, captcha_output={captcha_output}, pass_token={pass_token}, gen_time={gen_time}")
raise CaptchaValidationError('请完成验证码验证')

ok, resp = geetest_utils.verify_geetest_v4(
lot_number, captcha_output, pass_token, gen_time, captcha_id=captcha_id
)

if not ok:
logger.warning(f"Geetest validation failed: {resp}")
raise CaptchaValidationError('验证码校验失败')

logger.info("Geetest validation succeeded")
return True, None

@staticmethod
def _validate_turnstile(request: HttpRequest) -> Tuple[bool, Optional[str]]:
tf_token = request.POST.get('cf-turnstile-response') or request.POST.get('turnstile_token')

if not tf_token:
logger.warning("Turnstile validation failed: missing token")
raise CaptchaValidationError('请完成 Turnstile 验证')
def _validate_tianai(request: HttpRequest) -> Tuple[bool, Optional[str]]:
token = request.POST.get('captcha_token')

ok, resp = geetest_utils.verify_turnstile(
tf_token, remoteip=request.META.get('REMOTE_ADDR')
)

if not ok:
logger.warning(f"Turnstile validation failed: {resp}")
raise CaptchaValidationError('Turnstile 验证失败')

logger.info("Turnstile validation succeeded")
return True, None

@staticmethod
def _validate_local_captcha(request: HttpRequest) -> Tuple[bool, Optional[str]]:
lot_number = request.POST.get('lot_number')
captcha_input = request.POST.get('captcha_output')

if not all([lot_number, captcha_input]):
logger.warning(f"Local captcha validation failed: missing parameters - lot_number={lot_number}, captcha_input={captcha_input}")
if not token:
logger.warning("Tianai captcha validation failed: missing token")
raise CaptchaValidationError('请完成验证码验证')

is_valid = captcha_utils.verify_captcha(
lot_number, captcha_input, consume=True, check_attempts=True
)
from django_tianai_captcha.conf import get_captcha_application
app = get_captcha_application()

is_valid = app.secondary_verification(token)

if not is_valid:
logger.warning(f"Local captcha validation failed: lot_number={lot_number}")
raise CaptchaValidationError('本地验证码校验失败')
logger.warning("Tianai captcha secondary verification failed")
raise CaptchaValidationError('验证码校验失败')

logger.info(f"Local captcha validation succeeded: lot_number={lot_number}")
logger.info("Tianai captcha validation succeeded")
return True, None


Expand Down
Loading
Loading