Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: add password reset field verification #1636

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand Down Expand Up @@ -80,7 +81,7 @@ public void logout() {
@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);
}

Expand Down
14 changes: 7 additions & 7 deletions src/main/java/run/halo/app/model/params/ResetPasswordParam.java
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -10,15 +12,13 @@
* @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;
}
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 2 additions & 1 deletion src/main/java/run/halo/app/service/AdminService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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;

/**
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand Down Expand Up @@ -184,7 +185,7 @@ public void clearToken() {
}

@Override
public void sendResetPasswordCode(ResetPasswordParam param) {
public void sendResetPasswordCode(ResetPasswordSendCodeParam param) {
cacheStore.getAny("code", String.class).ifPresent(code -> {
throw new ServiceException("已经获取过验证码,不能重复获取");
});
Expand Down
203 changes: 203 additions & 0 deletions src/test/java/run/halo/app/controller/AdminControllerTest.java
Original file line number Diff line number Diff line change
@@ -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)
JohnNiang marked this conversation as resolved.
Show resolved Hide resolved
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 {
JohnNiang marked this conversation as resolved.
Show resolved Hide resolved

@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;
}
}
}