新增 POST /api/admin/notices、PATCH /api/admin/notices/{id}
保留超级管理员权限边界 支持草稿、发布、置顶、自动摘要 HTML 净化,拦截脚本和危险链接 公告更新与审计日志同事务提交 新增开关 ADMIN_NATIVE_NOTICE_WRITES_ENABLED=false 管理端公告列表和系统自动公告暂时仍由 Node 处理
This commit is contained in:
1 parent
3665aa3aa9
commit
9e21a277f0
14 files changed
+350
-21
No files matched your search
@@ -33,6 +33,8 @@ ADMIN_NATIVE_ORGANIZATION_WRITES_ENABLED=false
|
||||
ADMIN_NATIVE_ACCOUNT_BATCHES_ENABLED=false
|
||||
# 报名号规则与审批流程定义维护;必须同时启用管理端只读接口。
|
||||
ADMIN_NATIVE_CONFIGURATION_ENABLED=false
|
||||
# 手工通知公告创建和修改;公告列表及系统公示暂时仍由 Node 提供。
|
||||
ADMIN_NATIVE_NOTICE_WRITES_ENABLED=false
|
||||
|
||||
# 仅在首次创建空数据库时使用。部署前务必修改初始密码。
|
||||
INITIAL_ADMIN_USERNAME=admin
|
||||
|
||||
+5
-4
@@ -12,7 +12,7 @@
|
||||
- [x] 招生公示与 HMAC 文书验真公开接口
|
||||
- [x] 登录、自主注册、Session 与 TOTP(兼容开关默认关闭)
|
||||
- [x] 考生业务
|
||||
- [ ] 管理后台、审批流和考务编排(管理端读取、组织维护、批量报名号审批及流程配置已原生化)
|
||||
- [ ] 管理后台、审批流和考务编排(管理端读取、组织维护、批量报名号审批、流程配置及手工公告写入已原生化)
|
||||
- [x] 考生志愿填报与招生录取查询
|
||||
- [ ] Excel、文书和缓存
|
||||
- [ ] 容器入口切换及 Node.js 后端移除
|
||||
@@ -54,9 +54,9 @@ $env:AUTH_NATIVE_ENABLED = 'true'
|
||||
$env:CANDIDATE_NATIVE_ENABLED = 'true'
|
||||
```
|
||||
|
||||
管理后台第一批只读接口(管理上下文、仪表盘、学校、学校组织、管理员和考试列表)已经原生化,并保留超级、校级、班级管理员的权限与数据作用域。第二批覆盖学校、班级和管理员的创建与维护、管理员密码重置及自主注册开关;更新操作与审计日志在同一事务中提交,停用或重置管理员会同步失效其会话。第三批覆盖批量报名号申领的读取、提交和审批,终审会按照当前号码规则原子生成考生账号、初始密码和待补录资料。第四批覆盖报名号规则及审批流程定义的读取与维护,并保留流程层级和批量申领终审约束。
|
||||
管理后台第一批只读接口(管理上下文、仪表盘、学校、学校组织、管理员和考试列表)已经原生化,并保留超级、校级、班级管理员的权限与数据作用域。第二批覆盖学校、班级和管理员的创建与维护、管理员密码重置及自主注册开关;更新操作与审计日志在同一事务中提交,停用或重置管理员会同步失效其会话。第三批覆盖批量报名号申领的读取、提交和审批,终审会按照当前号码规则原子生成考生账号、初始密码和待补录资料。第四批覆盖报名号规则及审批流程定义的读取与维护,并保留流程层级和批量申领终审约束。第五批覆盖超级管理员创建和编辑手工公告,包含 HTML 净化、草稿发布状态及事务内审计;管理端公告列表与系统自动发布内容暂时仍由 Node 处理。
|
||||
|
||||
其余审批流和考务编排接口仍转发给 Node,因此两个管理端开关都要求原生认证和共享 Redis;组织维护开关还必须与只读开关一起启用:
|
||||
其余审批流和考务编排接口仍转发给 Node,因此管理端开关都要求原生认证和共享 Redis;各写入子功能还必须与只读开关一起启用:
|
||||
|
||||
```powershell
|
||||
$env:AUTH_NATIVE_ENABLED = 'true'
|
||||
@@ -64,9 +64,10 @@ $env:ADMIN_NATIVE_READS_ENABLED = 'true'
|
||||
$env:ADMIN_NATIVE_ORGANIZATION_WRITES_ENABLED = 'true'
|
||||
$env:ADMIN_NATIVE_ACCOUNT_BATCHES_ENABLED = 'true'
|
||||
$env:ADMIN_NATIVE_CONFIGURATION_ENABLED = 'true'
|
||||
$env:ADMIN_NATIVE_NOTICE_WRITES_ENABLED = 'true'
|
||||
```
|
||||
|
||||
`GET /health/migration` 的 `administration.nativeReadsEnabled`、`administration.nativeOrganizationWritesEnabled`、`administration.nativeAccountBatchesEnabled`、`administration.nativeConfigurationEnabled` 和 `administration.nativeRoutes` 会报告这些端点是否已切换。
|
||||
`GET /health/migration` 的 `administration.nativeReadsEnabled`、`administration.nativeOrganizationWritesEnabled`、`administration.nativeAccountBatchesEnabled`、`administration.nativeConfigurationEnabled`、`administration.nativeNoticeWritesEnabled` 和 `administration.nativeRoutes` 会报告这些端点是否已切换。
|
||||
|
||||
完整的宿主、静态资源、JSON 转发和 Session Cookie 冒烟测试:
|
||||
|
||||
|
||||
@@ -426,6 +426,7 @@ try {
|
||||
ADMIN_NATIVE_ORGANIZATION_WRITES_ENABLED = 'true'
|
||||
ADMIN_NATIVE_ACCOUNT_BATCHES_ENABLED = 'true'
|
||||
ADMIN_NATIVE_CONFIGURATION_ENABLED = 'true'
|
||||
ADMIN_NATIVE_NOTICE_WRITES_ENABLED = 'true'
|
||||
ADMIN_NATIVE_ALLOW_MEMORY = 'true'
|
||||
LegacyNode__Enabled = 'true'
|
||||
LegacyNode__BaseUrl = $legacyBaseUrl
|
||||
@@ -953,6 +954,48 @@ try {
|
||||
$restoreSettingBody = @{ enabled = $originalSelfRegistration } | ConvertTo-Json -Compress
|
||||
Invoke-RestMethod -Uri "$nativeAuthBaseUrl/api/admin/settings/self-registration" -Method Put -ContentType 'application/json' -Body $restoreSettingBody -WebSession $nativeSession | Out-Null
|
||||
|
||||
$noticeCreateForbidden = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/notices" -Method Post -ContentType 'application/json' -Body (@{ title = '越权公告'; content = '<p>无权发布</p>' } | ConvertTo-Json -Compress) -WebSession $nativeSchoolSession -SkipHttpErrorCheck
|
||||
if ($noticeCreateForbidden.StatusCode -ne 403 -or $noticeCreateForbidden.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
|
||||
throw 'Native notice creation did not preserve the super-admin boundary'
|
||||
}
|
||||
$invalidNotice = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/notices" -Method Post -ContentType 'application/json' -Body (@{ title = '无正文公告'; content = '<script>alert(1)</script>' } | ConvertTo-Json -Compress) -WebSession $nativeSession -SkipHttpErrorCheck
|
||||
if ($invalidNotice.StatusCode -ne 400 -or $invalidNotice.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
|
||||
throw 'Native notice creation accepted content that became empty after sanitization'
|
||||
}
|
||||
$noticeCreateBody = @{
|
||||
title = '原生迁移公告'
|
||||
summary = ''
|
||||
content = '<p>公告<strong>正文</strong><script>alert(1)</script></p><a href="javascript:alert(1)">危险链接</a>'
|
||||
category = '迁移公告'
|
||||
pinned = $true
|
||||
status = 'draft'
|
||||
} | ConvertTo-Json -Compress
|
||||
$noticeCreateResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/notices" -Method Post -ContentType 'application/json' -Body $noticeCreateBody -WebSession $nativeSession
|
||||
$createdNativeNotice = ($noticeCreateResponse.Content | ConvertFrom-Json).notice
|
||||
if ($noticeCreateResponse.StatusCode -ne 201 -or $noticeCreateResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core') {
|
||||
throw 'Native notice creation did not use ASP.NET Core'
|
||||
}
|
||||
if ($createdNativeNotice.status -ne 'draft' -or $null -ne $createdNativeNotice.publishAt -or $createdNativeNotice.summary -ne '公告正文 危险链接') {
|
||||
throw 'Native notice creation did not preserve draft state or derive its summary'
|
||||
}
|
||||
if ($createdNativeNotice.content -match '(?i)<script|javascript:') {
|
||||
throw 'Native notice creation returned unsafe HTML'
|
||||
}
|
||||
$noticePublishBody = @{ title = '原生迁移公告(已发布)'; status = 'published' } | ConvertTo-Json -Compress
|
||||
$noticePublishResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/admin/notices/$($createdNativeNotice.id)" -Method Patch -ContentType 'application/json' -Body $noticePublishBody -WebSession $nativeSession
|
||||
$publishedNativeNotice = ($noticePublishResponse.Content | ConvertFrom-Json).notice
|
||||
if ($noticePublishResponse.StatusCode -ne 200 -or $noticePublishResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core' -or $publishedNativeNotice.status -ne 'published' -or -not $publishedNativeNotice.publishAt) {
|
||||
throw 'Native notice update did not publish the notice'
|
||||
}
|
||||
$nativePublicNoticeResponse = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/public/notices/$($createdNativeNotice.id)"
|
||||
$nativePublicNotice = ($nativePublicNoticeResponse.Content | ConvertFrom-Json).notice
|
||||
if ($nativePublicNoticeResponse.Headers['X-EIS-Implementation'] -ne 'aspnet-core' -or $nativePublicNotice.title -ne '原生迁移公告(已发布)') {
|
||||
throw 'Native public notice endpoint could not read the newly published notice'
|
||||
}
|
||||
if ($nativePublicNotice.content -match '(?i)<script|javascript:') {
|
||||
throw 'Native public notice endpoint exposed unsafe HTML from an administrative write'
|
||||
}
|
||||
|
||||
$adminCandidateRoute = Invoke-WebRequest -Uri "$nativeAuthBaseUrl/api/candidate/profile" -WebSession $nativeSession -SkipHttpErrorCheck
|
||||
if ($adminCandidateRoute.StatusCode -ne 403) {
|
||||
throw 'Native candidate API did not enforce the candidate role boundary'
|
||||
@@ -1039,6 +1082,7 @@ try {
|
||||
NativeAdminOrganizationWrites = 'passed'
|
||||
NativeAdminAccountBatches = 'passed'
|
||||
NativeAdminConfiguration = 'passed'
|
||||
NativeAdminNoticeWrites = 'passed'
|
||||
} | Format-List
|
||||
}
|
||||
finally {
|
||||
|
||||
Loaded 3 of 14 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user