From 21d016b3dd855e47c87c62c3e4a4e2b2ec2fb257 Mon Sep 17 00:00:00 2001 From: Alena Prokharchyk Date: Thu, 20 Feb 2014 17:14:11 -0800 Subject: [PATCH] API response: added new field to @Param - RoleType[] authorized() default {}. The field defines who is authorized to see this partciluar reponse field. If not specified, the parameter is returned to everybody --- api/src/com/cloud/serializer/Param.java | 4 ++ .../api/response/IPAddressResponse.java | 3 +- .../api/response/NetworkResponse.java | 6 +-- .../api/response/UserVmResponse.java | 3 +- .../api/response/VolumeResponse.java | 3 +- .../cloudstack/api/response/VpcResponse.java | 3 +- server/src/com/cloud/api/ApiGsonHelper.java | 4 +- .../com/cloud/api/ApiResponseGsonHelper.java | 39 ++++++++++++++++++- .../cloud/api/ResponseObjectTypeAdapter.java | 7 ++-- .../api/response/ApiResponseSerializer.java | 24 ++++++++++++ 10 files changed, 81 insertions(+), 15 deletions(-) diff --git a/api/src/com/cloud/serializer/Param.java b/api/src/com/cloud/serializer/Param.java index 02deb6f1c1f..3e6f852128a 100644 --- a/api/src/com/cloud/serializer/Param.java +++ b/api/src/com/cloud/serializer/Param.java @@ -19,6 +19,8 @@ package com.cloud.serializer; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import org.apache.cloudstack.acl.RoleType; + @Retention(RetentionPolicy.RUNTIME) public @interface Param { String name() default ""; @@ -33,4 +35,6 @@ public @interface Param { boolean includeInApiDoc() default true; String since() default ""; + + RoleType[] authorized() default {}; } diff --git a/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java b/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java index 1f51d5a4dbb..3c03247eddb 100644 --- a/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java +++ b/api/src/org/apache/cloudstack/api/response/IPAddressResponse.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.api.response; import java.util.Date; import java.util.List; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; @@ -146,7 +147,7 @@ public class IPAddressResponse extends BaseResponse implements ControlledEntityR private Boolean isPortable; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is public ip for display to the regular user", since = "4.4") + @Param(description = "is public ip for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; /* diff --git a/api/src/org/apache/cloudstack/api/response/NetworkResponse.java b/api/src/org/apache/cloudstack/api/response/NetworkResponse.java index 58ec956e9bc..bf92aa4f785 100644 --- a/api/src/org/apache/cloudstack/api/response/NetworkResponse.java +++ b/api/src/org/apache/cloudstack/api/response/NetworkResponse.java @@ -18,8 +18,7 @@ package org.apache.cloudstack.api.response; import java.util.List; -import com.google.gson.annotations.SerializedName; - +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; @@ -27,6 +26,7 @@ import org.apache.cloudstack.api.EntityReference; import com.cloud.network.Network; import com.cloud.projects.ProjectAccount; import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") @EntityReference(value = {Network.class, ProjectAccount.class}) @@ -209,7 +209,7 @@ public class NetworkResponse extends BaseResponse implements ControlledEntityRes private String ip6Cidr; @SerializedName(ApiConstants.DISPLAY_NETWORK) - @Param(description = "an optional field, whether to the display the network to the end user or not.") + @Param(description = "an optional field, whether to the display the network to the end user or not.", authorized = {RoleType.Admin}) private Boolean displayNetwork; @SerializedName(ApiConstants.ACL_ID) diff --git a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java index 26ecb5bb088..84d532bbc17 100644 --- a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java @@ -21,6 +21,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; @@ -248,7 +249,7 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp private Set affinityGroupList; @SerializedName(ApiConstants.DISPLAY_VM) - @Param(description = "an optional field whether to the display the vm to the end user or not.") + @Param(description = "an optional field whether to the display the vm to the end user or not.", authorized = {RoleType.Admin}) private Boolean displayVm; @SerializedName(ApiConstants.IS_DYNAMICALLY_SCALABLE) diff --git a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java index 319a7340255..27e95def021 100644 --- a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java +++ b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java @@ -20,6 +20,7 @@ import java.util.Date; import java.util.LinkedHashSet; import java.util.Set; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; @@ -188,7 +189,7 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity private Set tags; @SerializedName(ApiConstants.DISPLAY_VOLUME) - @Param(description = "an optional field whether to the display the volume to the end user or not.") + @Param(description = "an optional field whether to the display the volume to the end user or not.", authorized = {RoleType.Admin}) private Boolean displayVolume; @SerializedName(ApiConstants.PATH) diff --git a/api/src/org/apache/cloudstack/api/response/VpcResponse.java b/api/src/org/apache/cloudstack/api/response/VpcResponse.java index 928d1b24f85..eeafb40c142 100644 --- a/api/src/org/apache/cloudstack/api/response/VpcResponse.java +++ b/api/src/org/apache/cloudstack/api/response/VpcResponse.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.api.response; import java.util.Date; import java.util.List; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; @@ -107,7 +108,7 @@ public class VpcResponse extends BaseResponse implements ControlledEntityRespons private List tags; @SerializedName(ApiConstants.FOR_DISPLAY) - @Param(description = "is vpc for display to the regular user", since = "4.4") + @Param(description = "is vpc for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; public void setId(String id) { diff --git a/server/src/com/cloud/api/ApiGsonHelper.java b/server/src/com/cloud/api/ApiGsonHelper.java index 58f29ceeee7..faa70c21fff 100644 --- a/server/src/com/cloud/api/ApiGsonHelper.java +++ b/server/src/com/cloud/api/ApiGsonHelper.java @@ -18,10 +18,10 @@ package com.cloud.api; import java.util.Map; -import com.google.gson.GsonBuilder; - import org.apache.cloudstack.api.ResponseObject; +import com.google.gson.GsonBuilder; + public class ApiGsonHelper { private static final GsonBuilder s_gBuilder; static { diff --git a/server/src/com/cloud/api/ApiResponseGsonHelper.java b/server/src/com/cloud/api/ApiResponseGsonHelper.java index 3352cc8a91d..c2cc9d92dbb 100644 --- a/server/src/com/cloud/api/ApiResponseGsonHelper.java +++ b/server/src/com/cloud/api/ApiResponseGsonHelper.java @@ -16,9 +16,15 @@ // under the License. package com.cloud.api; -import com.google.gson.GsonBuilder; - +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.serializer.Param; +import com.cloud.user.Account; +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.GsonBuilder; /** * The ApiResonseGsonHelper is different from ApiGsonHelper - it registeres one more adapter for String type required for api response encoding @@ -31,9 +37,38 @@ public class ApiResponseGsonHelper { s_gBuilder.setVersion(1.3); s_gBuilder.registerTypeAdapter(ResponseObject.class, new ResponseObjectTypeAdapter()); s_gBuilder.registerTypeAdapter(String.class, new EncodedStringTypeAdapter()); + s_gBuilder.setExclusionStrategies(new ExclStrat()); } public static GsonBuilder getBuilder() { return s_gBuilder; } + + private static class ExclStrat implements ExclusionStrategy { + + public boolean shouldSkipClass(Class arg0) { + return false; + } + public boolean shouldSkipField(FieldAttributes f) { + + Param param = f.getAnnotation(Param.class); + if (param != null) { + RoleType[] allowedRoles = param.authorized(); + if (allowedRoles.length > 0) { + boolean permittedParameter = false; + Account caller = CallContext.current().getCallingAccount(); + for (RoleType allowedRole : allowedRoles) { + if (allowedRole.getValue() == caller.getType()) { + permittedParameter = true; + break; + } + } + if (!permittedParameter) { + return true; + } + } + } + return false; + } + } } diff --git a/server/src/com/cloud/api/ResponseObjectTypeAdapter.java b/server/src/com/cloud/api/ResponseObjectTypeAdapter.java index a4bc6caa1cd..44baedc933b 100644 --- a/server/src/com/cloud/api/ResponseObjectTypeAdapter.java +++ b/server/src/com/cloud/api/ResponseObjectTypeAdapter.java @@ -19,6 +19,9 @@ package com.cloud.api; import java.lang.reflect.Method; import java.lang.reflect.Type; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.response.ExceptionResponse; +import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.log4j.Logger; import com.google.gson.JsonElement; @@ -26,10 +29,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; -import org.apache.cloudstack.api.ResponseObject; -import org.apache.cloudstack.api.response.ExceptionResponse; -import org.apache.cloudstack.api.response.SuccessResponse; - public class ResponseObjectTypeAdapter implements JsonSerializer { public static final Logger s_logger = Logger.getLogger(ResponseObjectTypeAdapter.class.getName()); diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java index f276e89d5e1..e9633a7ab6e 100644 --- a/server/src/com/cloud/api/response/ApiResponseSerializer.java +++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ResponseObject; @@ -34,11 +35,14 @@ import org.apache.cloudstack.api.response.CreateCmdResponse; import org.apache.cloudstack.api.response.ExceptionResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; import org.apache.log4j.Logger; import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseGsonHelper; import com.cloud.api.ApiServer; +import com.cloud.serializer.Param; +import com.cloud.user.Account; import com.cloud.utils.encoding.URLEncoder; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.ExceptionProxyObject; @@ -189,11 +193,31 @@ public class ApiResponseSerializer { continue; // skip transient fields } + SerializedName serializedName = field.getAnnotation(SerializedName.class); if (serializedName == null) { continue; // skip fields w/o serialized name } + Param param = field.getAnnotation(Param.class); + if (param != null) { + RoleType[] allowedRoles = param.authorized(); + if (allowedRoles.length > 0) { + boolean permittedParameter = false; + Account caller = CallContext.current().getCallingAccount(); + for (RoleType allowedRole : allowedRoles) { + if (allowedRole.getValue() == caller.getType()) { + permittedParameter = true; + break; + } + } + if (!permittedParameter) { + s_logger.trace("Ignoring paremeter " + param.name() + " as the caller is not authorized to see it"); + continue; + } + } + } + field.setAccessible(true); Object fieldValue = null; try {