fix(overseas): support projected authorization headers
This commit is contained in:
@@ -40,6 +40,7 @@ spring:
|
|||||||
connect-timeout: 3000
|
connect-timeout: 3000
|
||||||
read-timeout: 5000
|
read-timeout: 5000
|
||||||
server:
|
server:
|
||||||
|
max-http-request-header-size: ${SERVER_MAX_HTTP_REQUEST_HEADER_SIZE:32KB}
|
||||||
shutdown: graceful
|
shutdown: graceful
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ x-overseas-common: &overseas-common
|
|||||||
DEPLOYMENT_CELL: ${OVERSEAS_DEPLOYMENT_CELL:-test-overseas}
|
DEPLOYMENT_CELL: ${OVERSEAS_DEPLOYMENT_CELL:-test-overseas}
|
||||||
DATA_RESIDENCY: OVERSEAS
|
DATA_RESIDENCY: OVERSEAS
|
||||||
SPRING_SQL_INIT_MODE: never
|
SPRING_SQL_INIT_MODE: never
|
||||||
|
SERVER_MAX_HTTP_REQUEST_HEADER_SIZE: ${SERVER_MAX_HTTP_REQUEST_HEADER_SIZE:-32KB}
|
||||||
TZ: Asia/Shanghai
|
TZ: Asia/Shanghai
|
||||||
logging:
|
logging:
|
||||||
# 应用只写 stdout/stderr;json-file 兼容宝塔查看,大小与数量均受控。
|
# 应用只写 stdout/stderr;json-file 兼容宝塔查看,大小与数量均受控。
|
||||||
|
|||||||
+12
@@ -9,6 +9,9 @@ import java.nio.file.Path;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class OverseasSecurityValidationDeploymentContractTest {
|
class OverseasSecurityValidationDeploymentContractTest {
|
||||||
|
private static final String HEADER_SIZE =
|
||||||
|
"max-http-request-header-size: ${SERVER_MAX_HTTP_REQUEST_HEADER_SIZE:32KB}";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void gatewayLocalJwtDefaultsMatchAuthAndLastleg() throws Exception {
|
void gatewayLocalJwtDefaultsMatchAuthAndLastleg() throws Exception {
|
||||||
String gatewayApplication = read("gateway-overseas/src/main/resources/application.yml");
|
String gatewayApplication = read("gateway-overseas/src/main/resources/application.yml");
|
||||||
@@ -64,6 +67,15 @@ class OverseasSecurityValidationDeploymentContractTest {
|
|||||||
"OVERSEAS_SECURITY_SERVICE_URL", "OVERSEAS_SECURITY_EVENT_CONSUMER_URL");
|
"OVERSEAS_SECURITY_SERVICE_URL", "OVERSEAS_SECURITY_EVENT_CONSUMER_URL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void overseasAuthorizationSnapshotHeadersFitEveryServletService() throws Exception {
|
||||||
|
assertThat(read("config/nacos/overseas/baiyue-common.yml")).contains(HEADER_SIZE);
|
||||||
|
assertThat(read("lastleg-service/src/main/resources/application.yml")).contains(HEADER_SIZE);
|
||||||
|
assertThat(read("warehouse-overseas-service/src/main/resources/application.yml")).contains(HEADER_SIZE);
|
||||||
|
assertThat(read("deploy/docker-compose.overseas.yml"))
|
||||||
|
.contains("SERVER_MAX_HTTP_REQUEST_HEADER_SIZE: ${SERVER_MAX_HTTP_REQUEST_HEADER_SIZE:-32KB}");
|
||||||
|
}
|
||||||
|
|
||||||
private static String read(String relative) throws Exception {
|
private static String read(String relative) throws Exception {
|
||||||
Path path = Path.of(relative);
|
Path path = Path.of(relative);
|
||||||
if (!Files.exists(path)) path = Path.of("..").resolve(relative);
|
if (!Files.exists(path)) path = Path.of("..").resolve(relative);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
server:
|
server:
|
||||||
port: ${LASTLEG_PORT:9303}
|
port: ${LASTLEG_PORT:9303}
|
||||||
|
max-http-request-header-size: ${SERVER_MAX_HTTP_REQUEST_HEADER_SIZE:32KB}
|
||||||
spring:
|
spring:
|
||||||
servlet:
|
servlet:
|
||||||
multipart:
|
multipart:
|
||||||
|
|||||||
@@ -37,62 +37,95 @@ UPDATE ll_security_projection
|
|||||||
SET authorities_json = JSON_ARRAY()
|
SET authorities_json = JSON_ARRAY()
|
||||||
WHERE authorities_json IS NULL;
|
WHERE authorities_json IS NULL;
|
||||||
|
|
||||||
-- 9000000001 是本地平台自定义管理员账号;权限来源以 IAM 当前角色授权为准。
|
-- 从 IAM 当前平台成员重建 USER 与 AUTHORIZATION 投影,避免把修复绑定到某个环境账号 ID。
|
||||||
INSERT INTO ll_security_projection (
|
INSERT INTO ll_security_projection (
|
||||||
aggregate_type, aggregate_id, tenant_id, security_status,
|
aggregate_type, aggregate_id, tenant_id, security_status,
|
||||||
permission_version, roles_json, authorities_json, aggregate_version,
|
permission_version, roles_json, authorities_json, aggregate_version,
|
||||||
payload_hash, last_event_id, changed_at, updated_at
|
payload_hash, last_event_id, changed_at, updated_at
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
'AUTHORIZATION', '9000000001', 0, NULL,
|
'USER', CAST(u.auth_user_id AS CHAR), 0,
|
||||||
COALESCE((SELECT MAX(permission_version)
|
IF(u.status = 'ACTIVE' AND m.status = 'ACTIVE', 'ACTIVE', 'DISABLED'),
|
||||||
FROM baiyue_iam.iam_security_subject_version
|
COALESCE(v.permission_version, 0), JSON_ARRAY(), JSON_ARRAY(),
|
||||||
WHERE auth_user_id = 9000000001), 0),
|
COALESCE(v.permission_version, 0),
|
||||||
(SELECT JSON_ARRAYAGG(role_code)
|
SHA2(CONCAT('environment-repair:', u.auth_user_id, ':user:', COALESCE(v.permission_version, 0)), 256),
|
||||||
FROM (
|
CONCAT('repair-', u.auth_user_id, '-u-', DATE_FORMAT(NOW(3), '%Y%m%d%H%i%s%f')),
|
||||||
SELECT DISTINCT r.role_code
|
|
||||||
FROM baiyue_iam.iam_user u
|
|
||||||
JOIN baiyue_iam.iam_membership m ON m.user_id = u.id
|
|
||||||
JOIN baiyue_iam.iam_membership_role mr
|
|
||||||
ON mr.membership_id = m.id AND mr.tenant_id = m.tenant_id
|
|
||||||
JOIN baiyue_iam.iam_role r ON r.id = mr.role_id
|
|
||||||
WHERE u.auth_user_id = 9000000001
|
|
||||||
AND m.id = 9000000003 AND m.tenant_id = 0
|
|
||||||
AND m.status = 'ACTIVE' AND m.deleted = 0
|
|
||||||
AND r.app_code = 'PLATFORM'
|
|
||||||
AND (r.tenant_id = 0 OR r.tenant_id = m.tenant_id)
|
|
||||||
AND r.status = 'ACTIVE' AND r.deleted = 0
|
|
||||||
ORDER BY r.role_code
|
|
||||||
) roles),
|
|
||||||
(SELECT JSON_ARRAYAGG(permission_code)
|
|
||||||
FROM (
|
|
||||||
SELECT DISTINCT p.permission_code
|
|
||||||
FROM baiyue_iam.iam_user u
|
|
||||||
JOIN baiyue_iam.iam_membership m ON m.user_id = u.id
|
|
||||||
JOIN baiyue_iam.iam_membership_role mr
|
|
||||||
ON mr.membership_id = m.id AND mr.tenant_id = m.tenant_id
|
|
||||||
JOIN baiyue_iam.iam_role r ON r.id = mr.role_id
|
|
||||||
JOIN baiyue_iam.iam_role_permission rp
|
|
||||||
ON rp.role_id = r.id AND rp.tenant_id = r.tenant_id
|
|
||||||
JOIN baiyue_iam.iam_permission p
|
|
||||||
ON p.id = rp.permission_id AND p.tenant_id = rp.tenant_id
|
|
||||||
WHERE u.auth_user_id = 9000000001
|
|
||||||
AND m.id = 9000000003 AND m.tenant_id = 0
|
|
||||||
AND m.status = 'ACTIVE' AND m.deleted = 0
|
|
||||||
AND r.app_code = 'PLATFORM'
|
|
||||||
AND (r.tenant_id = 0 OR r.tenant_id = m.tenant_id)
|
|
||||||
AND r.status = 'ACTIVE' AND r.deleted = 0
|
|
||||||
AND p.app_code = 'PLATFORM'
|
|
||||||
AND (p.tenant_id = 0 OR p.tenant_id = m.tenant_id)
|
|
||||||
AND p.status = 'ACTIVE' AND p.deleted = 0
|
|
||||||
ORDER BY p.permission_code
|
|
||||||
) permissions),
|
|
||||||
COALESCE((SELECT MAX(permission_version)
|
|
||||||
FROM baiyue_iam.iam_security_subject_version
|
|
||||||
WHERE auth_user_id = 9000000001), 0),
|
|
||||||
SHA2('local-repair:9000000001:authorization', 256),
|
|
||||||
CONCAT('local-repair-9000000001-', DATE_FORMAT(NOW(3), '%Y%m%d%H%i%s%f')),
|
|
||||||
NOW(3), NOW(3)
|
NOW(3), NOW(3)
|
||||||
|
FROM baiyue_iam.iam_user u
|
||||||
|
JOIN baiyue_iam.iam_membership m
|
||||||
|
ON m.user_id = u.id AND m.tenant_id = 0 AND m.deleted = 0
|
||||||
|
LEFT JOIN baiyue_iam.iam_security_subject_version v
|
||||||
|
ON v.auth_user_id = u.auth_user_id
|
||||||
|
WHERE u.auth_user_id IS NOT NULL AND u.deleted = 0
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM baiyue_iam.iam_membership_role mr
|
||||||
|
JOIN baiyue_iam.iam_role r ON r.id = mr.role_id
|
||||||
|
WHERE mr.membership_id = m.id AND mr.tenant_id = m.tenant_id
|
||||||
|
AND r.app_code = 'PLATFORM' AND r.status = 'ACTIVE' AND r.deleted = 0
|
||||||
|
)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
tenant_id = VALUES(tenant_id), security_status = VALUES(security_status),
|
||||||
|
permission_version = VALUES(permission_version), aggregate_version = VALUES(aggregate_version),
|
||||||
|
payload_hash = VALUES(payload_hash), last_event_id = VALUES(last_event_id),
|
||||||
|
changed_at = VALUES(changed_at), updated_at = NOW(3);
|
||||||
|
|
||||||
|
INSERT INTO ll_security_projection (
|
||||||
|
aggregate_type, aggregate_id, tenant_id, security_status,
|
||||||
|
permission_version, roles_json, authorities_json, aggregate_version,
|
||||||
|
payload_hash, last_event_id, changed_at, updated_at
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
'AUTHORIZATION', CAST(u.auth_user_id AS CHAR), 0, NULL,
|
||||||
|
COALESCE(v.permission_version, 0),
|
||||||
|
COALESCE((
|
||||||
|
SELECT JSON_ARRAYAGG(r2.role_code)
|
||||||
|
FROM baiyue_iam.iam_user u2
|
||||||
|
JOIN baiyue_iam.iam_membership m2 ON m2.user_id = u2.id
|
||||||
|
JOIN baiyue_iam.iam_membership_role mr2
|
||||||
|
ON mr2.membership_id = m2.id AND mr2.tenant_id = m2.tenant_id
|
||||||
|
JOIN baiyue_iam.iam_role r2 ON r2.id = mr2.role_id
|
||||||
|
WHERE u2.auth_user_id = u.auth_user_id AND m2.tenant_id = 0
|
||||||
|
AND m2.status = 'ACTIVE' AND m2.deleted = 0
|
||||||
|
AND r2.app_code = 'PLATFORM' AND r2.status = 'ACTIVE' AND r2.deleted = 0
|
||||||
|
), JSON_ARRAY()),
|
||||||
|
COALESCE((
|
||||||
|
SELECT JSON_ARRAYAGG(p2.permission_code)
|
||||||
|
FROM baiyue_iam.iam_user u2
|
||||||
|
JOIN baiyue_iam.iam_membership m2 ON m2.user_id = u2.id
|
||||||
|
JOIN baiyue_iam.iam_membership_role mr2
|
||||||
|
ON mr2.membership_id = m2.id AND mr2.tenant_id = m2.tenant_id
|
||||||
|
JOIN baiyue_iam.iam_role r2 ON r2.id = mr2.role_id
|
||||||
|
JOIN baiyue_iam.iam_role_permission rp2
|
||||||
|
ON rp2.role_id = r2.id AND rp2.tenant_id = r2.tenant_id
|
||||||
|
JOIN baiyue_iam.iam_permission p2
|
||||||
|
ON p2.id = rp2.permission_id AND p2.tenant_id = rp2.tenant_id
|
||||||
|
WHERE u2.auth_user_id = u.auth_user_id AND m2.tenant_id = 0
|
||||||
|
AND m2.status = 'ACTIVE' AND m2.deleted = 0
|
||||||
|
AND r2.app_code = 'PLATFORM' AND r2.status = 'ACTIVE' AND r2.deleted = 0
|
||||||
|
AND p2.app_code = 'PLATFORM' AND p2.status = 'ACTIVE' AND p2.deleted = 0
|
||||||
|
AND (p2.permission_code LIKE 'platform:overseas-warehouse:%'
|
||||||
|
OR p2.permission_code LIKE 'platform:overseas-business:%'
|
||||||
|
OR p2.permission_code LIKE 'platform:overseas-master-data:%'
|
||||||
|
OR p2.permission_code LIKE 'platform:finance:%'
|
||||||
|
OR p2.permission_code LIKE 'lastleg:%')
|
||||||
|
), JSON_ARRAY()),
|
||||||
|
COALESCE(v.permission_version, 0),
|
||||||
|
SHA2(CONCAT('environment-repair:', u.auth_user_id, ':authorization:', COALESCE(v.permission_version, 0)), 256),
|
||||||
|
CONCAT('repair-', u.auth_user_id, '-a-', DATE_FORMAT(NOW(3), '%Y%m%d%H%i%s%f')),
|
||||||
|
NOW(3), NOW(3)
|
||||||
|
FROM baiyue_iam.iam_user u
|
||||||
|
JOIN baiyue_iam.iam_membership m
|
||||||
|
ON m.user_id = u.id AND m.tenant_id = 0 AND m.status = 'ACTIVE' AND m.deleted = 0
|
||||||
|
LEFT JOIN baiyue_iam.iam_security_subject_version v
|
||||||
|
ON v.auth_user_id = u.auth_user_id
|
||||||
|
WHERE u.auth_user_id IS NOT NULL AND u.status = 'ACTIVE' AND u.deleted = 0
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1 FROM baiyue_iam.iam_membership_role mr
|
||||||
|
JOIN baiyue_iam.iam_role r ON r.id = mr.role_id
|
||||||
|
WHERE mr.membership_id = m.id AND mr.tenant_id = m.tenant_id
|
||||||
|
AND r.app_code = 'PLATFORM' AND r.status = 'ACTIVE' AND r.deleted = 0
|
||||||
|
)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
tenant_id = VALUES(tenant_id),
|
tenant_id = VALUES(tenant_id),
|
||||||
permission_version = VALUES(permission_version),
|
permission_version = VALUES(permission_version),
|
||||||
@@ -104,19 +137,10 @@ ON DUPLICATE KEY UPDATE
|
|||||||
changed_at = VALUES(changed_at),
|
changed_at = VALUES(changed_at),
|
||||||
updated_at = NOW(3);
|
updated_at = NOW(3);
|
||||||
|
|
||||||
-- 若未来 IAM 为该账号创建了版本记录,按当前版本同步投影版本号。
|
|
||||||
UPDATE ll_security_projection p
|
|
||||||
JOIN baiyue_iam.iam_security_subject_version v
|
|
||||||
ON v.auth_user_id = CAST(p.aggregate_id AS UNSIGNED)
|
|
||||||
SET p.permission_version = v.permission_version,
|
|
||||||
p.aggregate_version = v.permission_version,
|
|
||||||
p.updated_at = NOW(3)
|
|
||||||
WHERE p.aggregate_type = 'AUTHORIZATION'
|
|
||||||
AND p.aggregate_id = '9000000001';
|
|
||||||
|
|
||||||
SELECT aggregate_type, aggregate_id, tenant_id, permission_version,
|
SELECT aggregate_type, aggregate_id, tenant_id, permission_version,
|
||||||
JSON_LENGTH(roles_json) AS role_count,
|
JSON_LENGTH(roles_json) AS role_count,
|
||||||
JSON_LENGTH(authorities_json) AS authority_count,
|
JSON_LENGTH(authorities_json) AS authority_count,
|
||||||
updated_at
|
updated_at
|
||||||
FROM ll_security_projection
|
FROM ll_security_projection
|
||||||
WHERE aggregate_type = 'AUTHORIZATION' AND aggregate_id = '9000000001';
|
WHERE tenant_id = 0 AND aggregate_type IN ('USER', 'AUTHORIZATION')
|
||||||
|
ORDER BY CAST(aggregate_id AS UNSIGNED), aggregate_type;
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import test from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
|
||||||
|
const sql = readFileSync(new URL('./db/security-projection-repair.sql', import.meta.url), 'utf8')
|
||||||
|
|
||||||
|
test('security projection repair derives every platform identity from IAM', () => {
|
||||||
|
assert.match(sql, /FROM baiyue_iam\.iam_user u/)
|
||||||
|
assert.match(sql, /CAST\(u\.auth_user_id AS CHAR\)/)
|
||||||
|
assert.doesNotMatch(sql, /9000000001|9000000003/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('security projection repair rebuilds user status and overseas authorization', () => {
|
||||||
|
assert.match(sql, /'USER'/)
|
||||||
|
assert.match(sql, /'AUTHORIZATION'/)
|
||||||
|
assert.match(sql, /platform:overseas-warehouse:/)
|
||||||
|
assert.match(sql, /platform:overseas-business:/)
|
||||||
|
assert.match(sql, /platform:overseas-master-data:/)
|
||||||
|
assert.match(sql, /platform:finance:/)
|
||||||
|
assert.match(sql, /lastleg:/)
|
||||||
|
assert.match(sql, /CONCAT\('repair-', u\.auth_user_id, '-[ua]-'/)
|
||||||
|
})
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
server:
|
server:
|
||||||
port: ${WAREHOUSE_PORT:9305}
|
port: ${WAREHOUSE_PORT:9305}
|
||||||
|
max-http-request-header-size: ${SERVER_MAX_HTTP_REQUEST_HEADER_SIZE:32KB}
|
||||||
spring:
|
spring:
|
||||||
sql:
|
sql:
|
||||||
init:
|
init:
|
||||||
|
|||||||
Reference in New Issue
Block a user