Commit 4dc4035a authored by kezhenxu94's avatar kezhenxu94 Committed by Jason Song

Enhancement: validates http parameters using javax.validation api (#1858)

* enhancement: validates http parameters using javax.validation api

* improve code quality according Codacity

* update as requested

* update according to Codacy
parent 601e3665
......@@ -7,7 +7,6 @@ import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.NotFoundException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.DeleteMapping;
......@@ -19,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
......@@ -34,10 +34,7 @@ public class AppController {
}
@PostMapping("/apps")
public AppDTO create(@RequestBody AppDTO dto) {
if (!InputValidator.isValidClusterNamespace(dto.getAppId())) {
throw new BadRequestException(String.format("AppId格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
public AppDTO create(@Valid @RequestBody AppDTO dto) {
App entity = BeanUtils.transform(App.class, dto);
App managedEntity = appService.findOne(entity.getAppId());
if (managedEntity != null) {
......@@ -46,8 +43,7 @@ public class AppController {
entity = adminService.createNewApp(entity);
dto = BeanUtils.transform(AppDTO.class, entity);
return dto;
return BeanUtils.transform(AppDTO.class, entity);
}
@DeleteMapping("/apps/{appId:.+}")
......
......@@ -6,7 +6,6 @@ import com.ctrip.framework.apollo.common.dto.ClusterDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.NotFoundException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.core.ConfigConsts;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
......@@ -16,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
@RestController
......@@ -30,11 +30,7 @@ public class ClusterController {
@PostMapping("/apps/{appId}/clusters")
public ClusterDTO create(@PathVariable("appId") String appId,
@RequestParam(value = "autoCreatePrivateNamespace", defaultValue = "true") boolean autoCreatePrivateNamespace,
@RequestBody ClusterDTO dto) {
if (!InputValidator.isValidClusterNamespace(dto.getName())) {
throw new BadRequestException(String.format("Cluster格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
@Valid @RequestBody ClusterDTO dto) {
Cluster entity = BeanUtils.transform(Cluster.class, dto);
Cluster managedEntity = clusterService.findOne(appId, entity.getName());
if (managedEntity != null) {
......@@ -47,8 +43,7 @@ public class ClusterController {
entity = clusterService.saveWithoutInstanceOfAppNamespaces(entity);
}
dto = BeanUtils.transform(ClusterDTO.class, entity);
return dto;
return BeanUtils.transform(ClusterDTO.class, entity);
}
@DeleteMapping("/apps/{appId}/clusters/{clusterName:.+}")
......
......@@ -6,7 +6,6 @@ import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.exception.NotFoundException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
......@@ -15,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
......@@ -29,10 +29,8 @@ public class NamespaceController {
@PostMapping("/apps/{appId}/clusters/{clusterName}/namespaces")
public NamespaceDTO create(@PathVariable("appId") String appId,
@PathVariable("clusterName") String clusterName, @RequestBody NamespaceDTO dto) {
if (!InputValidator.isValidClusterNamespace(dto.getNamespaceName())) {
throw new BadRequestException(String.format("Namespace格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
@PathVariable("clusterName") String clusterName,
@Valid @RequestBody NamespaceDTO dto) {
Namespace entity = BeanUtils.transform(Namespace.class, dto);
Namespace managedEntity = namespaceService.findOne(appId, clusterName, entity.getNamespaceName());
if (managedEntity != null) {
......@@ -41,8 +39,7 @@ public class NamespaceController {
entity = namespaceService.save(entity);
dto = BeanUtils.transform(NamespaceDTO.class, entity);
return dto;
return BeanUtils.transform(NamespaceDTO.class, entity);
}
@DeleteMapping("/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName:.+}")
......
......@@ -21,7 +21,7 @@ public abstract class AbstractControllerTest {
@Autowired
private HttpMessageConverters httpMessageConverters;
protected RestTemplate restTemplate = (new TestRestTemplate()).getRestTemplate();
@PostConstruct
......@@ -32,4 +32,8 @@ public abstract class AbstractControllerTest {
@Value("${local.server.port}")
int port;
protected String url(String path) {
return "http://localhost:" + port + path;
}
}
......@@ -4,7 +4,7 @@ import com.ctrip.framework.apollo.biz.repository.AppRepository;
import com.ctrip.framework.apollo.common.dto.AppDTO;
import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -13,6 +13,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.Sql.ExecutionPhase;
import org.springframework.web.client.HttpClientErrorException;
import static org.hamcrest.Matchers.containsString;
public class AppControllerTest extends AbstractControllerTest {
......@@ -112,6 +113,20 @@ public class AppControllerTest extends AbstractControllerTest {
Assert.assertNull(deletedApp);
}
@Test
@Sql(scripts = "/controller/cleanup.sql", executionPhase = ExecutionPhase.AFTER_TEST_METHOD)
public void shouldFailedWhenAppIdIsInvalid() {
AppDTO dto = generateSampleDTOData();
dto.setAppId("invalid app id");
try {
restTemplate.postForEntity(getBaseAppUrl(), dto, String.class);
Assert.fail("Should throw");
} catch (HttpClientErrorException e) {
Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode());
Assert.assertThat(new String(e.getResponseBodyAsByteArray()), containsString(InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
}
private AppDTO generateSampleDTOData() {
AppDTO dto = new AppDTO();
dto.setAppId("someAppId");
......
......@@ -2,17 +2,23 @@ package com.ctrip.framework.apollo.adminservice.controller;
import com.ctrip.framework.apollo.biz.entity.Cluster;
import com.ctrip.framework.apollo.biz.service.ClusterService;
import com.ctrip.framework.apollo.common.dto.ClusterDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.core.ConfigConsts;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
public class ClusterControllerTest {
public class ClusterControllerTest extends AbstractControllerTest {
private ClusterController clusterController;
@Mock
......@@ -40,4 +46,29 @@ public class ClusterControllerTest {
clusterController.delete("1", "2", "d");
verify(clusterService, times(1)).findOne("1", "2");
}
@Test
public void shouldFailWhenRequestBodyInvalid() {
ClusterDTO cluster = new ClusterDTO();
cluster.setAppId("valid");
cluster.setName("notBlank");
ResponseEntity<ClusterDTO> response =
restTemplate.postForEntity(baseUrl() + "/apps/{appId}/clusters", cluster, ClusterDTO.class, cluster.getAppId());
ClusterDTO createdCluster = response.getBody();
Assert.assertNotNull(createdCluster);
Assert.assertEquals(cluster.getAppId(), createdCluster.getAppId());
Assert.assertEquals(cluster.getName(), createdCluster.getName());
cluster.setName("invalid app name");
try {
restTemplate.postForEntity(baseUrl() + "/apps/{appId}/clusters", cluster, ClusterDTO.class, cluster.getAppId());
Assert.fail("Should throw");
} catch (HttpClientErrorException e) {
Assert.assertThat(new String(e.getResponseBodyAsByteArray()), containsString(InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
}
private String baseUrl() {
return "http://localhost:" + port;
}
}
package com.ctrip.framework.apollo.adminservice.controller;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.web.client.HttpClientErrorException;
import static org.hamcrest.Matchers.containsString;
/**
* Created by kezhenxu at 2019/1/8 16:27.
*
* @author kezhenxu (kezhenxu94@163.com)
*/
public class NamespaceControllerTest extends AbstractControllerTest {
@Test
public void create() {
try {
NamespaceDTO namespaceDTO = new NamespaceDTO();
namespaceDTO.setClusterName("cluster");
namespaceDTO.setNamespaceName("invalid name");
namespaceDTO.setAppId("whatever");
restTemplate.postForEntity(
url("/apps/{appId}/clusters/{clusterName}/namespaces"),
namespaceDTO, NamespaceDTO.class, namespaceDTO.getAppId(), namespaceDTO.getClusterName());
Assert.fail("Should throw");
} catch (HttpClientErrorException e) {
Assert.assertThat(new String(e.getResponseBodyAsByteArray()), containsString(InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
}
}
......@@ -190,7 +190,7 @@ public class RemoteConfigRepositoryTest {
when(pollResponse.getBody()).thenReturn(Lists.newArrayList(someNotification));
when(someResponse.getBody()).thenReturn(newApolloConfig);
longPollFinished.get(500, TimeUnit.MILLISECONDS);
longPollFinished.get(5000, TimeUnit.MILLISECONDS);
remoteConfigLongPollService.stopLongPollingRefresh();
......
package com.ctrip.framework.apollo.common.controller;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ctrip.framework.apollo.common.exception.AbstractApolloHttpException;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.tracer.Tracer;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
......@@ -13,22 +20,15 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.client.HttpStatusCodeException;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import static org.slf4j.event.Level.*;
import static org.slf4j.event.Level.ERROR;
import static org.slf4j.event.Level.WARN;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
......@@ -72,6 +72,18 @@ public class GlobalDefaultExceptionHandler {
return handleError(request, ex.getHttpStatus(), ex);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleMethodArgumentNotValidException(
HttpServletRequest request, MethodArgumentNotValidException ex
) {
final Optional<ObjectError> firstError = ex.getBindingResult().getAllErrors().stream().findFirst();
if (firstError.isPresent()) {
final String firstErrorMessage = firstError.get().getDefaultMessage();
return handleError(request, BAD_REQUEST, new BadRequestException(firstErrorMessage));
}
return handleError(request, BAD_REQUEST, ex);
}
private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request,
HttpStatus status, Throwable ex) {
return handleError(request, status, ex, ERROR);
......
package com.ctrip.framework.apollo.common.dto;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import javax.validation.constraints.Pattern;
public class AppDTO extends BaseDTO{
private long id;
private String name;
@Pattern(
regexp = InputValidator.CLUSTER_NAMESPACE_VALIDATOR,
message = "AppId格式错误: " + InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE
)
private String appId;
private String orgId;
......
package com.ctrip.framework.apollo.common.dto;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
public class ClusterDTO extends BaseDTO{
private long id;
@NotBlank(message = "cluster name cannot be blank")
@Pattern(
regexp = InputValidator.CLUSTER_NAMESPACE_VALIDATOR,
message = "Cluster格式错误: " + InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE
)
private String name;
@NotBlank(message = "appId cannot be blank")
private String appId;
private long parentClusterId;
......
package com.ctrip.framework.apollo.common.dto;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import javax.validation.constraints.Pattern;
public class NamespaceDTO extends BaseDTO{
private long id;
private String appId;
private String clusterName;
@Pattern(
regexp = InputValidator.CLUSTER_NAMESPACE_VALIDATOR,
message = "Namespace格式错误: " + InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE
)
private String namespaceName;
public long getId() {
......
package com.ctrip.framework.apollo.common.entity;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
......@@ -13,9 +16,15 @@ import javax.persistence.Table;
@Where(clause = "isDeleted = 0")
public class App extends BaseEntity {
@NotBlank(message = "Name cannot be blank")
@Column(name = "Name", nullable = false)
private String name;
@NotBlank(message = "AppId cannot be blank")
@Pattern(
regexp = InputValidator.CLUSTER_NAMESPACE_VALIDATOR,
message = InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE
)
@Column(name = "AppId", nullable = false)
private String appId;
......@@ -25,9 +34,11 @@ public class App extends BaseEntity {
@Column(name = "OrgName", nullable = false)
private String orgName;
@NotBlank(message = "OwnerName cannot be blank")
@Column(name = "OwnerName", nullable = false)
private String ownerName;
@NotBlank(message = "OwnerEmail cannot be blank")
@Column(name = "OwnerEmail", nullable = false)
private String ownerEmail;
......
package com.ctrip.framework.apollo.common.entity;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
......@@ -16,9 +19,15 @@ import javax.persistence.Table;
@Where(clause = "isDeleted = 0")
public class AppNamespace extends BaseEntity {
@NotBlank(message = "App Name cannot be blank")
@Pattern(
regexp = InputValidator.CLUSTER_NAMESPACE_VALIDATOR,
message = "Namespace格式错误: " + InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE + " & " + InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE
)
@Column(name = "Name", nullable = false)
private String name;
@NotBlank(message = "AppId cannot be blank")
@Column(name = "AppId", nullable = false)
private String appId;
......
......@@ -2,7 +2,6 @@ package com.ctrip.framework.apollo.common.utils;
import com.ctrip.framework.apollo.core.utils.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
......@@ -12,18 +11,12 @@ public class InputValidator {
public static final String INVALID_CLUSTER_NAMESPACE_MESSAGE = "只允许输入数字,字母和符号 - _ .";
public static final String INVALID_NAMESPACE_NAMESPACE_MESSAGE = "不允许以.json, .yml, .yaml, .xml, .properties结尾";
public static final String CLUSTER_NAMESPACE_VALIDATOR = "[0-9a-zA-Z_.-]+";
public static final String APP_NAMESPACE_VALIDATOR = "[a-zA-Z0-9._-]+(?<!\\.(json|yml|yaml|xml|properties))$";
private static final String APP_NAMESPACE_VALIDATOR = "[a-zA-Z0-9._-]+(?<!\\.(json|yml|yaml|xml|properties))$";
private static final Pattern CLUSTER_NAMESPACE_PATTERN =
Pattern.compile(CLUSTER_NAMESPACE_VALIDATOR);
private static final Pattern APP_NAMESPACE_PATTERN =
Pattern.compile(APP_NAMESPACE_VALIDATOR);
public static boolean isValidClusterNamespace(String input) {
Matcher matcher = CLUSTER_NAMESPACE_PATTERN.matcher(input);
return matcher.matches();
}
public static boolean isValidAppNamespace(String name){
if (StringUtils.isEmpty(name)){
return false;
......
package com.ctrip.framework.apollo.common.utils;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Jason Song(song_s@ctrip.com)
*/
public class InputValidatorTest {
@Test
public void testIsValidClusterNamespaceWithCorrectInput() throws Exception {
String someValidInput = "a1-b2_c3.d4";
assertTrue(InputValidator.isValidClusterNamespace(someValidInput));
}
@Test
public void testIsValidClusterNamespaceWithInCorrectInput() throws Exception {
String someInvalidInput = "中文123";
assertFalse(InputValidator.isValidClusterNamespace(someInvalidInput));
String anotherInvalidInput = "123@#{}";
assertFalse(InputValidator.isValidClusterNamespace(anotherInvalidInput));
}
}
package com.ctrip.framework.apollo.openapi.dto;
public class OpenAppNamespaceDTO extends BaseDTO {
private String name;
private String appId;
......
......@@ -56,8 +56,9 @@ public class NamespaceController {
@PreAuthorize(value = "@consumerPermissionValidator.hasCreateNamespacePermission(#request, #appId)")
@PostMapping(value = "/openapi/v1/apps/{appId}/appnamespaces")
public OpenAppNamespaceDTO createNamespace(@PathVariable String appId, @RequestBody OpenAppNamespaceDTO appNamespaceDTO,
HttpServletRequest request) {
public OpenAppNamespaceDTO createNamespace(@PathVariable String appId,
@RequestBody OpenAppNamespaceDTO appNamespaceDTO,
HttpServletRequest request) {
if (!Objects.equals(appId, appNamespaceDTO.getAppId())) {
throw new BadRequestException(String.format("AppId not equal. AppId in path = %s, AppId in payload = %s", appId,
......
......@@ -5,8 +5,6 @@ import com.ctrip.framework.apollo.common.entity.App;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.http.MultiResponseEntity;
import com.ctrip.framework.apollo.common.http.RichResponseEntity;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.component.PortalSettings;
......@@ -40,6 +38,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException;
import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
import java.util.Set;
......@@ -99,7 +98,7 @@ public class AppController {
}
@PostMapping
public App create(@RequestBody AppModel appModel) {
public App create(@Valid @RequestBody AppModel appModel) {
App app = transformToApp(appModel);
......@@ -119,7 +118,7 @@ public class AppController {
@PreAuthorize(value = "@permissionValidator.isAppAdmin(#appId)")
@PutMapping("/{appId:.+}")
public void update(@PathVariable String appId, @RequestBody AppModel appModel) {
public void update(@PathVariable String appId, @Valid @RequestBody AppModel appModel) {
if (!Objects.equals(appId, appModel.getAppId())) {
throw new BadRequestException("The App Id of path variable and request body is different");
}
......@@ -149,15 +148,7 @@ public class AppController {
}
@PostMapping(value = "/envs/{env}", consumes = {"application/json"})
public ResponseEntity<Void> create(@PathVariable String env, @RequestBody App app) {
RequestPrecondition.checkArgumentsNotEmpty(app.getName(), app.getAppId(), app.getOwnerEmail(),
app.getOwnerName(),
app.getOrgId(), app.getOrgName());
if (!InputValidator.isValidClusterNamespace(app.getAppId())) {
throw new BadRequestException(InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE);
}
public ResponseEntity<Void> create(@PathVariable String env, @Valid @RequestBody App app) {
appService.createAppInRemote(Env.valueOf(env), app);
roleInitializationService.initNamespaceSpecificEnvRoles(app.getAppId(), ConfigConsts.NAMESPACE_APPLICATION, env, userInfoHolder.getUser().getUserId());
......@@ -211,13 +202,6 @@ public class AppController {
String orgId = appModel.getOrgId();
String orgName = appModel.getOrgName();
RequestPrecondition.checkArgumentsNotEmpty(appId, appName, ownerName, orgId, orgName);
if (!InputValidator.isValidClusterNamespace(appModel.getAppId())) {
throw new BadRequestException(
String.format("AppId格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
return App.builder()
.appId(appId)
.name(appName)
......
package com.ctrip.framework.apollo.portal.controller;
import com.ctrip.framework.apollo.common.dto.ClusterDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.service.ClusterService;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
......@@ -16,6 +13,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.Objects;
import static com.ctrip.framework.apollo.common.utils.RequestPrecondition.checkModel;
......@@ -34,14 +32,9 @@ public class ClusterController {
@PreAuthorize(value = "@permissionValidator.hasCreateClusterPermission(#appId)")
@PostMapping(value = "apps/{appId}/envs/{env}/clusters")
public ClusterDTO createCluster(@PathVariable String appId, @PathVariable String env,
@RequestBody ClusterDTO cluster) {
@Valid @RequestBody ClusterDTO cluster) {
checkModel(Objects.nonNull(cluster));
RequestPrecondition.checkArgumentsNotEmpty(cluster.getAppId(), cluster.getName());
if (!InputValidator.isValidClusterNamespace(cluster.getName())) {
throw new BadRequestException(String.format("Cluster格式错误: %s", InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE));
}
String operator = userInfoHolder.getUser().getUserId();
cluster.setDataChangeLastModifiedBy(operator);
......
......@@ -7,7 +7,6 @@ import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.http.MultiResponseEntity;
import com.ctrip.framework.apollo.common.http.RichResponseEntity;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.api.AdminServiceAPI;
......@@ -37,6 +36,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Set;
......@@ -190,15 +190,7 @@ public class NamespaceController {
@PostMapping("/apps/{appId}/appnamespaces")
public AppNamespace createAppNamespace(@PathVariable String appId,
@RequestParam(defaultValue = "true") boolean appendNamespacePrefix,
@RequestBody AppNamespace appNamespace) {
RequestPrecondition.checkArgumentsNotEmpty(appNamespace.getAppId(), appNamespace.getName());
if (!InputValidator.isValidAppNamespace(appNamespace.getName())) {
throw new BadRequestException(String.format("Namespace格式错误: %s",
InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE + " & "
+ InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE));
}
@Valid @RequestBody AppNamespace appNamespace) {
AppNamespace createdAppNamespace = appNamespaceService.createAppNamespaceInLocal(appNamespace, appendNamespacePrefix);
if (portalConfig.canAppAdminCreatePrivateNamespace() || createdAppNamespace.isPublic()) {
......
package com.ctrip.framework.apollo.portal.entity.model;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import java.util.Set;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
public class AppModel {
@NotBlank(message = "name cannot be blank")
private String name;
@NotBlank(message = "appId cannot be blank")
@Pattern(
regexp = InputValidator.CLUSTER_NAMESPACE_VALIDATOR,
message = "AppId格式错误: " + InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE
)
private String appId;
@NotBlank(message = "orgId cannot be blank")
private String orgId;
@NotBlank(message = "orgName cannot be blank")
private String orgName;
@NotBlank(message = "ownerName cannot be blank")
private String ownerName;
private Set<String> admins;
......
package com.ctrip.framework.apollo;
import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Created by kezhenxu at 2019/1/8 20:19.
*
* @author kezhenxu (kezhenxu94@163.com)
*/
@Profile("skipAuthorization")
@Configuration
public class SkipAuthorizationConfiguration {
@Primary
@Bean
@Qualifier("consumerPermissionValidator")
public ConsumerPermissionValidator consumerPermissionValidator() {
ConsumerPermissionValidator mock = mock(ConsumerPermissionValidator.class);
when(mock.hasCreateNamespacePermission(any(), any())).thenReturn(true);
return mock;
}
}
package com.ctrip.framework.apollo.openapi.v1.controller;
import javax.annotation.PostConstruct;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
/**
* Created by kezhenxu at 2019/1/8 18:19.
*
* @author kezhenxu (kezhenxu94@163.com)
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class AbstractControllerTest {
@Autowired
private HttpMessageConverters httpMessageConverters;
protected RestTemplate restTemplate = (new TestRestTemplate()).getRestTemplate();
@PostConstruct
protected void postConstruct() {
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
restTemplate.setMessageConverters(httpMessageConverters.getConverters());
}
@Value("${local.server.port}")
protected int port;
protected String url(String path) {
return "http://localhost:" + port + path;
}
}
package com.ctrip.framework.apollo.openapi.v1.controller;
import com.ctrip.framework.apollo.common.utils.InputValidator;
import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator;
import com.ctrip.framework.apollo.openapi.dto.OpenAppNamespaceDTO;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.web.client.HttpClientErrorException;
import static org.hamcrest.Matchers.containsString;
/**
* Created by kezhenxu at 2019/1/8 18:17.
*
* @author kezhenxu (kezhenxu94@163.com)
*/
@ActiveProfiles("skipAuthorization")
public class NamespaceControllerTest extends AbstractControllerTest {
@Autowired
private ConsumerPermissionValidator consumerPermissionValidator;
@Ignore
@Test
public void shouldFailWhenAppNamespaceNameIsInvalid() {
Assert.assertTrue(consumerPermissionValidator.hasCreateNamespacePermission(null, null));
OpenAppNamespaceDTO dto = new OpenAppNamespaceDTO();
dto.setAppId("appId");
dto.setName("invalid name");
try {
restTemplate.postForEntity(
url("/openapi/v1/apps/{appId}/appnamespaces"),
dto, OpenAppNamespaceDTO.class, dto.getAppId()
);
Assert.fail("should throw");
} catch (HttpClientErrorException e) {
Assert.assertThat(
new String(e.getResponseBodyAsByteArray()),
containsString(InputValidator.INVALID_CLUSTER_NAMESPACE_MESSAGE + " & "
+ InputValidator.INVALID_NAMESPACE_NAMESPACE_MESSAGE)
);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment