This commit is contained in:
2026-08-08 09:17:42 +08:00
parent 4a6f976a91
commit 5951e6aa68
27 changed files with 598 additions and 13 deletions
@@ -66,6 +66,25 @@ class PlatformTenantControllerContractTest {
returnType.getActualTypeArguments()[0]);
}
@Test
void shouldExposeCustomerOwnerPasswordResetWithDedicatedPermission() throws Exception {
Method method = PlatformTenantController.class.getMethod("resetOwnerPassword", long.class,
com.baiyue.iam.dto.TenantDtos.ResetCustomerOwnerPasswordRequest.class, AuthenticatedUser.class);
assertEquals("/tenants/{tenantId:\\d+}/owner/password", method.getAnnotation(org.springframework.web.bind.annotation.PutMapping.class).value()[0]);
assertEquals("hasAuthority('platform:customer:reset-password')", method.getAnnotation(PreAuthorize.class).value());
}
@Test
void shouldExposeArchiveAndImpersonationWithDedicatedPermissions() throws Exception {
Method archive = PlatformTenantController.class.getMethod("archive", long.class, AuthenticatedUser.class);
assertEquals("/tenants/{tenantId:\\d+}/archive", archive.getAnnotation(org.springframework.web.bind.annotation.PostMapping.class).value()[0]);
assertEquals("hasAuthority('platform:customer:delete')", archive.getAnnotation(PreAuthorize.class).value());
Method impersonation = PlatformTenantController.class.getMethod("createImpersonation", long.class,
com.baiyue.iam.dto.TenantDtos.CustomerImpersonationRequest.class, AuthenticatedUser.class);
assertEquals("/tenants/{tenantId:\\d+}/impersonation", impersonation.getAnnotation(org.springframework.web.bind.annotation.PostMapping.class).value()[0]);
assertEquals("hasAuthority('platform:customer:impersonate')", impersonation.getAnnotation(PreAuthorize.class).value());
}
private <T extends Annotation> T annotation(Method method, int parameter, Class<T> type) {
return method.getParameters()[parameter].getAnnotation(type);
}
@@ -8,6 +8,11 @@ import com.baiyue.iam.dto.TenantDtos.UpdateProfileRequest;
import com.baiyue.iam.dto.TenantDtos.RegisterRequest;
import com.baiyue.iam.entity.IamTenantQualification;
import com.baiyue.iam.entity.IamTenantReview;
import com.baiyue.iam.entity.IamMembership;
import com.baiyue.iam.entity.IamUser;
import com.baiyue.iam.dto.TenantDtos.ResetCustomerOwnerPasswordRequest;
import com.baiyue.iam.dto.TenantDtos.CustomerImpersonationRequest;
import com.baiyue.common.security.AuthenticatedUser;
import com.baiyue.iam.repository.TenantRepository;
import com.baiyue.iam.repository.ProfileResourceRepository;
import com.baiyue.iam.repository.MemberRepository;
@@ -137,6 +142,57 @@ class TenantServiceTest {
verify(authClient).availability("internal-token", "13800138000");
}
@Test
void resetOwnerPasswordResolvesOwnerAuthAccountAndForwardsForceChangeFlag() {
when(repository.findTenant(10001L)).thenReturn(merchant(10001L, "ACTIVE"));
IamMembership owner = new IamMembership(); owner.setId(70L); owner.setTenantId(10001L);
owner.setUserId(71L); owner.setMemberType("OWNER"); owner.setStatus("ACTIVE");
IamUser user = new IamUser(); user.setId(71L); user.setAuthUserId(72L); user.setStatus("ACTIVE");
when(members.findOwnerByTenant(10001L)).thenReturn(owner);
when(members.findUser(71L)).thenReturn(user);
when(authClient.reset(eq("internal-token"), eq(72L), any(AuthAccountClient.PasswordRequest.class)))
.thenReturn(com.baiyue.common.core.api.ApiResponse.success(null));
service.resetOwnerPassword(10001L, new ResetCustomerOwnerPasswordRequest("NewPass2", null));
verify(authClient).reset(eq("internal-token"), eq(72L),
argThat(request -> request.forceChangePassword() && "NewPass2".equals(request.newPassword())));
}
@Test
void archiveIsIdempotentForDisabledCustomerWithoutCallingAuth() {
IamTenant tenant = merchant(10001L, "DISABLED");
when(repository.findTenantForUpdate(10001L)).thenReturn(tenant);
assertEquals("DISABLED", service.archiveMerchant(10001L).status());
verify(authClient, never()).archiveTenant(any(), any());
verify(repository, never()).saveTenant(any());
}
@Test
void impersonationTicketUsesActiveOwnerAndCurrentPlatformActor() {
when(repository.findTenant(10001L)).thenReturn(merchant(10001L, "ACTIVE"));
IamMembership owner = new IamMembership(); owner.setId(70L); owner.setTenantId(10001L);
owner.setUserId(71L); owner.setMemberType("OWNER"); owner.setStatus("ACTIVE");
IamUser user = new IamUser(); user.setId(71L); user.setAuthUserId(72L); user.setStatus("ACTIVE");
when(members.findOwnerByTenant(10001L)).thenReturn(owner);
when(members.findUser(71L)).thenReturn(user);
when(authClient.createImpersonationTicket(eq("internal-token"), any(AuthAccountClient.ImpersonationTicketRequest.class)))
.thenReturn(com.baiyue.common.core.api.ApiResponse.success(
new AuthAccountClient.ImpersonationTicketResponse("one-time-code", "ticket-1", 60)));
AuthenticatedUser actor = new AuthenticatedUser(9001L, 0L, 0L, 9002L,
"operator", "PLATFORM", "WEB", java.util.Set.of("platform:customer:impersonate"));
var result = service.createImpersonationTicket(10001L,
new CustomerImpersonationRequest("support investigation"), actor);
assertEquals("one-time-code", result.code());
verify(authClient).createImpersonationTicket(eq("internal-token"), argThat(request ->
request.targetAuthUserId().equals(72L) && request.tenantId().equals(10001L)
&& request.membershipId().equals(70L) && request.actorUserId().equals(9001L)
&& request.actorMembershipId().equals(9002L)));
}
@Test
void platformCannotBypassReviewToActivatePendingTenant() {
when(repository.findTenant(10001L)).thenReturn(merchant(10001L, "PENDING"));