|
|
@@ -0,0 +1,78 @@
|
|
|
+package com.dms.service;
|
|
|
+
|
|
|
+import static org.assertj.core.api.Assertions.assertThat;
|
|
|
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.never;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
+import static org.mockito.Mockito.verifyNoInteractions;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+
|
|
|
+import com.dms.common.DmsException;
|
|
|
+import com.dms.config.DmsProperties;
|
|
|
+import com.dms.mapper.UserMapper;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.springframework.mock.env.MockEnvironment;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 禁用用户名密码登录开关(安全管控,DMS_PASSWORD_LOGIN_DISABLED 缺省 false):
|
|
|
+ * 开启后 login 最前置 403 拒绝(不消耗验证码、不查询用户),关闭时登录照常走原路径。
|
|
|
+ */
|
|
|
+class PasswordLoginSwitchTest {
|
|
|
+
|
|
|
+ private static DmsProperties props(boolean disabled) {
|
|
|
+ MockEnvironment environment = new MockEnvironment();
|
|
|
+ if (disabled) {
|
|
|
+ environment.setProperty("DMS_PASSWORD_LOGIN_DISABLED", "true");
|
|
|
+ }
|
|
|
+ return new DmsProperties(environment);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static AuthService service(UserMapper userMapper,
|
|
|
+ CaptchaService captchaService, DmsProperties props) {
|
|
|
+ return new AuthService(userMapper, mock(AuditService.class),
|
|
|
+ mock(LoginIpService.class), mock(UserSummaryService.class),
|
|
|
+ mock(LoginLockoutService.class), captchaService, null, props);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void loginRejectedBeforeCaptchaAndDbLookupWhenDisabled() {
|
|
|
+ UserMapper userMapper = mock(UserMapper.class);
|
|
|
+ CaptchaService captchaService = mock(CaptchaService.class);
|
|
|
+ AuthService service = service(userMapper, captchaService, props(true));
|
|
|
+ // 403 FORBIDDEN:对全部用户名(含内置 admin)一律拒绝,消息引导单点登录
|
|
|
+ assertThatThrownBy(() -> service.login("someone", "Passw0rd!", false, null, null, null))
|
|
|
+ .isInstanceOf(DmsException.class)
|
|
|
+ .hasFieldOrPropertyWithValue("code", "FORBIDDEN")
|
|
|
+ .hasMessage("用户名密码登录已被禁用,请使用单点登录");
|
|
|
+ // 守卫先于验证码消费与用户查询:两者均不触达,不泄露任何账号信息
|
|
|
+ verify(captchaService, never()).verify(any(), any());
|
|
|
+ verifyNoInteractions(userMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void loginProceedsOnOriginalPathWhenEnabled() {
|
|
|
+ UserMapper userMapper = mock(UserMapper.class);
|
|
|
+ when(userMapper.findActiveByUsername("someone")).thenReturn(null);
|
|
|
+ CaptchaService captchaService = mock(CaptchaService.class);
|
|
|
+ AuthService service = service(userMapper, captchaService, props(false));
|
|
|
+ // 未配置开关(缺省 false):照常查库,用户不存在报密码错误(原语义不变)
|
|
|
+ assertThatThrownBy(() -> service.login("someone", "bad", false, null, null, null))
|
|
|
+ .isInstanceOf(DmsException.class)
|
|
|
+ .hasFieldOrPropertyWithValue("code", "INVALID_CREDENTIALS");
|
|
|
+ verify(userMapper).findActiveByUsername("someone");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void propsParseTruthinessAndDefault() {
|
|
|
+ assertThat(props(false).passwordLoginDisabled()).isFalse();
|
|
|
+ MockEnvironment on = new MockEnvironment();
|
|
|
+ on.setProperty("DMS_PASSWORD_LOGIN_DISABLED", "1");
|
|
|
+ assertThat(new DmsProperties(on).passwordLoginDisabled()).isTrue();
|
|
|
+ MockEnvironment bogus = new MockEnvironment();
|
|
|
+ bogus.setProperty("DMS_PASSWORD_LOGIN_DISABLED", "yes-on-please");
|
|
|
+ // 仅 1/true/yes/on 视为开启,其余(含乱写)一律关闭
|
|
|
+ assertThat(new DmsProperties(bogus).passwordLoginDisabled()).isFalse();
|
|
|
+ }
|
|
|
+}
|