mirror of https://github.com/halo-dev/halo
Add password reset field verification (#1636)
* perf: add password reset field verification Signed-off-by: Ryan Wang <i@ryanc.cc> * feat: add unit test case Co-authored-by: guqing <1484563614@qq.com>pull/1666/head
parent
acf6a98d25
commit
1ee7b58ef1
|
@ -20,6 +20,7 @@ import run.halo.app.model.entity.User;
|
||||||
import run.halo.app.model.enums.MFAType;
|
import run.halo.app.model.enums.MFAType;
|
||||||
import run.halo.app.model.params.LoginParam;
|
import run.halo.app.model.params.LoginParam;
|
||||||
import run.halo.app.model.params.ResetPasswordParam;
|
import run.halo.app.model.params.ResetPasswordParam;
|
||||||
|
import run.halo.app.model.params.ResetPasswordSendCodeParam;
|
||||||
import run.halo.app.model.properties.PrimaryProperties;
|
import run.halo.app.model.properties.PrimaryProperties;
|
||||||
import run.halo.app.model.support.BaseResponse;
|
import run.halo.app.model.support.BaseResponse;
|
||||||
import run.halo.app.security.token.AuthToken;
|
import run.halo.app.security.token.AuthToken;
|
||||||
|
@ -80,7 +81,7 @@ public class AdminController {
|
||||||
@ApiOperation("Sends reset password verify code")
|
@ApiOperation("Sends reset password verify code")
|
||||||
@CacheLock(autoDelete = false)
|
@CacheLock(autoDelete = false)
|
||||||
@DisableOnCondition
|
@DisableOnCondition
|
||||||
public void sendResetCode(@RequestBody @Valid ResetPasswordParam param) {
|
public void sendResetCode(@RequestBody @Valid ResetPasswordSendCodeParam param) {
|
||||||
adminService.sendResetPasswordCode(param);
|
adminService.sendResetPasswordCode(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package run.halo.app.model.params;
|
package run.halo.app.model.params;
|
||||||
|
|
||||||
import javax.validation.constraints.NotBlank;
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset password params.
|
* Reset password params.
|
||||||
|
@ -10,15 +12,13 @@ import lombok.Data;
|
||||||
* @date 2019-09-05
|
* @date 2019-09-05
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class ResetPasswordParam {
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class ResetPasswordParam extends ResetPasswordSendCodeParam {
|
||||||
@NotBlank(message = "用户名不能为空")
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
@NotBlank(message = "邮箱不能为空")
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
|
@NotBlank(message = "验证码不能为空")
|
||||||
private String code;
|
private String code;
|
||||||
|
|
||||||
|
@NotBlank(message = "密码不能为空")
|
||||||
|
@Size(min = 8, max = 100, message = "密码的字符长度必须在 {min} - {max} 之间")
|
||||||
private String password;
|
private String password;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package run.halo.app.model.params;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters required to send the reset password verification code.
|
||||||
|
*
|
||||||
|
* @author ryanwang
|
||||||
|
* @date 2022-01-24
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ResetPasswordSendCodeParam {
|
||||||
|
|
||||||
|
@NotBlank(message = "用户名不能为空")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank(message = "邮箱不能为空")
|
||||||
|
private String email;
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import run.halo.app.model.dto.LoginPreCheckDTO;
|
||||||
import run.halo.app.model.entity.User;
|
import run.halo.app.model.entity.User;
|
||||||
import run.halo.app.model.params.LoginParam;
|
import run.halo.app.model.params.LoginParam;
|
||||||
import run.halo.app.model.params.ResetPasswordParam;
|
import run.halo.app.model.params.ResetPasswordParam;
|
||||||
|
import run.halo.app.model.params.ResetPasswordSendCodeParam;
|
||||||
import run.halo.app.security.token.AuthToken;
|
import run.halo.app.security.token.AuthToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +55,7 @@ public interface AdminService {
|
||||||
*
|
*
|
||||||
* @param param param must not be null
|
* @param param param must not be null
|
||||||
*/
|
*/
|
||||||
void sendResetPasswordCode(@NonNull ResetPasswordParam param);
|
void sendResetPasswordCode(@NonNull ResetPasswordSendCodeParam param);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset password by code.
|
* Reset password by code.
|
||||||
|
|
|
@ -32,6 +32,7 @@ import run.halo.app.model.enums.LogType;
|
||||||
import run.halo.app.model.enums.MFAType;
|
import run.halo.app.model.enums.MFAType;
|
||||||
import run.halo.app.model.params.LoginParam;
|
import run.halo.app.model.params.LoginParam;
|
||||||
import run.halo.app.model.params.ResetPasswordParam;
|
import run.halo.app.model.params.ResetPasswordParam;
|
||||||
|
import run.halo.app.model.params.ResetPasswordSendCodeParam;
|
||||||
import run.halo.app.model.properties.EmailProperties;
|
import run.halo.app.model.properties.EmailProperties;
|
||||||
import run.halo.app.model.support.HaloConst;
|
import run.halo.app.model.support.HaloConst;
|
||||||
import run.halo.app.security.authentication.Authentication;
|
import run.halo.app.security.authentication.Authentication;
|
||||||
|
@ -184,7 +185,7 @@ public class AdminServiceImpl implements AdminService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendResetPasswordCode(ResetPasswordParam param) {
|
public void sendResetPasswordCode(ResetPasswordSendCodeParam param) {
|
||||||
cacheStore.getAny("code", String.class).ifPresent(code -> {
|
cacheStore.getAny("code", String.class).ifPresent(code -> {
|
||||||
throw new ServiceException("已经获取过验证码,不能重复获取");
|
throw new ServiceException("已经获取过验证码,不能重复获取");
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,203 @@
|
||||||
|
package run.halo.app.controller;
|
||||||
|
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.ResultActions;
|
||||||
|
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||||
|
import run.halo.app.controller.admin.api.AdminController;
|
||||||
|
import run.halo.app.core.ControllerExceptionHandler;
|
||||||
|
import run.halo.app.model.dto.EnvironmentDTO;
|
||||||
|
import run.halo.app.model.dto.LoginPreCheckDTO;
|
||||||
|
import run.halo.app.model.entity.User;
|
||||||
|
import run.halo.app.model.params.LoginParam;
|
||||||
|
import run.halo.app.model.params.ResetPasswordParam;
|
||||||
|
import run.halo.app.model.params.ResetPasswordSendCodeParam;
|
||||||
|
import run.halo.app.security.token.AuthToken;
|
||||||
|
import run.halo.app.service.AdminService;
|
||||||
|
import run.halo.app.utils.JsonUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin controller test.
|
||||||
|
*
|
||||||
|
* @author guqing
|
||||||
|
* @date 2022-02-11
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class AdminControllerTest {
|
||||||
|
|
||||||
|
private static final String FIELD_ERROR_MESSAGE = "字段验证错误,请完善后重试!";
|
||||||
|
|
||||||
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
AdminController adminController =
|
||||||
|
new AdminController(new MockAdminService(), null);
|
||||||
|
mockMvc = MockMvcBuilders.standaloneSetup(adminController)
|
||||||
|
.setControllerAdvice(ControllerExceptionHandler.class)
|
||||||
|
.addFilter((request, response, chain) -> {
|
||||||
|
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendResetPasswordCodeShouldOk() throws Exception {
|
||||||
|
ResetPasswordSendCodeParam param = new ResetPasswordSendCodeParam();
|
||||||
|
param.setEmail("example@example.com");
|
||||||
|
param.setUsername("admin");
|
||||||
|
sendResetPasswordCodePerform(JsonUtils.objectToJson(param))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendResetPasswordCodeShould4xxError() throws Exception {
|
||||||
|
ResetPasswordSendCodeParam param = new ResetPasswordSendCodeParam();
|
||||||
|
param.setEmail("example@example.com");
|
||||||
|
sendResetPasswordCodePerform(JsonUtils.objectToJson(param))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().is4xxClientError())
|
||||||
|
.andExpect(jsonPath("$.message").value(FIELD_ERROR_MESSAGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendResetPasswordCodeShould4xxErrorToo() throws Exception {
|
||||||
|
ResetPasswordSendCodeParam param = new ResetPasswordSendCodeParam();
|
||||||
|
param.setUsername("admin");
|
||||||
|
sendResetPasswordCodePerform(JsonUtils.objectToJson(param))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().is4xxClientError())
|
||||||
|
.andExpect(jsonPath("$.message").value(FIELD_ERROR_MESSAGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resetPasswordShouldOk() throws Exception {
|
||||||
|
ResetPasswordParam param = new ResetPasswordParam();
|
||||||
|
param.setUsername("admin");
|
||||||
|
param.setEmail("example@example.com");
|
||||||
|
param.setPassword("a password");
|
||||||
|
param.setCode("a code");
|
||||||
|
param.setPassword("12345678");
|
||||||
|
resetPasswordPerform(JsonUtils.objectToJson(param))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resetPasswordAndEmailFieldAbsentShouldReturn4xxError() throws Exception {
|
||||||
|
ResetPasswordParam param = new ResetPasswordParam();
|
||||||
|
// The email field value in the parent class is missing,verification will also be triggered
|
||||||
|
param.setUsername("admin");
|
||||||
|
param.setCode("a code");
|
||||||
|
param.setPassword("a password");
|
||||||
|
resetPasswordPerform(JsonUtils.objectToJson(param))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().is4xxClientError())
|
||||||
|
.andExpect(jsonPath("$.message").value(FIELD_ERROR_MESSAGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resetPasswordAndCodeFieldAbsentShouldReturn4xxError() throws Exception {
|
||||||
|
ResetPasswordParam param = new ResetPasswordParam();
|
||||||
|
// The code field value in the param class is missing,verification will also be triggered
|
||||||
|
param.setUsername("admin");
|
||||||
|
param.setEmail("example@example.com");
|
||||||
|
param.setPassword("a password");
|
||||||
|
resetPasswordPerform(JsonUtils.objectToJson(param))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().is4xxClientError())
|
||||||
|
.andExpect(jsonPath("$.message").value(FIELD_ERROR_MESSAGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resetPasswordAndInsufficientPasswordLengthShouldReturn4xxError() throws Exception {
|
||||||
|
ResetPasswordParam param = new ResetPasswordParam();
|
||||||
|
// The code field value in the param class is missing,verification will also be triggered
|
||||||
|
param.setUsername("admin");
|
||||||
|
param.setEmail("example@example.com");
|
||||||
|
param.setCode("a code");
|
||||||
|
param.setPassword("123");
|
||||||
|
resetPasswordPerform(JsonUtils.objectToJson(param))
|
||||||
|
.andDo(print())
|
||||||
|
.andExpect(status().is4xxClientError())
|
||||||
|
.andExpect(jsonPath("$.message").value(FIELD_ERROR_MESSAGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private ResultActions resetPasswordPerform(String param)
|
||||||
|
throws Exception {
|
||||||
|
return this.mockMvc.perform(put("/api/admin/password/reset")
|
||||||
|
.content(param)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private ResultActions sendResetPasswordCodePerform(String param)
|
||||||
|
throws Exception {
|
||||||
|
return this.mockMvc.perform(post("/api/admin/password/code")
|
||||||
|
.content(param)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MockAdminService implements AdminService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User authenticate(LoginParam loginParam) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthToken authCodeCheck(LoginParam loginParam) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearToken() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendResetPasswordCode(ResetPasswordSendCodeParam param) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetPasswordByCode(ResetPasswordParam param) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnvironmentDTO getEnvironments() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthToken refreshToken(String refreshToken) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLogFiles(Long lines) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LoginPreCheckDTO getUserEnv(String username) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue