diff --git a/api/src/org/apache/cloudstack/api/ApiArgValidator.java b/api/src/org/apache/cloudstack/api/ApiArgValidator.java new file mode 100644 index 00000000000..bd2294c6018 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/ApiArgValidator.java @@ -0,0 +1,23 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api; + +public enum ApiArgValidator { + NotNullOrEmpty, // does Strings.isNullOrEmpty check + PositiveNumber, // does != null and > 0 check +} diff --git a/api/src/org/apache/cloudstack/api/Parameter.java b/api/src/org/apache/cloudstack/api/Parameter.java index 7ee6897af29..fa6075dc970 100644 --- a/api/src/org/apache/cloudstack/api/Parameter.java +++ b/api/src/org/apache/cloudstack/api/Parameter.java @@ -49,4 +49,6 @@ public @interface Parameter { String since() default ""; RoleType[] authorized() default {}; + + ApiArgValidator[] validations() default {}; } diff --git a/server/src/com/cloud/api/dispatch/ParamProcessWorker.java b/server/src/com/cloud/api/dispatch/ParamProcessWorker.java index 099c0c91e66..feefaabc551 100644 --- a/server/src/com/cloud/api/dispatch/ParamProcessWorker.java +++ b/server/src/com/cloud/api/dispatch/ParamProcessWorker.java @@ -34,6 +34,7 @@ import java.text.SimpleDateFormat; import javax.inject.Inject; +import com.google.common.base.Strings; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.ControlledEntity; @@ -49,6 +50,7 @@ import org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.ApiArgValidator; import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; import org.apache.cloudstack.api.command.admin.usage.GetUsageRecordsCmd; @@ -92,6 +94,55 @@ public class ParamProcessWorker implements DispatchWorker { processParameters(task.getCmd(), task.getParams()); } + private void validateNonEmptyString(final Object param, final String argName) { + if (param == null || Strings.isNullOrEmpty(param.toString())) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("Empty or null value provided for API arg: %s", argName)); + } + } + + private void validateNaturalNumber(final Object param, final String argName) { + Long value = null; + if (param != null && param instanceof Long) { + value = (Long) param; + } else if (param != null) { + value = Long.valueOf(param.toString()); + } + if (value == null || value < 1L) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, String.format("Invalid value provided for API arg: %s", argName)); + } + } + + private void validateField(final Object paramObj, final Parameter annotation) throws ServerApiException { + if (annotation == null) { + return; + } + final String argName = annotation.name(); + for (final ApiArgValidator validator : annotation.validations()) { + if (validator == null) { + continue; + } + switch (validator) { + case NotNullOrEmpty: + switch (annotation.type()) { + case UUID: + case STRING: + validateNonEmptyString(paramObj, argName); + break; + } + break; + case PositiveNumber: + switch (annotation.type()) { + case SHORT: + case INTEGER: + case LONG: + validateNaturalNumber(paramObj, argName); + break; + } + break; + } + } + } + @SuppressWarnings({"unchecked", "rawtypes"}) public void processParameters(final BaseCmd cmd, final Map params) { final Map entitiesToAccess = new HashMap(); @@ -112,6 +163,7 @@ public class ParamProcessWorker implements DispatchWorker { // marshall the parameter into the correct type and set the field value try { + validateField(paramObj, parameterAnnotation); setFieldValue(field, cmd, paramObj, parameterAnnotation); } catch (final IllegalArgumentException argEx) { if (s_logger.isDebugEnabled()) { @@ -420,6 +472,7 @@ public class ParamProcessWorker implements DispatchWorker { for (final Class entity : entities) { CallContext.current().putContextParameter(entity, internalId); } + validateNaturalNumber(internalId, annotation.name()); return internalId; } } @@ -452,6 +505,7 @@ public class ParamProcessWorker implements DispatchWorker { throw new InvalidParameterValueException("Invalid parameter " + annotation.name() + " value=" + uuid + " due to incorrect long value format, or entity does not exist or due to incorrect parameter annotation for the field in api cmd class."); } + validateNaturalNumber(internalId, annotation.name()); return internalId; } }