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.params.LoginParam;
|
||||
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.support.BaseResponse;
|
||||
import run.halo.app.security.token.AuthToken;
|
||||
|
@ -80,7 +81,7 @@ public class AdminController {
|
|||
@ApiOperation("Sends reset password verify code")
|
||||
@CacheLock(autoDelete = false)
|
||||
@DisableOnCondition
|
||||
public void sendResetCode(@RequestBody @Valid ResetPasswordParam param) {
|
||||
public void sendResetCode(@RequestBody @Valid ResetPasswordSendCodeParam param) {
|
||||
adminService.sendResetPasswordCode(param);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package run.halo.app.model.params;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* Reset password params.
|
||||
|
@ -10,15 +12,13 @@ import lombok.Data;
|
|||
* @date 2019-09-05
|
||||
*/
|
||||
@Data
|
||||
public class ResetPasswordParam {
|
||||
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "邮箱不能为空")
|
||||
private String email;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ResetPasswordParam extends ResetPasswordSendCodeParam {
|
||||
|
||||
@NotBlank(message = "验证码不能为空")
|
||||
private String code;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 8, max = 100, message = "密码的字符长度必须在 {min} - {max} 之间")
|
||||
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.params.LoginParam;
|
||||
import run.halo.app.model.params.ResetPasswordParam;
|
||||
import run.halo.app.model.params.ResetPasswordSendCodeParam;
|
||||
import run.halo.app.security.token.AuthToken;
|
||||
|
||||
/**
|
||||
|
@ -54,7 +55,7 @@ public interface AdminService {
|
|||
*
|
||||
* @param param param must not be null
|
||||
*/
|
||||
void sendResetPasswordCode(@NonNull ResetPasswordParam param);
|
||||
void sendResetPasswordCode(@NonNull ResetPasswordSendCodeParam param);
|
||||
|
||||
/**
|
||||
* 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.params.LoginParam;
|
||||
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.support.HaloConst;
|
||||
import run.halo.app.security.authentication.Authentication;
|
||||
|
@ -184,7 +185,7 @@ public class AdminServiceImpl implements AdminService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void sendResetPasswordCode(ResetPasswordParam param) {
|
||||
public void sendResetPasswordCode(ResetPasswordSendCodeParam param) {
|
||||
cacheStore.getAny("code", String.class).ifPresent(code -> {
|
||||
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