Merge remote-tracking branch 'origin/test' into dev
This commit is contained in:
@@ -63,6 +63,42 @@ public interface AuthorizationQueryMapper {
|
||||
@Param("membershipId") long membershipId,
|
||||
@Param("appCode") String appCode);
|
||||
|
||||
@Select("""
|
||||
SELECT DISTINCT r.role_code
|
||||
FROM iam_user u
|
||||
JOIN iam_membership m ON m.user_id = u.id
|
||||
JOIN iam_membership_role mr ON mr.membership_id = m.id AND mr.tenant_id = m.tenant_id
|
||||
JOIN iam_role r ON r.id = mr.role_id
|
||||
WHERE u.auth_user_id = #{userId} AND u.status = 'ACTIVE' AND u.deleted = 0
|
||||
AND m.tenant_id = #{tenantId} AND m.status = 'ACTIVE' AND m.deleted = 0
|
||||
AND (r.tenant_id = 0 OR r.tenant_id = #{tenantId})
|
||||
AND r.app_code = #{appCode} AND r.status = 'ACTIVE' AND r.deleted = 0
|
||||
ORDER BY r.role_code
|
||||
""")
|
||||
List<String> findRolesByIdentity(@Param("userId") long userId,
|
||||
@Param("tenantId") long tenantId,
|
||||
@Param("appCode") String appCode);
|
||||
|
||||
@Select("""
|
||||
SELECT DISTINCT p.permission_code
|
||||
FROM iam_user u
|
||||
JOIN iam_membership m ON m.user_id = u.id
|
||||
JOIN iam_membership_role mr ON mr.membership_id = m.id AND mr.tenant_id = m.tenant_id
|
||||
JOIN iam_role r ON r.id = mr.role_id
|
||||
JOIN iam_role_permission rp ON rp.role_id = r.id
|
||||
JOIN iam_permission p ON p.id = rp.permission_id
|
||||
WHERE u.auth_user_id = #{userId} AND u.status = 'ACTIVE' AND u.deleted = 0
|
||||
AND m.tenant_id = #{tenantId} AND m.status = 'ACTIVE' AND m.deleted = 0
|
||||
AND (r.tenant_id = 0 OR r.tenant_id = #{tenantId})
|
||||
AND r.app_code = #{appCode} AND r.status = 'ACTIVE' AND r.deleted = 0
|
||||
AND (p.tenant_id = 0 OR p.tenant_id = #{tenantId})
|
||||
AND p.app_code = #{appCode} AND p.status = 'ACTIVE' AND p.deleted = 0
|
||||
ORDER BY p.permission_code
|
||||
""")
|
||||
List<String> findPermissionsByIdentity(@Param("userId") long userId,
|
||||
@Param("tenantId") long tenantId,
|
||||
@Param("appCode") String appCode);
|
||||
|
||||
@Select("SELECT COALESCE(MAX(permission_version),0) FROM iam_security_subject_version WHERE auth_user_id=#{userId}")
|
||||
long findPermissionVersion(@Param("userId") long userId);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.baiyue.common.event.EventPayloadHash;
|
||||
import com.baiyue.common.event.EventTypes;
|
||||
import com.baiyue.common.event.IamSecurityEventsV1;
|
||||
import com.baiyue.common.event.RegionCode;
|
||||
import com.baiyue.iam.mapper.AuthorizationQueryMapper;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -14,12 +15,20 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** 在 IAM 状态事务内递增安全版本并写入可靠事件发送箱。 */
|
||||
@Service
|
||||
public class SecurityEventOutboxService {
|
||||
private static final List<String> OVERSEAS_AUTHORITY_PREFIXES = List.of(
|
||||
"platform:overseas-warehouse:", "platform:overseas-business:",
|
||||
"platform:overseas-master-data:", "merchant:warehouse:",
|
||||
"merchant:cross-border:", "platform:finance:", "lastleg:");
|
||||
@Resource private JdbcTemplate jdbc;
|
||||
@Resource private ObjectMapper objectMapper;
|
||||
@Resource private AuthorizationQueryMapper authorizationQueries;
|
||||
|
||||
public void userStatus(long authUserId, long tenantId, String appCode, String status) {
|
||||
long version = bump(authUserId);
|
||||
@@ -39,8 +48,13 @@ public class SecurityEventOutboxService {
|
||||
|
||||
public void permissionChanged(long authUserId, long tenantId, String appCode, String reason) {
|
||||
long version = bump(authUserId);
|
||||
Set<String> roles = Set.copyOf(authorizationQueries.findRolesByIdentity(authUserId, tenantId, appCode));
|
||||
Set<String> authorities = authorizationQueries.findPermissionsByIdentity(authUserId, tenantId, appCode)
|
||||
.stream().filter(permission -> permission != null && OVERSEAS_AUTHORITY_PREFIXES.stream()
|
||||
.anyMatch(permission::startsWith)).collect(Collectors.toUnmodifiableSet());
|
||||
write(EventTypes.IAM_PERMISSION_VERSION_CHANGED, "AUTHORIZATION", String.valueOf(authUserId), tenantId, version,
|
||||
new IamSecurityEventsV1.PermissionVersionChanged(authUserId, tenantId, appCode, version, reason, Instant.now()));
|
||||
new IamSecurityEventsV1.PermissionVersionChanged(authUserId, tenantId, appCode, version, reason,
|
||||
Instant.now(), roles, authorities));
|
||||
}
|
||||
|
||||
private long bump(long authUserId) {
|
||||
|
||||
@@ -80,7 +80,9 @@ class SecurityEventOutboxRelayTest {
|
||||
|
||||
private EventEnvelope envelope(RegionCode target) {
|
||||
var value = new IamSecurityEventsV1.PermissionVersionChanged(
|
||||
101L, 0L, "PLATFORM", 3L, "contract", Instant.parse("2026-08-25T07:00:00Z"));
|
||||
101L, 0L, "PLATFORM", 3L, "contract", Instant.parse("2026-08-25T07:00:00Z"),
|
||||
java.util.Set.of("PLATFORM_ADMIN"),
|
||||
java.util.Set.of("platform:overseas-warehouse:last-mile-order:list"));
|
||||
var payload = objectMapper.valueToTree(value);
|
||||
return new EventEnvelope("00000000-0000-4000-8000-000000000081",
|
||||
EventTypes.IAM_PERMISSION_VERSION_CHANGED, IamSecurityEventsV1.VERSION,
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.baiyue.iam.service;
|
||||
|
||||
import com.baiyue.common.event.EventEnvelope;
|
||||
import com.baiyue.common.event.IamSecurityEventsV1;
|
||||
import com.baiyue.iam.mapper.AuthorizationQueryMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -14,6 +15,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
@@ -24,6 +27,7 @@ import static org.mockito.Mockito.when;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class SecurityEventOutboxServiceTest {
|
||||
@Mock JdbcTemplate jdbc;
|
||||
@Mock AuthorizationQueryMapper authorizationQueries;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
|
||||
private SecurityEventOutboxService service;
|
||||
|
||||
@@ -32,6 +36,7 @@ class SecurityEventOutboxServiceTest {
|
||||
service = new SecurityEventOutboxService();
|
||||
ReflectionTestUtils.setField(service, "jdbc", jdbc);
|
||||
ReflectionTestUtils.setField(service, "objectMapper", objectMapper);
|
||||
ReflectionTestUtils.setField(service, "authorizationQueries", authorizationQueries);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,4 +76,27 @@ class SecurityEventOutboxServiceTest {
|
||||
assertThat(payload.status()).isEqualTo(expectedSecurityStatus);
|
||||
assertThat(envelope.aggregateVersion()).isEqualTo(3L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void permissionEventCarriesOnlyTheOverseasAuthorizationSnapshot() throws Exception {
|
||||
when(jdbc.queryForObject(anyString(), eq(Long.class), any())).thenReturn(7L);
|
||||
when(authorizationQueries.findRolesByIdentity(101L, 0L, "ADMIN"))
|
||||
.thenReturn(List.of("PLATFORM_ADMIN"));
|
||||
when(authorizationQueries.findPermissionsByIdentity(101L, 0L, "ADMIN"))
|
||||
.thenReturn(List.of("platform:overseas-warehouse:orders:view", "platform:iam:users:view",
|
||||
"lastleg:orders:view"));
|
||||
|
||||
service.permissionChanged(101L, 0L, "ADMIN", "ROLE_CHANGED");
|
||||
|
||||
ArgumentCaptor<Object[]> parameters = ArgumentCaptor.forClass(Object[].class);
|
||||
verify(jdbc).update(eq("INSERT INTO iam_security_event_outbox(event_id,event_type,aggregate_type,aggregate_id,payload_json) "
|
||||
+ "VALUES (?,?,?,?,CAST(? AS JSON))"), parameters.capture());
|
||||
EventEnvelope envelope = objectMapper.readValue(String.valueOf(parameters.getValue()[4]), EventEnvelope.class);
|
||||
IamSecurityEventsV1.PermissionVersionChanged payload = objectMapper.treeToValue(
|
||||
envelope.payload(), IamSecurityEventsV1.PermissionVersionChanged.class);
|
||||
assertThat(payload.roles()).containsExactly("PLATFORM_ADMIN");
|
||||
assertThat(payload.authorities()).containsExactlyInAnyOrder(
|
||||
"platform:overseas-warehouse:orders:view", "lastleg:orders:view");
|
||||
assertThat(payload.authorities()).doesNotContain("platform:iam:users:view");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user