forked from wangziqi/gongxue-base
feat: add object storage signing providers
This commit is contained in:
@@ -11,6 +11,7 @@ const TENANT_OPERATOR_USER_ID = '00000000-0000-0000-0000-000000000103';
|
||||
const TENANT_SALES_USER_ID = '00000000-0000-0000-0000-000000000104';
|
||||
const TENANT_AGENT_USER_ID = '00000000-0000-0000-0000-000000000105';
|
||||
const START_SERVER = process.argv.includes('--start-server');
|
||||
const ENABLE_REAL_STORAGE_SIGN_TESTS = process.env.ENABLE_REAL_STORAGE_SIGN_TESTS === 'true';
|
||||
|
||||
const ids = {
|
||||
region: '00000000-0000-0000-0000-000000000301',
|
||||
@@ -633,6 +634,81 @@ async function testTenantContentAssetsAndImports() {
|
||||
});
|
||||
assert.equal(upload.assetDraft?.assetType, 'pdf', 'upload signer should return asset draft');
|
||||
assert.ok(upload.upload?.objectKey?.includes(MAIN_TENANT_ID), 'upload object key should be tenant scoped');
|
||||
assert.equal(upload.upload?.signatureMode, 'local-placeholder', 'local signer should be explicit in test env');
|
||||
assert.equal(upload.upload?.headers?.['content-type'], 'application/pdf', 'upload signer should bind content-type');
|
||||
assert.ok(!JSON.stringify(upload).includes('ACCESS_KEY'), 'upload signer must not leak cloud credentials');
|
||||
|
||||
if (ENABLE_REAL_STORAGE_SIGN_TESTS) {
|
||||
const tencentUpload = await request('/api/tenant-content/assets/sign-upload', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: {
|
||||
fileName: 'cos-resource.pdf',
|
||||
assetType: 'pdf',
|
||||
storageProvider: 'tencent_cos',
|
||||
bucket: process.env.TEST_TENCENT_COS_BUCKET || 'tenant-assets',
|
||||
objectKey: `${MAIN_TENANT_ID}/pdf/cos-resource.pdf`,
|
||||
mimeType: 'application/pdf',
|
||||
},
|
||||
});
|
||||
assert.ok(tencentUpload.upload?.url?.includes('q-sign-algorithm=sha1'), 'Tencent COS signed URL should expose XML V5 query params');
|
||||
assert.ok(!tencentUpload.upload?.url?.includes('SecretKey'), 'Tencent COS signed URL must not leak SecretKey');
|
||||
}
|
||||
|
||||
const unsafeUpload = await request('/api/tenant-content/assets/sign-upload', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: {
|
||||
fileName: 'unsafe.pdf',
|
||||
assetType: 'pdf',
|
||||
storageProvider: 'local_dev',
|
||||
objectKey: '../escape.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
},
|
||||
expectStatus: 400,
|
||||
});
|
||||
assert.equal(unsafeUpload.code, 'INVALID_OBJECT_KEY', 'upload signer should reject path traversal object keys');
|
||||
|
||||
const crossTenantUpload = await request('/api/tenant-content/assets/sign-upload', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: {
|
||||
fileName: 'cross-tenant.pdf',
|
||||
assetType: 'pdf',
|
||||
storageProvider: 'local_dev',
|
||||
objectKey: `${PARTNER_TENANT_ID}/pdf/cross-tenant.pdf`,
|
||||
mimeType: 'application/pdf',
|
||||
},
|
||||
expectStatus: 400,
|
||||
});
|
||||
assert.equal(crossTenantUpload.code, 'OBJECT_KEY_TENANT_PREFIX_REQUIRED', 'upload object key should stay in current tenant prefix');
|
||||
|
||||
const deniedMimeUpload = await request('/api/tenant-content/assets/sign-upload', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: {
|
||||
fileName: 'script.html',
|
||||
assetType: 'document',
|
||||
storageProvider: 'local_dev',
|
||||
mimeType: 'text/html',
|
||||
},
|
||||
expectStatus: 400,
|
||||
});
|
||||
assert.equal(deniedMimeUpload.code, 'MIME_TYPE_NOT_ALLOWED', 'upload signer should reject unsupported mime types');
|
||||
|
||||
const deniedLargeUpload = await request('/api/tenant-content/assets/sign-upload', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: {
|
||||
fileName: 'huge-video.mp4',
|
||||
assetType: 'video',
|
||||
storageProvider: 'local_dev',
|
||||
mimeType: 'video/mp4',
|
||||
fileSizeBytes: 1024 * 1024 * 1024,
|
||||
},
|
||||
expectStatus: 400,
|
||||
});
|
||||
assert.equal(deniedLargeUpload.code, 'FILE_TOO_LARGE', 'upload signer should enforce max upload size');
|
||||
|
||||
const asset = await request('/api/tenant-content/assets', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
@@ -679,6 +755,50 @@ async function testTenantContentAssetsAndImports() {
|
||||
assert.equal(download.item?.id, asset.item.id, 'svip student should receive asset download');
|
||||
assert.equal(download.access?.svip, true, 'asset download should report svip access');
|
||||
assert.equal(download.download?.url, 'https://example.test/resources/integration.pdf', 'external asset download should use cdn url');
|
||||
assert.equal(download.download?.signatureMode, 'public-or-provider-managed', 'external asset should be marked provider managed');
|
||||
|
||||
const localAsset = await request('/api/tenant-content/assets', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
title: '集成测试本地对象资料',
|
||||
assetType: 'pdf',
|
||||
storageProvider: 'local_dev',
|
||||
bucket: upload.assetDraft.bucket,
|
||||
objectKey: upload.assetDraft.objectKey,
|
||||
fileName: upload.assetDraft.fileName,
|
||||
mimeType: upload.assetDraft.mimeType,
|
||||
fileSizeBytes: upload.assetDraft.fileSizeBytes,
|
||||
visibility: 'tenant',
|
||||
status: 'active',
|
||||
},
|
||||
});
|
||||
assert.equal(localAsset.item?.objectKey, upload.assetDraft.objectKey, 'asset upsert should keep normalized object key');
|
||||
|
||||
const localDownload = await request('/api/catalog/assets/download', {
|
||||
query: { assetId: localAsset.item.id },
|
||||
});
|
||||
assert.equal(localDownload.download?.method, 'GET', 'object asset download should sign GET');
|
||||
assert.equal(localDownload.download?.signatureMode, 'local-placeholder', 'local object asset should use local placeholder signer');
|
||||
assert.ok(localDownload.download?.url?.includes(encodeURIComponent(localAsset.item.bucket)), 'object asset download should include bucket');
|
||||
|
||||
const invalidObjectAsset = await request('/api/tenant-content/assets', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'PUT',
|
||||
body: {
|
||||
title: '非法对象键',
|
||||
assetType: 'pdf',
|
||||
storageProvider: 'local_dev',
|
||||
bucket: 'tenant-assets',
|
||||
objectKey: `${PARTNER_TENANT_ID}/pdf/unsafe.pdf`,
|
||||
fileName: 'unsafe.pdf',
|
||||
mimeType: 'application/pdf',
|
||||
visibility: 'tenant',
|
||||
status: 'active',
|
||||
},
|
||||
expectStatus: 400,
|
||||
});
|
||||
assert.equal(invalidObjectAsset.code, 'OBJECT_KEY_TENANT_PREFIX_REQUIRED', 'asset upsert should reject cross-tenant object keys');
|
||||
|
||||
const partnerAssetList = await request('/api/tenant-content/assets', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
|
||||
Reference in New Issue
Block a user