新增 POST /api/admin/notices、PATCH /api/admin/notices/{id}

保留超级管理员权限边界
支持草稿、发布、置顶、自动摘要
HTML 净化,拦截脚本和危险链接
公告更新与审计日志同事务提交
新增开关 ADMIN_NATIVE_NOTICE_WRITES_ENABLED=false
管理端公告列表和系统自动公告暂时仍由 Node 处理
This commit is contained in:
2026-07-23 08:41:53 +08:00 Unverified
parent 3665aa3aa9
commit 9e21a277f0
14 changed files with 350 additions and 21 deletions
+44
View File
@@ -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 {