Skip to content

Commit

Permalink
Enhancement: validation (apolloconfig#1869)
Browse files Browse the repository at this point in the history
* Enhancement: validates http parameters using javax.validation api

* remove unnecessary validation
  • Loading branch information
kezhenxu94 authored and nobodyiam committed Jan 19, 2019
1 parent 96dca9d commit f755353
Show file tree
Hide file tree
Showing 15 changed files with 176 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Optional;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
Expand Down Expand Up @@ -84,6 +85,13 @@ public ResponseEntity<Map<String, Object>> handleMethodArgumentNotValidException
return handleError(request, BAD_REQUEST, ex);
}

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Map<String, Object>> handleConstraintViolationException(
HttpServletRequest request, ConstraintViolationException ex
) {
return handleError(request, BAD_REQUEST, new BadRequestException(ex.getMessage()));
}

private ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request,
HttpStatus status, Throwable ex) {
return handleError(request, status, ex, ERROR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ public class RequestPrecondition {

private static String ILLEGAL_MODEL = "request model is invalid";

private static String ILLEGAL_NUMBER = "number should be positive";


public static void checkArgumentsNotEmpty(String... args) {
checkArguments(!StringUtils.isContainEmpty(args), CONTAIN_EMPTY_ARGUMENT);
}
Expand All @@ -27,31 +24,4 @@ public static void checkArguments(boolean expression, Object errorMessage) {
throw new BadRequestException(String.valueOf(errorMessage));
}
}

public static void checkNumberPositive(int... args){
for (int num: args){
if (num <= 0){
throw new BadRequestException(ILLEGAL_NUMBER);
}
}
}

public static void checkNumberPositive(long... args){
for (long num: args){
if (num <= 0){
throw new BadRequestException(ILLEGAL_NUMBER);
}
}
}

public static void checkNumberNotNegative(int... args){
for (int num: args){
if (num < 0){
throw new BadRequestException(ILLEGAL_NUMBER);
}
}
}



}
6 changes: 6 additions & 0 deletions apollo-portal/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.15.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ public OpenReleaseDTO createRelease(@PathVariable String appId, @PathVariable St
@PathVariable String namespaceName,
@RequestBody NamespaceReleaseDTO model,
HttpServletRequest request) {

checkModel(model != null);
RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(model.getReleasedBy(), model
.getReleaseTitle()),
"Params(releaseTitle and releasedBy) can not be empty");
Expand Down Expand Up @@ -91,7 +89,6 @@ public OpenReleaseDTO merge(@PathVariable String appId, @PathVariable String env
@PathVariable String clusterName, @PathVariable String namespaceName,
@PathVariable String branchName, @RequestParam(value = "deleteBranch", defaultValue = "true") boolean deleteBranch,
@RequestBody NamespaceReleaseDTO model, HttpServletRequest request) {
checkModel(model != null);
RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(model.getReleasedBy(), model
.getReleaseTitle()),
"Params(releaseTitle and releasedBy) can not be empty");
Expand All @@ -114,7 +111,6 @@ public OpenReleaseDTO createGrayRelease(@PathVariable String appId,
@PathVariable String namespaceName, @PathVariable String branchName,
@RequestBody NamespaceReleaseDTO model,
HttpServletRequest request) {
checkModel(model != null);
RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(model.getReleasedBy(), model
.getReleaseTitle()),
"Params(releaseTitle and releasedBy) can not be empty");
Expand All @@ -140,8 +136,6 @@ public OpenReleaseDTO createGrayDelRelease(@PathVariable String appId,
@PathVariable String namespaceName, @PathVariable String branchName,
@RequestBody NamespaceGrayDelReleaseDTO model,
HttpServletRequest request) {

checkModel(model != null);
RequestPrecondition.checkArguments(!StringUtils.isContainEmpty(model.getReleasedBy(), model
.getReleaseTitle()),
"Params(releaseTitle and releasedBy) can not be empty");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ public ClusterController(final ClusterService clusterService, final UserInfoHold
@PostMapping(value = "apps/{appId}/envs/{env}/clusters")
public ClusterDTO createCluster(@PathVariable String appId, @PathVariable String env,
@Valid @RequestBody ClusterDTO cluster) {

checkModel(Objects.nonNull(cluster));

String operator = userInfoHolder.getUser().getUserId();
cluster.setDataChangeLastModifiedBy(operator);
cluster.setDataChangeCreatedBy(operator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import com.ctrip.framework.apollo.core.enums.Env;
import com.ctrip.framework.apollo.portal.component.PermissionValidator;
import com.ctrip.framework.apollo.portal.service.CommitService;
import javax.validation.Valid;
import javax.validation.constraints.Positive;
import javax.validation.constraints.PositiveOrZero;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -13,7 +17,7 @@
import java.util.Collections;
import java.util.List;


@Validated
@RestController
public class CommitController {

Expand All @@ -28,16 +32,12 @@ public CommitController(final CommitService commitService, final PermissionValid
@GetMapping("/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/commits")
public List<CommitDTO> find(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
@RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) {
@Valid @PositiveOrZero(message = "page should be positive or 0") @RequestParam(defaultValue = "0") int page,
@Valid @Positive(message = "size should be positive number") @RequestParam(defaultValue = "10") int size) {
if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
return Collections.emptyList();
}

RequestPrecondition.checkNumberPositive(size);
RequestPrecondition.checkNumberNotNegative(page);

return commitService.find(appId, Env.valueOf(env), clusterName, namespaceName, page, size);

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ public ItemController(final ItemService configService, final UserInfoHolder user
public void modifyItemsByText(@PathVariable String appId, @PathVariable String env,
@PathVariable String clusterName, @PathVariable String namespaceName,
@RequestBody NamespaceTextModel model) {

checkModel(model != null);

model.setAppId(appId);
model.setClusterName(clusterName);
model.setEnv(env);
Expand Down Expand Up @@ -141,7 +138,7 @@ public List<ItemDTO> findBranchItems(@PathVariable("appId") String appId, @PathV

@PostMapping(value = "/namespaces/{namespaceName}/diff", consumes = {"application/json"})
public List<ItemDiffs> diff(@RequestBody NamespaceSyncModel model) {
checkModel(Objects.nonNull(model) && !model.isInvalid());
checkModel(!model.isInvalid());

List<ItemDiffs> itemDiffs = configService.compare(model.getSyncToNamespaces(), model.getSyncItems());

Expand All @@ -164,7 +161,7 @@ public List<ItemDiffs> diff(@RequestBody NamespaceSyncModel model) {
@PutMapping(value = "/apps/{appId}/namespaces/{namespaceName}/items", consumes = {"application/json"})
public ResponseEntity<Void> update(@PathVariable String appId, @PathVariable String namespaceName,
@RequestBody NamespaceSyncModel model) {
checkModel(Objects.nonNull(model) && !model.isInvalid());
checkModel(!model.isInvalid());
boolean hasPermission = permissionValidator.hasModifyNamespacePermission(appId, namespaceName);
Env envNoPermission = null;
// if uses has ModifyNamespace permission then he has permission
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@
import com.ctrip.framework.apollo.portal.entity.vo.ReleaseCompareResult;
import com.ctrip.framework.apollo.portal.listener.ConfigPublishEvent;
import com.ctrip.framework.apollo.portal.service.ReleaseService;
import javax.validation.Valid;
import javax.validation.constraints.Positive;
import javax.validation.constraints.PositiveOrZero;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -29,6 +33,7 @@

import static com.ctrip.framework.apollo.common.utils.RequestPrecondition.checkModel;

@Validated
@RestController
public class ReleaseController {

Expand All @@ -53,8 +58,6 @@ public ReleaseController(
public ReleaseDTO createRelease(@PathVariable String appId,
@PathVariable String env, @PathVariable String clusterName,
@PathVariable String namespaceName, @RequestBody NamespaceReleaseModel model) {

checkModel(Objects.nonNull(model));
model.setAppId(appId);
model.setEnv(env);
model.setClusterName(clusterName);
Expand Down Expand Up @@ -85,8 +88,6 @@ public ReleaseDTO createGrayRelease(@PathVariable String appId,
@PathVariable String env, @PathVariable String clusterName,
@PathVariable String namespaceName, @PathVariable String branchName,
@RequestBody NamespaceReleaseModel model) {

checkModel(Objects.nonNull(model));
model.setAppId(appId);
model.setEnv(env);
model.setClusterName(branchName);
Expand Down Expand Up @@ -117,15 +118,12 @@ public List<ReleaseBO> findAllReleases(@PathVariable String appId,
@PathVariable String env,
@PathVariable String clusterName,
@PathVariable String namespaceName,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size) {
@Valid @PositiveOrZero(message = "page should be positive or 0") @RequestParam(defaultValue = "0") int page,
@Valid @Positive(message = "size should be positive number") @RequestParam(defaultValue = "5") int size) {
if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
return Collections.emptyList();
}

RequestPrecondition.checkNumberPositive(size);
RequestPrecondition.checkNumberNotNegative(page);

return releaseService.findAllReleases(appId, Env.valueOf(env), clusterName, namespaceName, page, size);
}

Expand All @@ -134,16 +132,13 @@ public List<ReleaseDTO> findActiveReleases(@PathVariable String appId,
@PathVariable String env,
@PathVariable String clusterName,
@PathVariable String namespaceName,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size) {
@Valid @PositiveOrZero(message = "page should be positive or 0") @RequestParam(defaultValue = "0") int page,
@Valid @Positive(message = "size should be positive number") @RequestParam(defaultValue = "5") int size) {

if (permissionValidator.shouldHideConfigToCurrentUser(appId, env, namespaceName)) {
return Collections.emptyList();
}

RequestPrecondition.checkNumberPositive(size);
RequestPrecondition.checkNumberNotNegative(page);

return releaseService.findActiveReleases(appId, Env.valueOf(env), clusterName, namespaceName, page, size);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,18 @@


import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.RequestPrecondition;
import com.ctrip.framework.apollo.portal.entity.po.ServerConfig;
import com.ctrip.framework.apollo.portal.repository.ServerConfigRepository;
import com.ctrip.framework.apollo.portal.spi.UserInfoHolder;
import java.util.Objects;
import javax.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;

import static com.ctrip.framework.apollo.common.utils.RequestPrecondition.checkModel;

/**
* 配置中心本身需要一些配置,这些配置放在数据库里面
*/
Expand All @@ -33,11 +30,7 @@ public ServerConfigController(final ServerConfigRepository serverConfigRepositor

@PreAuthorize(value = "@permissionValidator.isSuperAdmin()")
@PostMapping("/server/config")
public ServerConfig createOrUpdate(@RequestBody ServerConfig serverConfig) {

checkModel(Objects.nonNull(serverConfig));
RequestPrecondition.checkArgumentsNotEmpty(serverConfig.getKey(), serverConfig.getValue());

public ServerConfig createOrUpdate(@Valid @RequestBody ServerConfig serverConfig) {
String modifiedBy = userInfoHolder.getUser().getUserId();

ServerConfig storedConfig = serverConfigRepository.findByKey(serverConfig.getKey());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.ctrip.framework.apollo.common.entity.BaseEntity;

import javax.validation.constraints.NotBlank;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;

Expand All @@ -17,9 +18,11 @@
@SQLDelete(sql = "Update ServerConfig set isDeleted = 1 where id = ?")
@Where(clause = "isDeleted = 0")
public class ServerConfig extends BaseEntity {
@NotBlank(message = "ServerConfig.Key cannot be blank")
@Column(name = "Key", nullable = false)
private String key;

@NotBlank(message = "ServerConfig.Value cannot be blank")
@Column(name = "Value", nullable = false)
private String value;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.ctrip.framework.apollo;

import com.ctrip.framework.apollo.openapi.auth.ConsumerPermissionValidator;
import org.springframework.beans.factory.annotation.Qualifier;
import com.ctrip.framework.apollo.openapi.util.ConsumerAuthUtil;
import com.ctrip.framework.apollo.portal.component.PermissionValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
Expand All @@ -13,17 +14,34 @@
/**
* Created by kezhenxu at 2019/1/8 20:19.
*
* Configuration class that will disable authorization.
*
* @author kezhenxu ([email protected])
*/
@Profile("skipAuthorization")
@Configuration
public class SkipAuthorizationConfiguration {
@Primary
@Bean
@Qualifier("consumerPermissionValidator")
public ConsumerPermissionValidator consumerPermissionValidator() {
ConsumerPermissionValidator mock = mock(ConsumerPermissionValidator.class);
final ConsumerPermissionValidator mock = mock(ConsumerPermissionValidator.class);
when(mock.hasCreateNamespacePermission(any(), any())).thenReturn(true);
return mock;
}

@Primary
@Bean
public ConsumerAuthUtil consumerAuthUtil() {
final ConsumerAuthUtil mock = mock(ConsumerAuthUtil.class);
when(mock.getConsumerId(any())).thenReturn(1L);
return mock;
}

@Primary
@Bean("permissionValidator")
public PermissionValidator permissionValidator() {
final PermissionValidator mock = mock(PermissionValidator.class);
when(mock.isSuperAdmin()).thenReturn(true);
return mock;
}
}
Loading

0 comments on commit f755353

Please sign in to comment.